/* * DebugDialog.java * Copyright 2011 Connor Petty <cpmeister@users.sourceforge.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on Mar 14, 2011, 3:45:35 PM */ package pcgen.gui2.dialog; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Desktop; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; import java.net.URI; import java.net.URISyntaxException; import java.text.DecimalFormat; import java.text.MessageFormat; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import pcgen.gui2.PCGenFrame; import pcgen.gui2.tools.Utility; import pcgen.system.LoggingRecorder; import pcgen.util.Logging; /** * * @author Connor Petty <cpmeister@users.sourceforge.net> */ public class DebugDialog extends JDialog { private static MemoryMXBean memoryBean = ManagementFactory .getMemoryMXBean(); private final LogPanel logPanel; private final MemoryPanel memoryPanel; public DebugDialog(PCGenFrame frame) { super(frame); setTitle("Log & Memory Use"); logPanel = new LogPanel(); memoryPanel = new MemoryPanel(); initComponents(); pack(); setSize(700, 500); Utility.installEscapeCloseOperation(this); } private void initComponents() { Container contentPane = getContentPane(); contentPane.setLayout(new BorderLayout()); contentPane.add(logPanel, BorderLayout.CENTER); contentPane.add(memoryPanel, BorderLayout.SOUTH); setDefaultCloseOperation(HIDE_ON_CLOSE); } @Override public void dispose() { super.dispose(); memoryPanel.dispose(); } private static class LogPanel extends JPanel implements ActionListener, MouseListener { private final JTextArea logText; private final JButton clearButton; public LogPanel() { logText = new JTextArea(); clearButton = new JButton("Clear Log"); initComponents(); } private void initComponents() { setBorder(BorderFactory.createTitledBorder("Debug Log")); setLayout(new BorderLayout()); add(new JScrollPane(logText) { @Override public Dimension getMaximumSize() { return super.getPreferredSize(); } }, BorderLayout.CENTER); add(clearButton, BorderLayout.SOUTH); logText.setFocusable(true); logText.setEditable(false); logText.addMouseListener(this); clearButton.setActionCommand("CLEAR"); clearButton.addActionListener(this); initDebugLog(); } private void initDebugLog() { // logText.setLineWrap(true); logText.setEditable(false); logText.setText(LoggingRecorder.getLogs()); Logging.registerHandler(new LogHandler()); } @Override public void mouseClicked(MouseEvent e) { int caretPos = determineCaretPosition(e); String line = getCurrentIndexedLine(caretPos); File file = extractFileFromLine(line); if (file != null) { try { openFile(file); } catch (IOException e1) { Logging.log(Level.WARNING, "Unable to open the requested file: " + file.getName(), e1); } } else { Logging.log(Level.FINER, "No file in current line."); } } @Override public void actionPerformed(ActionEvent e) { if ("CLEAR".equals(e.getActionCommand())) { LoggingRecorder.clearLogs(); initDebugLog(); } else { logText.repaint(); } } private void openFile(File file) throws IOException { if (Desktop.isDesktopSupported()) { if (System.getProperty("os.name").toLowerCase().contains("windows")) { String cmd = "rundll32 url.dll,FileProtocolHandler " + file.getCanonicalPath(); Runtime.getRuntime().exec(cmd); } else { Desktop.getDesktop().open(file); } } } protected File extractFileFromLine(String line) { File file = null; String[] parts = line.split("\""); for (String part : parts) { file = convertURItoFileIfPossible(part); if (file == null) { String[] subParts = part.split(" "); for (String subPart : subParts) { file = convertURItoFileIfPossible(subPart); if (file != null) { return file; } } } else { return file; } } return file; } private File convertURItoFileIfPossible(String filePart) { File file = null; URI fileURI; try { fileURI = extractFileURIFromLinePart(filePart); file = new File(fileURI); if (!file.exists()) { file = null; } } catch (Exception e) { // This part is NOT a valid URI. No harm done. } return file; } private URI extractFileURIFromLinePart(String part) throws URISyntaxException { String filePart = part; if (part.indexOf(':') < 0) { filePart = "file://" + part; } return new URI(filePart); } private String getCurrentIndexedLine(int index) { int startIndex = logText.getText().lastIndexOf("\n", index) + 1; int endIndex = logText.getText().indexOf("\n", index); String line = ""; if (startIndex >= 0 && endIndex >= startIndex) { line = logText.getText().substring(startIndex, endIndex); } return line; } private int determineCaretPosition(MouseEvent e) { logText.setCaretPosition(logText.viewToModel(e.getPoint())); int caretPos = logText.getCaretPosition(); return caretPos; } private class LogHandler extends Handler implements Runnable { public LogHandler() { setLevel(Logging.DEBUG); } @Override public void publish(LogRecord record) { SwingUtilities.invokeLater(this); } @Override public void flush() { } @Override public void close() throws SecurityException { } @Override public void run() { logText.setText(LoggingRecorder.getLogs()); } } @Override public void mouseEntered(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mouseReleased(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mouseExited(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mousePressed(MouseEvent arg0) { // TODO Auto-generated method stub } } private static class MemoryPanel extends JPanel implements ActionListener { private final Timer timer; private final JButton gcButton; private final JTable memoryTable; public MemoryPanel() { timer = new Timer(1000, this); gcButton = new JButton("Run Garbage Collection"); memoryTable = new JTable(new MemoryTableModel()); initComponents(); timer.start(); } private void initComponents() { setBorder(BorderFactory.createTitledBorder("Memory Usage")); setLayout(new BorderLayout()); add(new JScrollPane(memoryTable) { @Override public Dimension getMaximumSize() { return super.getPreferredSize(); } }, BorderLayout.CENTER); memoryTable.setFocusable(false); memoryTable.setRowSelectionAllowed(false); memoryTable.setPreferredScrollableViewportSize(memoryTable .getPreferredSize()); memoryTable.setDefaultRenderer(Long.class, new DefaultTableCellRenderer() { DecimalFormat format = new DecimalFormat("###,###,###"); @Override protected void setValue(Object value) { setHorizontalAlignment(SwingConstants.RIGHT); setText(format.format(value)); } }); gcButton.setActionCommand("COLLECT"); gcButton.addActionListener(this); add(gcButton, BorderLayout.SOUTH); } public void dispose() { timer.stop(); } @Override public void actionPerformed(ActionEvent e) { if ("COLLECT".equals(e.getActionCommand())) { memoryBean.gc(); Logging.log(Logging.INFO, MessageFormat.format( "Memory used after manual GC, Heap: {0}, Non heap: {1}", memoryBean.getHeapMemoryUsage().getUsed(), memoryBean .getNonHeapMemoryUsage().getUsed())); } else { memoryTable.repaint(); } } } private static class MemoryTableModel extends AbstractTableModel { private static long megaByte = 1024 * 1024; @Override public int getRowCount() { return 2; } @Override public int getColumnCount() { return 6; } @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex == 0) { return String.class; } else { return Long.class; } } @Override public String getColumnName(int column) { switch (column) { case 0: return ""; case 1: return "Initial"; case 2: return "Used"; case 3: return "Committed"; case 4: return "Max"; case 5: return "% Used"; default: return super.getColumnName(column); } } @Override public Object getValueAt(int rowIndex, int columnIndex) { MemoryUsage usage; if (rowIndex == 0) { usage = memoryBean.getHeapMemoryUsage(); } else { usage = memoryBean.getNonHeapMemoryUsage(); } switch (columnIndex) { case 0: return (rowIndex == 0) ? "Heap" : "Non-Heap"; case 1: return usage.getInit(); // / megaByte; case 2: return usage.getUsed(); // / megaByte; case 3: return usage.getCommitted(); // / megaByte; case 4: return usage.getMax(); // / megaByte; case 5: return (100*usage.getUsed())/usage.getMax(); // / percent default: return 0; } } } }