import java.awt.AWTEvent; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.event.ComponentEvent; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.lang.management.MemoryUsage; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import javax.swing.JComponent; public class MemoryUsageGraph extends JComponent { private long init; private long used; private long committed; private long max; private DecimalFormat format; private Rectangle2D overviewBase; private Rectangle2D detailBase; private List overviewScales; private List detailScales; private float scaleLength; private boolean resizeFlag = false; private Font scaleFont; private float ascent; private final static Color COMMITTED_COLOR = new Color(0, 96, 0); private final static Color USED_COLOR = Color.YELLOW; private final static Stroke DASH_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, new float[]{4.0f}, 0.0f); private final static String FONT_NAME = "Dialog"; private final static int MIN_FONT_SIZE = 10; private final static int MAX_FONT_SIZE = 16; private final static float MIN_SCALE_LENGTH = 4.0f; private final static float MAX_SCALE_LENGTH = 12.0f; private final static float SCALE_LENGTH_RATIO = 0.04f; private final static double TRANS_B_TO_M = 1024.0 * 1024.0; private final static float TOP_RATIO = 0.05f; private final static float BOTTOM_RATIO = 0.95f; private final static float LEFT_RATIO = 0.05f; private final static float GRAPH_WIDTH_RATIO = 0.90f; private final static float OVERVIEW_HEIGHT_RATIO = 0.25f; private final static float DETAIL_HEIGHT_RATIO = 0.55f; private final static float GRAPH_GAP_RATIO = 0.10f; private final static float OVERVIEW_BOTTOM_RATIO = TOP_RATIO + OVERVIEW_HEIGHT_RATIO; private final static float DETAIL_TOP_RATIO = OVERVIEW_BOTTOM_RATIO + GRAPH_GAP_RATIO; private final static float RIGHT_RATIO = LEFT_RATIO + GRAPH_WIDTH_RATIO; public MemoryUsageGraph() { super(); enableEvents(AWTEvent.COMPONENT_EVENT_MASK); setPreferredSize(new Dimension(200, 100)); format = new DecimalFormat("0.0"); resizeFlag = true; } protected void processComponentEvent(ComponentEvent event) { if (event.getID() == ComponentEvent.COMPONENT_RESIZED) { resizeFlag = true; } super.processComponentEvent(event); } public void setBounds(int x, int y, int width, int height) { resizeFlag = true; super.setBounds(x, y, width, height); } private synchronized void setInit(long init) { this.init = init; } private synchronized long getInit() { return init; } private synchronized void setUsed(long used) { this.used = used; } private synchronized long getUsed() { return used; } private synchronized void setCommitted(long committed) { this.committed = committed; } private synchronized long getCommitted() { return committed; } private synchronized void setMax(long max) { this.max = max; } private synchronized long getMax() { return max; } public void setMemoryUsage(MemoryUsage usage) { if (usage == null) { return; } setInit(usage.getInit()); setUsed(usage.getUsed()); setCommitted(usage.getCommitted()); setMax(usage.getMax()); repaint(); } private void createBaseGraph(Graphics2D g2d, Dimension d) { int fontSize = (int)(Math.sqrt(d.width * d.width + d.height * d.height) / 50.0); if (fontSize < MIN_FONT_SIZE) { fontSize = MIN_FONT_SIZE; } else if (fontSize > MAX_FONT_SIZE) { fontSize = MAX_FONT_SIZE; } scaleFont = new Font(FONT_NAME, Font.PLAIN, fontSize); FontRenderContext context = g2d.getFontRenderContext(); TextLayout layout = new TextLayout("a", scaleFont, context); ascent = layout.getAscent(); overviewScales = new ArrayList(); detailScales = new ArrayList(); float startX = d.width * LEFT_RATIO; scaleLength = d.height * SCALE_LENGTH_RATIO; if (MAX_SCALE_LENGTH < scaleLength) { scaleLength = MAX_SCALE_LENGTH; } else if (MIN_SCALE_LENGTH > scaleLength) { scaleLength = MIN_SCALE_LENGTH; } float overviewY = d.height * TOP_RATIO + ascent; overviewScales.add(new Line2D.Float(startX, overviewY, startX, overviewY + scaleLength)); float detailY = d.height * BOTTOM_RATIO - ascent; detailScales.add(new Line2D.Float(startX, detailY - scaleLength, startX, detailY)); float w1 = d.width * GRAPH_WIDTH_RATIO / 2.0f; float w2 = d.width * GRAPH_WIDTH_RATIO / 10.0f; for (int i = 0; i < 2; i++) { float x; for (int j = 1; j < 5; j++) { x = startX + w1 * i + w2 * j; overviewScales.add(new Line2D.Float(x, overviewY + scaleLength/2.0f, x, overviewY + scaleLength)); detailScales.add(new Line2D.Float(x, detailY - scaleLength, x, detailY - scaleLength/2.0f)); } x = startX + w1 * (i + 1); overviewScales.add(new Line2D.Float(x, overviewY, x, overviewY + scaleLength)); detailScales.add(new Line2D.Float(x, detailY - scaleLength, x, detailY)); } overviewBase = new Rectangle2D.Float(d.width * LEFT_RATIO, overviewY + scaleLength, d.width * GRAPH_WIDTH_RATIO, d.height * OVERVIEW_HEIGHT_RATIO - ascent - scaleLength); detailBase = new Rectangle2D.Float(d.width * LEFT_RATIO, d.height * DETAIL_TOP_RATIO, d.width * GRAPH_WIDTH_RATIO, d.height * DETAIL_HEIGHT_RATIO - ascent - scaleLength); } // 対応線の描画 private void drawCorrespondLines(Graphics2D g2d, Dimension d, float com) { g2d.setColor(Color.DARK_GRAY); Stroke orig = g2d.getStroke(); g2d.setStroke(DASH_STROKE); Line2D line = new Line2D.Float(d.width * LEFT_RATIO, d.height * OVERVIEW_BOTTOM_RATIO, d.width * LEFT_RATIO, d.height * DETAIL_TOP_RATIO); g2d.draw(line); line = new Line2D.Float(d.width * LEFT_RATIO + com, d.height * OVERVIEW_BOTTOM_RATIO, d.width * RIGHT_RATIO, d.height * DETAIL_TOP_RATIO); g2d.draw(line); g2d.setStroke(orig); } private void drawUsedGraph(Graphics2D g2d, Dimension d) { float startX = d.width * LEFT_RATIO; float extLength = ascent + scaleLength; float com = (float)((double)getCommitted() / (double)getMax() * d.width * GRAPH_WIDTH_RATIO); Rectangle2D value = new Rectangle2D.Float(startX, d.height * TOP_RATIO + extLength, com, d.height * OVERVIEW_HEIGHT_RATIO - extLength); g2d.setColor(COMMITTED_COLOR); g2d.fill(value); float usedO = (float)((double)getUsed() / (double)getMax() * d.width * GRAPH_WIDTH_RATIO); value = new Rectangle2D.Float(startX, d.height * TOP_RATIO + extLength, usedO, d.height * OVERVIEW_HEIGHT_RATIO - extLength); g2d.setColor(USED_COLOR); g2d.fill(value); float usedD = (float)((double)getUsed() / (double)getCommitted() * d.width * GRAPH_WIDTH_RATIO); value = new Rectangle2D.Float(startX, d.height * DETAIL_TOP_RATIO, usedD, d.height * DETAIL_HEIGHT_RATIO - extLength); g2d.fill(value); drawCorrespondLines(g2d, d, com); } // スケールの描画 private void drawScale(Graphics2D g2d, Dimension d) { g2d.setColor(Color.BLACK); for (Line2D line: overviewScales) { g2d.draw(line); } for (Line2D line: detailScales) { g2d.draw(line); } FontRenderContext context = g2d.getFontRenderContext(); TextLayout layout = new TextLayout(format.format(0), scaleFont, context); layout.draw(g2d, d.width * LEFT_RATIO, d.height * 0.05f + layout.getAscent()); layout.draw(g2d, d.width * LEFT_RATIO, d.height * BOTTOM_RATIO); layout = new TextLayout(format.format(getMax()/TRANS_B_TO_M), scaleFont, context); layout.draw(g2d, d.width * RIGHT_RATIO - layout.getAdvance(), d.height * TOP_RATIO + layout.getAscent()); layout = new TextLayout(format.format(getCommitted()/TRANS_B_TO_M), scaleFont, context); layout.draw(g2d, d.width * RIGHT_RATIO - layout.getAdvance(), d.height * BOTTOM_RATIO); } public void paintComponent(Graphics g) { Dimension d = getSize(); Graphics2D g2d = (Graphics2D)g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if (resizeFlag) { createBaseGraph(g2d, d); resizeFlag = false; } g2d.setColor(Color.BLACK); g2d.fill(overviewBase); g2d.setColor(COMMITTED_COLOR); g2d.fill(detailBase); if (getMax() != 0) { drawUsedGraph(g2d, d); } g2d.setColor(Color.BLACK); g2d.draw(overviewBase); g2d.draw(detailBase); drawScale(g2d, d); } }