alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (Plotter.java)

This example Java source code file (Plotter.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

accessibleplotter, arraylist, awt, bean, color, day, event, filechooser, gui, hour, minute, object, override, plotter, savedatafilechooser, sequence, string, stroke, swing, timestamps

The Plotter.java Java example source code

/*
 * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.tools.jconsole;

import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import java.lang.reflect.Array;
import java.util.*;

import javax.accessibility.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.filechooser.*;
import javax.swing.filechooser.FileFilter;


import com.sun.tools.jconsole.JConsoleContext;

import static sun.tools.jconsole.Formatter.*;
import static sun.tools.jconsole.ProxyClient.*;

@SuppressWarnings("serial")
public class Plotter extends JComponent
                     implements Accessible, ActionListener, PropertyChangeListener {

    public static enum Unit {
        NONE, BYTES, PERCENT
    }

    static final String[] rangeNames = {
        Messages.ONE_MIN,
        Messages.FIVE_MIN,
        Messages.TEN_MIN,
        Messages.THIRTY_MIN,
        Messages.ONE_HOUR,
        Messages.TWO_HOURS,
        Messages.THREE_HOURS,
        Messages.SIX_HOURS,
        Messages.TWELVE_HOURS,
        Messages.ONE_DAY,
        Messages.SEVEN_DAYS,
        Messages.ONE_MONTH,
        Messages.THREE_MONTHS,
        Messages.SIX_MONTHS,
        Messages.ONE_YEAR,
        Messages.ALL
    };

    static final int[] rangeValues = {
        1,
        5,
        10,
        30,
        1 * 60,
        2 * 60,
        3 * 60,
        6 * 60,
        12 * 60,
        1 * 24 * 60,
        7 * 24 * 60,
        1 * 31 * 24 * 60,
        3 * 31 * 24 * 60,
        6 * 31 * 24 * 60,
        366 * 24 * 60,
        -1
    };


    final static long SECOND = 1000;
    final static long MINUTE = 60 * SECOND;
    final static long HOUR   = 60 * MINUTE;
    final static long DAY    = 24 * HOUR;

    final static Color bgColor = new Color(250, 250, 250);
    final static Color defaultColor = Color.blue.darker();

    final static int ARRAY_SIZE_INCREMENT = 4000;

    private static Stroke dashedStroke;

    private TimeStamps times = new TimeStamps();
    private ArrayList<Sequence> seqs = new ArrayList();
    private JPopupMenu popupMenu;
    private JMenu timeRangeMenu;
    private JRadioButtonMenuItem[] menuRBs;
    private JMenuItem saveAsMI;
    private JFileChooser saveFC;

    private int viewRange = -1; // Minutes (value <= 0 means full range)
    private Unit unit;
    private int decimals;
    private double decimalsMultiplier;
    private Border border = null;
    private Rectangle r = new Rectangle(1, 1, 1, 1);
    private Font smallFont = null;

    // Initial margins, may be recalculated as needed
    private int topMargin = 10;
    private int bottomMargin = 45;
    private int leftMargin = 65;
    private int rightMargin = 70;
    private final boolean displayLegend;

    public Plotter() {
        this(Unit.NONE, 0);
    }

    public Plotter(Unit unit) {
        this(unit, 0);
    }

    public Plotter(Unit unit, int decimals) {
        this(unit,decimals,true);
    }

    // Note: If decimals > 0 then values must be decimally shifted left
    // that many places, i.e. multiplied by Math.pow(10.0, decimals).
    public Plotter(Unit unit, int decimals, boolean displayLegend) {
        this.displayLegend = displayLegend;
        setUnit(unit);
        setDecimals(decimals);

        enableEvents(AWTEvent.MOUSE_EVENT_MASK);

        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (getParent() instanceof PlotterPanel) {
                    getParent().requestFocusInWindow();
                }
            }
        });

    }

    public void setUnit(Unit unit) {
        this.unit = unit;
    }

    public void setDecimals(int decimals) {
        this.decimals = decimals;
        this.decimalsMultiplier = Math.pow(10.0, decimals);
    }

    public void createSequence(String key, String name, Color color, boolean isPlotted) {
        Sequence seq = getSequence(key);
        if (seq == null) {
            seq = new Sequence(key);
        }
        seq.name = name;
        seq.color = (color != null) ? color : defaultColor;
        seq.isPlotted = isPlotted;

        seqs.add(seq);
    }

    public void setUseDashedTransitions(String key, boolean b) {
        Sequence seq = getSequence(key);
        if (seq != null) {
            seq.transitionStroke = b ? getDashedStroke() : null;
        }
    }

    public void setIsPlotted(String key, boolean isPlotted) {
        Sequence seq = getSequence(key);
        if (seq != null) {
            seq.isPlotted = isPlotted;
        }
    }

    // Note: If decimals > 0 then values must be decimally shifted left
    // that many places, i.e. multiplied by Math.pow(10.0, decimals).
    public synchronized void addValues(long time, long... values) {
        assert (values.length == seqs.size());
        times.add(time);
        for (int i = 0; i < values.length; i++) {
            seqs.get(i).add(values[i]);
        }
        repaint();
    }

    private Sequence getSequence(String key) {
        for (Sequence seq : seqs) {
            if (seq.key.equals(key)) {
                return seq;
            }
        }
        return null;
    }

    /**
     * @return the displayed time range in minutes, or -1 for all data
     */
    public int getViewRange() {
        return viewRange;
    }

    /**
     * @param minutes the displayed time range in minutes, or -1 to diaplay all data
     */
    public void setViewRange(int minutes) {
        if (minutes != viewRange) {
            int oldValue = viewRange;
            viewRange = minutes;
            /* Do not i18n this string */
            firePropertyChange("viewRange", oldValue, viewRange);
            if (popupMenu != null) {
                for (int i = 0; i < menuRBs.length; i++) {
                    if (rangeValues[i] == viewRange) {
                        menuRBs[i].setSelected(true);
                        break;
                    }
                }
            }
            repaint();
        }
    }

    @Override
    public JPopupMenu getComponentPopupMenu() {
        if (popupMenu == null) {
            popupMenu = new JPopupMenu(Messages.CHART_COLON);
            timeRangeMenu = new JMenu(Messages.PLOTTER_TIME_RANGE_MENU);
            timeRangeMenu.setMnemonic(Resources.getMnemonicInt(Messages.PLOTTER_TIME_RANGE_MENU));
            popupMenu.add(timeRangeMenu);
            menuRBs = new JRadioButtonMenuItem[rangeNames.length];
            ButtonGroup rbGroup = new ButtonGroup();
            for (int i = 0; i < rangeNames.length; i++) {
                menuRBs[i] = new JRadioButtonMenuItem(rangeNames[i]);
                rbGroup.add(menuRBs[i]);
                menuRBs[i].addActionListener(this);
                if (viewRange == rangeValues[i]) {
                    menuRBs[i].setSelected(true);
                }
                timeRangeMenu.add(menuRBs[i]);
            }

            popupMenu.addSeparator();

            saveAsMI = new JMenuItem(Messages.PLOTTER_SAVE_AS_MENU_ITEM);
            saveAsMI.setMnemonic(Resources.getMnemonicInt(Messages.PLOTTER_SAVE_AS_MENU_ITEM));
            saveAsMI.addActionListener(this);
            popupMenu.add(saveAsMI);
        }
        return popupMenu;
    }

    public void actionPerformed(ActionEvent ev) {
        JComponent src = (JComponent)ev.getSource();
        if (src == saveAsMI) {
            saveAs();
        } else {
            int index = timeRangeMenu.getPopupMenu().getComponentIndex(src);
            setViewRange(rangeValues[index]);
        }
    }

    private void saveAs() {
        if (saveFC == null) {
            saveFC = new SaveDataFileChooser();
        }
        int ret = saveFC.showSaveDialog(this);
        if (ret == JFileChooser.APPROVE_OPTION) {
            saveDataToFile(saveFC.getSelectedFile());
        }
    }

    private void saveDataToFile(File file) {
        try {
            PrintStream out = new PrintStream(new FileOutputStream(file));

            // Print header line
            out.print("Time");
            for (Sequence seq : seqs) {
                out.print(","+seq.name);
            }
            out.println();

            // Print data lines
            if (seqs.size() > 0 && seqs.get(0).size > 0) {
                for (int i = 0; i < seqs.get(0).size; i++) {
                    double excelTime = toExcelTime(times.time(i));
                    out.print(String.format(Locale.ENGLISH, "%.6f", excelTime));
                    for (Sequence seq : seqs) {
                        out.print("," + getFormattedValue(seq.value(i), false));
                    }
                    out.println();
                }
            }

            out.close();
            JOptionPane.showMessageDialog(this,
                                          Resources.format(Messages.FILE_CHOOSER_SAVED_FILE,
                                                           file.getAbsolutePath(),
                                                           file.length()));
        } catch (IOException ex) {
            String msg = ex.getLocalizedMessage();
            String path = file.getAbsolutePath();
            if (msg.startsWith(path)) {
                msg = msg.substring(path.length()).trim();
            }
            JOptionPane.showMessageDialog(this,
                                          Resources.format(Messages.FILE_CHOOSER_SAVE_FAILED_MESSAGE,
                                                           path,
                                                           msg),
                                          Messages.FILE_CHOOSER_SAVE_FAILED_TITLE,
                                          JOptionPane.ERROR_MESSAGE);
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        int width = getWidth()-rightMargin-leftMargin-10;
        int height = getHeight()-topMargin-bottomMargin;
        if (width <= 0 || height <= 0) {
            // not enough room to paint anything
            return;
        }

        Color oldColor = g.getColor();
        Font  oldFont  = g.getFont();
        Color fg = getForeground();
        Color bg = getBackground();
        boolean bgIsLight = (bg.getRed() > 200 &&
                             bg.getGreen() > 200 &&
                             bg.getBlue() > 200);


        ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                         RenderingHints.VALUE_ANTIALIAS_ON);

        if (smallFont == null) {
            smallFont = oldFont.deriveFont(9.0F);
        }

        r.x = leftMargin - 5;
        r.y = topMargin  - 8;
        r.width  = getWidth()-leftMargin-rightMargin;
        r.height = getHeight()-topMargin-bottomMargin+16;

        if (border == null) {
            // By setting colors here, we avoid recalculating them
            // over and over.
            border = new BevelBorder(BevelBorder.LOWERED,
                                     getBackground().brighter().brighter(),
                                     getBackground().brighter(),
                                     getBackground().darker().darker(),
                                     getBackground().darker());
        }

        border.paintBorder(this, g, r.x, r.y, r.width, r.height);

        // Fill background color
        g.setColor(bgColor);
        g.fillRect(r.x+2, r.y+2, r.width-4, r.height-4);
        g.setColor(oldColor);

        long tMin = Long.MAX_VALUE;
        long tMax = Long.MIN_VALUE;
        long vMin = Long.MAX_VALUE;
        long vMax = 1;

        int w = getWidth()-rightMargin-leftMargin-10;
        int h = getHeight()-topMargin-bottomMargin;

        if (times.size > 1) {
            tMin = Math.min(tMin, times.time(0));
            tMax = Math.max(tMax, times.time(times.size-1));
        }
        long viewRangeMS;
        if (viewRange > 0) {
            viewRangeMS = viewRange * MINUTE;
        } else {
            // Display full time range, but no less than a minute
            viewRangeMS = Math.max(tMax - tMin, 1 * MINUTE);
        }

        // Calculate min/max values
        for (Sequence seq : seqs) {
            if (seq.size > 0) {
                for (int i = 0; i < seq.size; i++) {
                    if (seq.size == 1 || times.time(i) >= tMax - viewRangeMS) {
                        long val = seq.value(i);
                        if (val > Long.MIN_VALUE) {
                            vMax = Math.max(vMax, val);
                            vMin = Math.min(vMin, val);
                        }
                    }
                }
            } else {
                vMin = 0L;
            }
            if (unit == Unit.BYTES || !seq.isPlotted) {
                // We'll scale only to the first (main) value set.
                // TODO: Use a separate property for this.
                break;
            }
        }

        // Normalize scale
        vMax = normalizeMax(vMax);
        if (vMin > 0) {
            if (vMax / vMin > 4) {
                vMin = 0;
            } else {
                vMin = normalizeMin(vMin);
            }
        }


        g.setColor(fg);

        // Axes
        // Draw vertical axis
        int x = leftMargin - 18;
        int y = topMargin;
        FontMetrics fm = g.getFontMetrics();

        g.drawLine(x,   y,   x,   y+h);

        int n = 5;
        if ((""+vMax).startsWith("2")) {
            n = 4;
        } else if ((""+vMax).startsWith("3")) {
            n = 6;
        } else if ((""+vMax).startsWith("4")) {
            n = 4;
        } else if ((""+vMax).startsWith("6")) {
            n = 6;
        } else if ((""+vMax).startsWith("7")) {
            n = 7;
        } else if ((""+vMax).startsWith("8")) {
            n = 8;
        } else if ((""+vMax).startsWith("9")) {
            n = 3;
        }

        // Ticks
        ArrayList<Long> tickValues = new ArrayList();
        tickValues.add(vMin);
        for (int i = 0; i < n; i++) {
            long v = i * vMax / n;
            if (v > vMin) {
                tickValues.add(v);
            }
        }
        tickValues.add(vMax);
        n = tickValues.size();

        String[] tickStrings = new String[n];
        for (int i = 0; i < n; i++) {
            long v = tickValues.get(i);
            tickStrings[i] = getSizeString(v, vMax);
        }

        // Trim trailing decimal zeroes.
        if (decimals > 0) {
            boolean trimLast = true;
            boolean removedDecimalPoint = false;
            do {
                for (String str : tickStrings) {
                    if (!(str.endsWith("0") || str.endsWith("."))) {
                        trimLast = false;
                        break;
                    }
                }
                if (trimLast) {
                    if (tickStrings[0].endsWith(".")) {
                        removedDecimalPoint = true;
                    }
                    for (int i = 0; i < n; i++) {
                        String str = tickStrings[i];
                        tickStrings[i] = str.substring(0, str.length()-1);
                    }
                }
            } while (trimLast && !removedDecimalPoint);
        }

        // Draw ticks
        int lastY = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            long v = tickValues.get(i);
            y = topMargin+h-(int)(h * (v-vMin) / (vMax-vMin));
            g.drawLine(x-2, y, x+2, y);
            String s = tickStrings[i];
            if (unit == Unit.PERCENT) {
                s += "%";
            }
            int sx = x-6-fm.stringWidth(s);
            if (y < lastY-13) {
                if (checkLeftMargin(sx)) {
                    // Wait for next repaint
                    return;
                }
                g.drawString(s, sx, y+4);
            }
            // Draw horizontal grid line
            g.setColor(Color.lightGray);
            g.drawLine(r.x + 4, y, r.x + r.width - 4, y);
            g.setColor(fg);
            lastY = y;
        }

        // Draw horizontal axis
        x = leftMargin;
        y = topMargin + h + 15;
        g.drawLine(x,   y,   x+w, y);

        long t1 = tMax;
        if (t1 <= 0L) {
            // No data yet, so draw current time
            t1 = System.currentTimeMillis();
        }
        long tz = timeDF.getTimeZone().getOffset(t1);
        long tickInterval = calculateTickInterval(w, 40, viewRangeMS);
        if (tickInterval > 3 * HOUR) {
            tickInterval = calculateTickInterval(w, 80, viewRangeMS);
        }
        long t0 = tickInterval - (t1 - viewRangeMS + tz) % tickInterval;
        while (t0 < viewRangeMS) {
            x = leftMargin + (int)(w * t0 / viewRangeMS);
            g.drawLine(x, y-2, x, y+2);

            long t = t1 - viewRangeMS + t0;
            String str = formatClockTime(t);
            g.drawString(str, x, y+16);
            //if (tickInterval > (1 * HOUR) && t % (1 * DAY) == 0) {
            if ((t + tz) % (1 * DAY) == 0) {
                str = formatDate(t);
                g.drawString(str, x, y+27);
            }
            // Draw vertical grid line
            g.setColor(Color.lightGray);
            g.drawLine(x, topMargin, x, topMargin + h);
            g.setColor(fg);
            t0 += tickInterval;
        }

        // Plot values
        int start = 0;
        int nValues = 0;
        int nLists = seqs.size();
        if (nLists > 0) {
            nValues = seqs.get(0).size;
        }
        if (nValues == 0) {
            g.setColor(oldColor);
            return;
        } else {
            Sequence seq = seqs.get(0);
            // Find starting point
            for (int p = 0; p < seq.size; p++) {
                if (times.time(p) >= tMax - viewRangeMS) {
                    start = p;
                    break;
                }
            }
        }

        //Optimization: collapse plot of more than four values per pixel
        int pointsPerPixel = (nValues - start) / w;
        if (pointsPerPixel < 4) {
            pointsPerPixel = 1;
        }

        // Draw graphs
        // Loop backwards over sequences because the first needs to be painted on top
        for (int i = nLists-1; i >= 0; i--) {
            int x0 = leftMargin;
            int y0 = topMargin + h + 1;

            Sequence seq = seqs.get(i);
            if (seq.isPlotted && seq.size > 0) {
                // Paint twice, with white and with color
                for (int pass = 0; pass < 2; pass++) {
                    g.setColor((pass == 0) ? Color.white : seq.color);
                    int x1 = -1;
                    long v1 = -1;
                    for (int p = start; p < nValues; p += pointsPerPixel) {
                        // Make sure we get the last value
                        if (pointsPerPixel > 1 && p >= nValues - pointsPerPixel) {
                            p = nValues - 1;
                        }
                        int x2 = (int)(w * (times.time(p)-(t1-viewRangeMS)) / viewRangeMS);
                        long v2 = seq.value(p);
                        if (v2 >= vMin && v2 <= vMax) {
                            int y2  = (int)(h * (v2 -vMin) / (vMax-vMin));
                            if (x1 >= 0 && v1 >= vMin && v1 <= vMax) {
                                int y1 = (int)(h * (v1-vMin) / (vMax-vMin));

                                if (y1 == y2) {
                                    // fillrect is much faster
                                    g.fillRect(x0+x1, y0-y1-pass, x2-x1, 1);
                                } else {
                                    Graphics2D g2d = (Graphics2D)g;
                                    Stroke oldStroke = null;
                                    if (seq.transitionStroke != null) {
                                        oldStroke = g2d.getStroke();
                                        g2d.setStroke(seq.transitionStroke);
                                    }
                                    g.drawLine(x0+x1, y0-y1-pass, x0+x2, y0-y2-pass);
                                    if (oldStroke != null) {
                                        g2d.setStroke(oldStroke);
                                    }
                                }
                            }
                        }
                        x1 = x2;
                        v1 = v2;
                    }
                }

                // Current value
                long v = seq.value(seq.size - 1);
                if (v >= vMin && v <= vMax) {
                    if (bgIsLight) {
                        g.setColor(seq.color);
                    } else {
                        g.setColor(fg);
                    }
                    x = r.x + r.width + 2;
                    y = topMargin+h-(int)(h * (v-vMin) / (vMax-vMin));
                    // a small triangle/arrow
                    g.fillPolygon(new int[] { x+2, x+6, x+6 },
                                  new int[] { y,   y+3, y-3 },
                                  3);
                }
                g.setColor(fg);
            }
        }

        int[] valueStringSlots = new int[nLists];
        for (int i = 0; i < nLists; i++) valueStringSlots[i] = -1;
        for (int i = 0; i < nLists; i++) {
            Sequence seq = seqs.get(i);
            if (seq.isPlotted && seq.size > 0) {
                // Draw current value

                // TODO: collapse values if pointsPerPixel >= 4

                long v = seq.value(seq.size - 1);
                if (v >= vMin && v <= vMax) {
                    x = r.x + r.width + 2;
                    y = topMargin+h-(int)(h * (v-vMin) / (vMax-vMin));
                    int y2 = getValueStringSlot(valueStringSlots, y, 2*10, i);
                    g.setFont(smallFont);
                    if (bgIsLight) {
                        g.setColor(seq.color);
                    } else {
                        g.setColor(fg);
                    }
                    String curValue = getFormattedValue(v, true);
                    if (unit == Unit.PERCENT) {
                        curValue += "%";
                    }
                    int valWidth = fm.stringWidth(curValue);
                    String legend = (displayLegend?seq.name:"");
                    int legendWidth = fm.stringWidth(legend);
                    if (checkRightMargin(valWidth) || checkRightMargin(legendWidth)) {
                        // Wait for next repaint
                        return;
                    }
                    g.drawString(legend  , x + 17, Math.min(topMargin+h,      y2 + 3 - 10));
                    g.drawString(curValue, x + 17, Math.min(topMargin+h + 10, y2 + 3));

                    // Maybe draw a short line to value
                    if (y2 > y + 3) {
                        g.drawLine(x + 9, y + 2, x + 14, y2);
                    } else if (y2 < y - 3) {
                        g.drawLine(x + 9, y - 2, x + 14, y2);
                    }
                }
                g.setFont(oldFont);
                g.setColor(fg);

            }
        }
        g.setColor(oldColor);
    }

    private boolean checkLeftMargin(int x) {
        // Make sure leftMargin has at least 2 pixels over
        if (x < 2) {
            leftMargin += (2 - x);
            // Repaint from top (above any cell renderers)
            SwingUtilities.getWindowAncestor(this).repaint();
            return true;
        }
        return false;
    }

    private boolean checkRightMargin(int w) {
        // Make sure rightMargin has at least 2 pixels over
        if (w + 2 > rightMargin) {
            rightMargin = (w + 2);
            // Repaint from top (above any cell renderers)
            SwingUtilities.getWindowAncestor(this).repaint();
            return true;
        }
        return false;
    }

    private int getValueStringSlot(int[] slots, int y, int h, int i) {
        for (int s = 0; s < slots.length; s++) {
            if (slots[s] >= y && slots[s] < y + h) {
                // collide below us
                if (slots[s] > h) {
                    return getValueStringSlot(slots, slots[s]-h, h, i);
                } else {
                    return getValueStringSlot(slots, slots[s]+h, h, i);
                }
            } else if (y >= h && slots[s] > y - h && slots[s] < y) {
                // collide above us
                return getValueStringSlot(slots, slots[s]+h, h, i);
            }
        }
        slots[i] = y;
        return y;
    }

    private long calculateTickInterval(int w, int hGap, long viewRangeMS) {
        long tickInterval = viewRangeMS * hGap / w;
        if (tickInterval < 1 * MINUTE) {
            tickInterval = 1 * MINUTE;
        } else if (tickInterval < 5 * MINUTE) {
            tickInterval = 5 * MINUTE;
        } else if (tickInterval < 10 * MINUTE) {
            tickInterval = 10 * MINUTE;
        } else if (tickInterval < 30 * MINUTE) {
            tickInterval = 30 * MINUTE;
        } else if (tickInterval < 1 * HOUR) {
            tickInterval = 1 * HOUR;
        } else if (tickInterval < 3 * HOUR) {
            tickInterval = 3 * HOUR;
        } else if (tickInterval < 6 * HOUR) {
            tickInterval = 6 * HOUR;
        } else if (tickInterval < 12 * HOUR) {
            tickInterval = 12 * HOUR;
        } else if (tickInterval < 1 * DAY) {
            tickInterval = 1 * DAY;
        } else {
            tickInterval = normalizeMax(tickInterval / DAY) * DAY;
        }
        return tickInterval;
    }

    private long normalizeMin(long l) {
        int exp = (int)Math.log10((double)l);
        long multiple = (long)Math.pow(10.0, exp);
        int i = (int)(l / multiple);
        return i * multiple;
    }

    private long normalizeMax(long l) {
        int exp = (int)Math.log10((double)l);
        long multiple = (long)Math.pow(10.0, exp);
        int i = (int)(l / multiple);
        l = (i+1)*multiple;
        return l;
    }

    private String getFormattedValue(long v, boolean groupDigits) {
        String str;
        String fmt = "%";
        if (groupDigits) {
            fmt += ",";
        }
        if (decimals > 0) {
            fmt += "." + decimals + "f";
            str = String.format(fmt, v / decimalsMultiplier);
        } else {
            fmt += "d";
            str = String.format(fmt, v);
        }
        return str;
    }

    private String getSizeString(long v, long vMax) {
        String s;

        if (unit == Unit.BYTES && decimals == 0) {
            s = formatBytes(v, vMax);
        } else {
            s = getFormattedValue(v, true);
        }
        return s;
    }

    private static synchronized Stroke getDashedStroke() {
        if (dashedStroke == null) {
            dashedStroke = new BasicStroke(1.0f,
                                           BasicStroke.CAP_BUTT,
                                           BasicStroke.JOIN_MITER,
                                           10.0f,
                                           new float[] { 2.0f, 3.0f },
                                           0.0f);
        }
        return dashedStroke;
    }

    private static Object extendArray(Object a1) {
        int n = Array.getLength(a1);
        Object a2 =
            Array.newInstance(a1.getClass().getComponentType(),
                              n + ARRAY_SIZE_INCREMENT);
        System.arraycopy(a1, 0, a2, 0, n);
        return a2;
    }


    private static class TimeStamps {
        // Time stamps (long) are split into offsets (long) and a
        // series of times from the offsets (int). A new offset is
        // stored when the the time value doesn't fit in an int
        // (approx every 24 days).  An array of indices is used to
        // define the starting point for each offset in the times
        // array.
        long[] offsets = new long[0];
        int[] indices = new int[0];
        int[] rtimes = new int[ARRAY_SIZE_INCREMENT];

        // Number of stored timestamps
        int size = 0;

        /**
         * Returns the time stamp for index i
         */
        public long time(int i) {
            long offset = 0;
            for (int j = indices.length - 1; j >= 0; j--) {
                if (i >= indices[j]) {
                    offset = offsets[j];
                    break;
                }
            }
            return offset + rtimes[i];
        }

        public void add(long time) {
            // May need to store a new time offset
            int n = offsets.length;
            if (n == 0 || time - offsets[n - 1] > Integer.MAX_VALUE) {
                // Grow offset and indices arrays and store new offset
                offsets = Arrays.copyOf(offsets, n + 1);
                offsets[n] = time;
                indices = Arrays.copyOf(indices, n + 1);
                indices[n] = size;
            }

            // May need to extend the array size
            if (rtimes.length == size) {
                rtimes = (int[])extendArray(rtimes);
            }

            // Store the time
            rtimes[size]  = (int)(time - offsets[offsets.length - 1]);
            size++;
        }
    }

    private static class Sequence {
        String key;
        String name;
        Color color;
        boolean isPlotted;
        Stroke transitionStroke = null;

        // Values are stored in an int[] if all values will fit,
        // otherwise in a long[]. An int can represent up to 2 GB.
        // Use a random start size, so all arrays won't need to
        // be grown during the same update interval
        Object values =
            new byte[ARRAY_SIZE_INCREMENT + (int)(Math.random() * 100)];

        // Number of stored values
        int size = 0;

        public Sequence(String key) {
            this.key = key;
        }

        /**
         * Returns the value at index i
         */
        public long value(int i) {
            return Array.getLong(values, i);
        }

        public void add(long value) {
            // May need to switch to a larger array type
            if ((values instanceof byte[] ||
                 values instanceof short[] ||
                 values instanceof int[]) &&
                       value > Integer.MAX_VALUE) {
                long[] la = new long[Array.getLength(values)];
                for (int i = 0; i < size; i++) {
                    la[i] = Array.getLong(values, i);
                }
                values = la;
            } else if ((values instanceof byte[] ||
                        values instanceof short[]) &&
                       value > Short.MAX_VALUE) {
                int[] ia = new int[Array.getLength(values)];
                for (int i = 0; i < size; i++) {
                    ia[i] = Array.getInt(values, i);
                }
                values = ia;
            } else if (values instanceof byte[] &&
                       value > Byte.MAX_VALUE) {
                short[] sa = new short[Array.getLength(values)];
                for (int i = 0; i < size; i++) {
                    sa[i] = Array.getShort(values, i);
                }
                values = sa;
            }

            // May need to extend the array size
            if (Array.getLength(values) == size) {
                values = extendArray(values);
            }

            // Store the value
            if (values instanceof long[]) {
                ((long[])values)[size] = value;
            } else if (values instanceof int[]) {
                ((int[])values)[size] = (int)value;
            } else if (values instanceof short[]) {
                ((short[])values)[size] = (short)value;
            } else {
                ((byte[])values)[size] = (byte)value;
            }
            size++;
        }
    }

    // Can be overridden by subclasses
    long getValue() {
        return 0;
    }

    long getLastTimeStamp() {
        return times.time(times.size - 1);
    }

    long getLastValue(String key) {
        Sequence seq = getSequence(key);
        return (seq != null && seq.size > 0) ? seq.value(seq.size - 1) : 0L;
    }


    // Called on EDT
    public void propertyChange(PropertyChangeEvent ev) {
        String prop = ev.getPropertyName();

        if (prop == JConsoleContext.CONNECTION_STATE_PROPERTY) {
            ConnectionState newState = (ConnectionState)ev.getNewValue();

            switch (newState) {
              case DISCONNECTED:
                synchronized(this) {
                    long time = System.currentTimeMillis();
                    times.add(time);
                    for (Sequence seq : seqs) {
                        seq.add(Long.MIN_VALUE);
                    }
                }
                break;
            }
        }
    }

    private static class SaveDataFileChooser extends JFileChooser {
        private static final long serialVersionUID = -5182890922369369669L;
        SaveDataFileChooser() {
            setFileFilter(new FileNameExtensionFilter("CSV file", "csv"));
        }

        @Override
        public void approveSelection() {
            File file = getSelectedFile();
            if (file != null) {
                FileFilter filter = getFileFilter();
                if (filter != null && filter instanceof FileNameExtensionFilter) {
                    String[] extensions =
                        ((FileNameExtensionFilter)filter).getExtensions();

                    boolean goodExt = false;
                    for (String ext : extensions) {
                        if (file.getName().toLowerCase().endsWith("." + ext.toLowerCase())) {
                            goodExt = true;
                            break;
                        }
                    }
                    if (!goodExt) {
                        file = new File(file.getParent(),
                                        file.getName() + "." + extensions[0]);
                    }
                }

                if (file.exists()) {
                    String okStr = Messages.FILE_CHOOSER_FILE_EXISTS_OK_OPTION;
                    String cancelStr = Messages.FILE_CHOOSER_FILE_EXISTS_CANCEL_OPTION;
                    int ret =
                        JOptionPane.showOptionDialog(this,
                                                     Resources.format(Messages.FILE_CHOOSER_FILE_EXISTS_MESSAGE,
                                                                      file.getName()),
                                                     Messages.FILE_CHOOSER_FILE_EXISTS_TITLE,
                                                     JOptionPane.OK_CANCEL_OPTION,
                                                     JOptionPane.WARNING_MESSAGE,
                                                     null,
                                                     new Object[] { okStr, cancelStr },
                                                     okStr);
                    if (ret != JOptionPane.OK_OPTION) {
                        return;
                    }
                }
                setSelectedFile(file);
            }
            super.approveSelection();
        }
    }

    @Override
    public AccessibleContext getAccessibleContext() {
        if (accessibleContext == null) {
            accessibleContext = new AccessiblePlotter();
        }
        return accessibleContext;
    }

    protected class AccessiblePlotter extends AccessibleJComponent {
        private static final long serialVersionUID = -3847205410473510922L;
        protected AccessiblePlotter() {
            setAccessibleName(Messages.PLOTTER_ACCESSIBLE_NAME);
        }

        @Override
        public String getAccessibleName() {
            String name = super.getAccessibleName();

            if (seqs.size() > 0 && seqs.get(0).size > 0) {
                String keyValueList = "";
                for (Sequence seq : seqs) {
                    if (seq.isPlotted) {
                        String value = "null";
                        if (seq.size > 0) {
                            if (unit == Unit.BYTES) {
                                value = Resources.format(Messages.SIZE_BYTES, seq.value(seq.size - 1));
                            } else {
                                value =
                                    getFormattedValue(seq.value(seq.size - 1), false) +
                                    ((unit == Unit.PERCENT) ? "%" : "");
                            }
                        }
                        // Assume format string ends with newline
                        keyValueList +=
                            Resources.format(Messages.PLOTTER_ACCESSIBLE_NAME_KEY_AND_VALUE,
                                    seq.key, value);
                    }
                }
                name += "\n" + keyValueList + ".";
            } else {
                name += "\n" + Messages.PLOTTER_ACCESSIBLE_NAME_NO_DATA;
            }
            return name;
        }

        @Override
        public AccessibleRole getAccessibleRole() {
            return AccessibleRole.CANVAS;
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java Plotter.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.