package net.cakenet.jsaton.ui.tools;
import net.cakenet.jsaton.util.ImageUtil;
import net.cakenet.jsaton.util.TimeUtil;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.SortedMap;
import java.util.TreeMap;
public class MemoryUsageWindow extends ToolWindow {
private static final int[] values = new int[]{10, 25, 50, 100, 250, 500, 1000, 1500, 2000, 3000, 4000, 5000};
private static final int HISTORY_LEN = 2048;
private JPanel panel1;
private JProgressBar usagePercentage;
private JButton gc;
private JLabel graph;
private JSpinner intervalSelect;
private BufferedImage buffer;
private TreeMap<Long, Long> history = new TreeMap<>();
private transient int interval = 500;
public MemoryUsageWindow() {
super("Memory usage");
gc.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
new Thread() {
public void run() {
Runtime.getRuntime().gc();
}
}.start();
}
});
Thread t = new Thread() {
public void run() {
while (true) {
updateUsage();
try {
Thread.sleep(interval);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
graph.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
long curTime = System.currentTimeMillis();
int w = getWidth();
int idx = (w - e.getX());
long timeGuest = curTime - (interval * idx);
Long key = history.floorKey(timeGuest);
if (key == null)
graph.setToolTipText(null);
else {
graph.setToolTipText(String.format("%dMB about %s ago", history.get(key) / 1048576L,
TimeUtil.timeString((int) (curTime - key))));
}
}
});
t.setDaemon(true);
t.setPriority(1);
t.start();
intervalSelect.setEditor(new JSpinner.NumberEditor(intervalSelect));
intervalSelect.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
interval = (Integer) intervalSelect.getValue();
updateUsage();
}
});
}
private void updateUsage() {
long curTime = System.currentTimeMillis();
Runtime r = Runtime.getRuntime();
long total = r.totalMemory();
long free = r.freeMemory();
long used = total - free;
history.put(curTime, used);
if (history.size() > HISTORY_LEN)
history.pollFirstEntry();
// Draw...
// Find maximum in history...
if (!isVisible())
return;
Dimension size = graph.getSize();
if (size.width <= 0 || size.height <= 0)
return;
if (buffer == null || buffer.getWidth() != size.width || buffer.getHeight() != size.height) {
buffer = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
graph.setIcon(new ImageIcon(buffer));
}
// do it....
final long time_min = curTime - (interval * size.width);
Graphics g = buffer.getGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, size.width, size.height);
// Todo: draw grid...
final int[] graph_intervales = new int[]{500, 1000, 5000, 10000, 20000};
final int mem_megs = (int) (total / 1048576L);
int mem_interval = ((mem_megs - (mem_megs % 25)) / 25);
if (mem_interval == 0)
mem_interval = ((mem_megs - (mem_megs % 10)) / 10);
final Color graphColor = Color.BLUE;
Color intervalColor = graphColor.darker().darker();
final int min_int_width = 20;
int height = size.height / mem_interval;
g.setColor(intervalColor);
for (int y = 0; y < size.height; y += height)
g.drawLine(0, y, size.width, y);
for (int i = graph_intervales.length - 1; i >= 0; i--) {
int ival = graph_intervales[i];
float width = ((float) ival / (float) interval);
if (width < min_int_width)
continue;
g.setColor(intervalColor);
for (float x = size.width; x >= 0; x -= width)
g.drawLine((int) x, 0, (int) x, size.height);
intervalColor = intervalColor.darker().darker();
}
g.setColor(graphColor);
Long start = history.floorKey(time_min);
if (start == null)
start = history.ceilingKey(time_min);
SortedMap<Long, Long> interest = history.tailMap(start);
int prevY = -1;
int prevX = -1;
for (SortedMap.Entry<Long, Long> e : interest.entrySet()) {
long u = e.getValue();
long t = e.getKey();
int xOffset = (int) (curTime - t) / this.interval;
float perc = (((float) u) / ((float) total));
int y = size.height - (int) (perc * size.height);
int x = size.width - xOffset;
if (prevY != -1)
g.drawLine(prevX, prevY, x, y);
prevY = y;
prevX = x;
}
usagePercentage.setValue((int) ((((float) used) / ((float) total)) * 100));
usagePercentage.setString(String.format("%dMB of %dMB", used / 1048576L, total / 1048576L));
redraw();
}
private void redraw() {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
redraw();
}
});
return;
}
usagePercentage.repaint();
graph.repaint();
}
public Icon getIcon() {
return ImageUtil.loadIcon("icons/chart_line.png");
}
public Component getContents() {
return panel1;
}
private void createUIComponents() {
intervalSelect = new JSpinner(new SpinnerNumberModel() {
private java.util.List<ChangeListener> listeners = new LinkedList<>();
private int current = 500;
public Object getValue() {
return current;
}
public void setValue(Object value) {
this.current = (Integer) value;
if (listeners.isEmpty())
return;
ChangeEvent ce = new ChangeEvent(this);
for (ChangeListener l : listeners.toArray(new ChangeListener[listeners.size()]))
l.stateChanged(ce);
}
private int nearestIndex() {
for (int i = 0; i < values.length; i++) {
if (values[i] > current) {
int idx = i - 1;
if (idx < 0)
return 0;
return idx;
}
}
return values.length - 1;
}
public Object getNextValue() {
int idx = nearestIndex() + 1;
if (idx >= values.length)
return null;
return values[idx];
}
public Object getPreviousValue() {
int idx = nearestIndex() - 1;
if (idx < 0)
return null;
return values[idx];
}
public void addChangeListener(ChangeListener l) {
listeners.add(l);
}
public void removeChangeListener(ChangeListener l) {
listeners.remove(l);
}
});
}
}