/* * Copyright (c) 2004, 2008, 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 com.sun.tools.jconsole.JConsoleContext.ConnectionState.*; import static sun.tools.jconsole.Formatter.*;import static sun.tools.jconsole.ProxyClient.*;import static sun.tools.jconsole.Resources.*;import static sun.tools.jconsole.Utilities.*; @SuppressWarnings("serial")public class Plotter extends JComponent implements Accessible, ActionListener, PropertyChangeListener { public static enum Unit { NONE, BYTES, PERCENT } static final String[] rangeNames = { Resources.getText(" 1 min"), Resources.getText(" 5 min"), Resources.getText("10 min"), Resources.getText("30 min"), Resources.getText(" 1 hour"), Resources.getText(" 2 hours"), Resources.getText(" 3 hours"), Resources.getText(" 6 hours"), Resources.getText("12 hours"), Resources.getText(" 1 day"), Resources.getText(" 7 days"), Resources.getText(" 1 month"), Resources.getText(" 3 months"), Resources.getText(" 6 months"), Resources.getText(" 1 year"), Resources.getText("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<Sequence>(); 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(Resources.getText("Chart:")); timeRangeMenu = new JMenu(Resources.getText("Plotter.timeRangeMenu")); timeRangeMenu.setMnemonic(getMnemonicInt("Plotter.timeRangeMenu")); 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(getText("Plotter.saveAsMenuItem")); saveAsMI.setMnemonic(getMnemonicInt("Plotter.saveAsMenuItem")); 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, getText("FileChooser.savedFile", 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, getText("FileChooser.saveFailed.message", path, msg), getText("FileChooser.saveFailed.title"), JOptionPane.ERROR_MESSAGE); } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); 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<Long>(); 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); } Lines 1–500 of 1095. Load more
/* * Copyright (c) 2004, 2008, 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 com.sun.tools.jconsole.JConsoleContext.ConnectionState.*; import static sun.tools.jconsole.Formatter.*; import static sun.tools.jconsole.ProxyClient.*; import static sun.tools.jconsole.Resources.*; import static sun.tools.jconsole.Utilities.*; @SuppressWarnings("serial") public class Plotter extends JComponent implements Accessible, ActionListener, PropertyChangeListener { public static enum Unit { NONE, BYTES, PERCENT } static final String[] rangeNames = { Resources.getText(" 1 min"), Resources.getText(" 5 min"), Resources.getText("10 min"), Resources.getText("30 min"), Resources.getText(" 1 hour"), Resources.getText(" 2 hours"), Resources.getText(" 3 hours"), Resources.getText(" 6 hours"), Resources.getText("12 hours"), Resources.getText(" 1 day"), Resources.getText(" 7 days"), Resources.getText(" 1 month"), Resources.getText(" 3 months"), Resources.getText(" 6 months"), Resources.getText(" 1 year"), Resources.getText("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<Sequence>(); 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(Resources.getText("Chart:")); timeRangeMenu = new JMenu(Resources.getText("Plotter.timeRangeMenu")); timeRangeMenu.setMnemonic(getMnemonicInt("Plotter.timeRangeMenu")); 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(getText("Plotter.saveAsMenuItem")); saveAsMI.setMnemonic(getMnemonicInt("Plotter.saveAsMenuItem")); 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, getText("FileChooser.savedFile", 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, getText("FileChooser.saveFailed.message", path, msg), getText("FileChooser.saveFailed.title"), JOptionPane.ERROR_MESSAGE); } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); 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<Long>(); 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); }