/* * Copyright (c) 2012, Codename One 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. Codename One 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 Codename One through http://www.codenameone.com/ if you * need additional information or have any questions. */ package com.codename1.impl.javase; import com.codename1.ui.Component; import com.codename1.ui.Display; import com.codename1.ui.Image; import com.codename1.ui.Label; import java.awt.Color; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.font.FontRenderContext; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextLayout; import java.awt.image.BufferedImage; import java.io.PrintWriter; import java.io.StringWriter; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.BreakIterator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.AbstractCellEditor; import javax.swing.Action; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.JTree; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.LineBorder; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; import javax.swing.tree.DefaultTreeCellRenderer; /** * The Codename One performance monitor shows you how long (in nano-seconds) it took to paint a specific * * @author Shai Almog */ public class PerformanceMonitor extends javax.swing.JFrame { private int componentId = 0; private static String idString = "$prefid"; private Map<Integer, Stats> componentStats = new HashMap<Integer, Stats>(); private DefaultTableModel trackedDrawing; private boolean trackDrawing; private boolean paused; private static final String[] COLUMNS = { "Name", "Type", "UIID", "Parent Name", "Icon", "Invocations", "Fastest", "Slowest", "Average" }; private static final Class[] COLUMN_CLASSES = { String.class, String.class, String.class, String.class, String.class, Integer.class, Long.class, Long.class, Long.class }; private int imageRam; /** Creates new form PerformanceMonitor */ public PerformanceMonitor() { initComponents(); if(Display.getInstance().getCurrent() != null) { refreshFrameActionPerformed(null); } resultData.setModel(new Model()); performanceLog.setLineWrap(true); resultData.setRowSorter(new TableRowSorter<Model>((Model)resultData.getModel())); } public void addImageRAM(int ram) { imageRam += ram; imageMemory.setText("Image Memory Overhead: " + imageRam); } public void removeImageRAM(int ram) { imageRam -= ram; imageMemory.setText("Image Memory Overhead: " + imageRam); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jScrollPane2 = new javax.swing.JScrollPane(); jTable1 = new javax.swing.JTable(); jTabbedPane1 = new javax.swing.JTabbedPane(); jPanel1 = new javax.swing.JPanel(); jScrollPane1 = new javax.swing.JScrollPane(); resultData = new javax.swing.JTable(); clearData = new javax.swing.JButton(); pauseContinue = new javax.swing.JButton(); jLabel1 = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); jScrollPane3 = new javax.swing.JScrollPane(); performanceLog = new javax.swing.JTextArea(); imageMemory = new javax.swing.JLabel(); runGC = new javax.swing.JButton(); jPanel2 = new javax.swing.JPanel(); jSplitPane1 = new javax.swing.JSplitPane(); jScrollPane4 = new javax.swing.JScrollPane(); componentHierarchy = new javax.swing.JTree(); jScrollPane5 = new javax.swing.JScrollPane(); renderedItems = createJTable(); jToolBar1 = new javax.swing.JToolBar(); refreshFrame = new javax.swing.JButton(); FormListener formListener = new FormListener(); jTable1.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { {null, null, null, null}, {null, null, null, null}, {null, null, null, null}, {null, null, null, null} }, new String [] { "Title 1", "Title 2", "Title 3", "Title 4" } )); jScrollPane2.setViewportView(jTable1); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle("Performance Monitor"); addWindowListener(formListener); jScrollPane1.setViewportView(resultData); clearData.setText("Clear Data"); clearData.addActionListener(formListener); pauseContinue.setText("Pause/Continue"); pauseContinue.addActionListener(formListener); jLabel1.setText("Component Details (times are in nano-seconds: one billionth of a second)"); jLabel2.setText("Log"); performanceLog.setColumns(20); performanceLog.setRows(5); jScrollPane3.setViewportView(performanceLog); imageMemory.setText("Image Memory Overhead: (Calculating...)"); runGC.setText("GC"); runGC.addActionListener(formListener); javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(jScrollPane3, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 793, Short.MAX_VALUE) .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 793, Short.MAX_VALUE) .addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel1Layout.createSequentialGroup() .addComponent(clearData) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(pauseContinue) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 89, Short.MAX_VALUE) .addComponent(runGC) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(imageMemory)) .addComponent(jLabel1, javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING)) .addContainerGap()) ); jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {clearData, pauseContinue, runGC}); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(clearData) .addComponent(pauseContinue) .addComponent(imageMemory) .addComponent(runGC)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 170, Short.MAX_VALUE) .addGap(18, 18, 18) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 137, Short.MAX_VALUE) .addContainerGap()) ); jTabbedPane1.addTab("Generic Statistics", jPanel1); componentHierarchy.addTreeSelectionListener(formListener); jScrollPane4.setViewportView(componentHierarchy); jSplitPane1.setLeftComponent(jScrollPane4); renderedItems.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { {null, null, null, null}, {null, null, null, null}, {null, null, null, null}, {null, null, null, null} }, new String [] { "Title 1", "Title 2", "Title 3", "Title 4" } )); jScrollPane5.setViewportView(renderedItems); jSplitPane1.setRightComponent(jScrollPane5); jToolBar1.setRollover(true); refreshFrame.setText("Refresh"); refreshFrame.setFocusable(false); refreshFrame.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); refreshFrame.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); refreshFrame.addActionListener(formListener); jToolBar1.add(refreshFrame); javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); jPanel2.setLayout(jPanel2Layout); jPanel2Layout.setHorizontalGroup( jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 805, Short.MAX_VALUE) .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); jPanel2Layout.setVerticalGroup( jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup() .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 385, Short.MAX_VALUE)) ); jTabbedPane1.addTab("Rendering Details", jPanel2); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(jTabbedPane1) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(jTabbedPane1) .addContainerGap()) ); pack(); } // Code for dispatching events from components to event handlers. private class FormListener implements java.awt.event.ActionListener, java.awt.event.WindowListener, javax.swing.event.TreeSelectionListener { FormListener() {} public void actionPerformed(java.awt.event.ActionEvent evt) { if (evt.getSource() == clearData) { PerformanceMonitor.this.clearDataActionPerformed(evt); } else if (evt.getSource() == pauseContinue) { PerformanceMonitor.this.pauseContinueActionPerformed(evt); } else if (evt.getSource() == runGC) { PerformanceMonitor.this.runGCActionPerformed(evt); } else if (evt.getSource() == refreshFrame) { PerformanceMonitor.this.refreshFrameActionPerformed(evt); } } public void windowActivated(java.awt.event.WindowEvent evt) { } public void windowClosed(java.awt.event.WindowEvent evt) { } public void windowClosing(java.awt.event.WindowEvent evt) { if (evt.getSource() == PerformanceMonitor.this) { PerformanceMonitor.this.formWindowClosing(evt); } } public void windowDeactivated(java.awt.event.WindowEvent evt) { } public void windowDeiconified(java.awt.event.WindowEvent evt) { } public void windowIconified(java.awt.event.WindowEvent evt) { } public void windowOpened(java.awt.event.WindowEvent evt) { } public void valueChanged(javax.swing.event.TreeSelectionEvent evt) { if (evt.getSource() == componentHierarchy) { PerformanceMonitor.this.componentHierarchyValueChanged(evt); } } }// </editor-fold>//GEN-END:initComponents public void printToLog(String t) { performanceLog.append(t + "\n"); performanceLog.setCaretPosition(t.length()); } private void pauseContinueActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseContinueActionPerformed paused = !paused; }//GEN-LAST:event_pauseContinueActionPerformed private void clearDataActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearDataActionPerformed ((Model)resultData.getModel()).clear(); performanceLog.setText(""); }//GEN-LAST:event_clearDataActionPerformed private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing JavaSEPort.disablePerformanceMonitor(); }//GEN-LAST:event_formWindowClosing private void runGCActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_runGCActionPerformed System.gc(); System.gc(); }//GEN-LAST:event_runGCActionPerformed private void refreshFrameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshFrameActionPerformed componentHierarchy.setModel(new ComponentTreeModel(Display.getInstance().getCurrent())); componentHierarchy.setCellRenderer(new DefaultTreeCellRenderer() { @Override public java.awt.Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { String s = value.toString(); if(value instanceof Component) { s = ((Component)value).getUIID() + ": " + s; } return super.getTreeCellRendererComponent(tree, s, sel, expanded, leaf, row, hasFocus); //To change body of generated methods, choose Tools | Templates. } }); Display.getInstance().callSerially(new Runnable() { public void run() { trackDrawing = true; Display.getInstance().getCurrent().repaint(); Display.getInstance().callSerially(new Runnable() { public void run() { // data collected trackDrawing = false; renderedItems.setModel(createTableModel()); } }); } }); }//GEN-LAST:event_refreshFrameActionPerformed private void refreshComponentStatsTable(Component c) { TableModel tm = (TableModel)c.getClientProperty("track"); if(tm != null) { renderedItems.setModel(tm); } else { renderedItems.setModel(createTableModel()); } } private void componentHierarchyValueChanged(javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event_componentHierarchyValueChanged if(evt.getPath() != null) { Component c = (Component)evt.getPath().getLastPathComponent(); refreshComponentStatsTable(c); } }//GEN-LAST:event_componentHierarchyValueChanged public void nothingWithinComponentPaint(Component c) { if(trackDrawing) { if(c.isCellRenderer()) { return; } if(c.getParent() != null) { trackedDrawing = (DefaultTableModel)c.getParent().getClientProperty("track"); } } } private static DefaultTableModel createTableModel() { return new DefaultTableModel(new String[]{"Call", "Details", "Comments", "Stack", "Image"}, 0) { @Override public Class<?> getColumnClass(int columnIndex) { if(columnIndex == 3) { return Action.class; } if(columnIndex == 4) { return Icon.class; } return String.class; } }; } public void beforeComponentPaint(Component c) { if(trackDrawing) { if(c.isCellRenderer()) { return; } trackedDrawing = createTableModel(); c.putClientProperty("track", trackedDrawing); return; } if(!paused) { c.putClientProperty("$t", new Long(System.nanoTime())); } } public void afterComponentPaint(Component c) { if(trackDrawing) { if(c.isCellRenderer()) { return; } if(c.getParent() != null) { trackedDrawing = (DefaultTableModel)c.getParent().getClientProperty("track"); } return; } if(paused) { return; } long t = System.nanoTime(); Long l = (Long)c.getClientProperty("$t"); if(l != null) { t = t - l.longValue(); Integer id = (Integer)c.getClientProperty(idString); Stats st; if(id == null) { id = new Integer(componentId); c.putClientProperty(idString, id); componentId++; st = new Stats(c); componentStats.put(id, st); } else { st = componentStats.get(id); if(st == null) { st = new Stats(c); componentStats.put(id, st); } } st.updateInvocation(t); } } public static String getStackTrace(Throwable t) { StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); String s = sw.toString(); int pos = s.indexOf("at"); pos = s.indexOf("at", pos + 2); return s.substring(pos + 2); } public void setClip(int x, int y, int width, int height) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "setClip(" + x + ", " + y + ", " + width + ", " + height + ")", "", "", getStackTrace(new Throwable()), null }); } } public void clipRect(int x, int y, int width, int height) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "clipRect(" + x + ", " + y + ", " + width + ", " + height + ")", "", "", getStackTrace(new Throwable()), null }); } } public void drawLine(int x1, int y1, int x2, int y2) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "drawLine(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ")", "", "", getStackTrace(new Throwable()), null }); } } public void fillRect(int x, int y, int w, int h) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "fillRect(" + x + ", " + y + ", " + w + ", " + h + ")", "", "", getStackTrace(new Throwable()), null }); } } public void drawRect(int x, int y, int width, int height) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "drawRect(" + x + ", " + y + ", " + width + ", " + height + ")", "", "", getStackTrace(new Throwable()), null }); } } public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "drawRoundRect(" + x + ", " + y + ", " + width + ", " + height + ", " + arcWidth + ", " + arcHeight + ")", "", "", getStackTrace(new Throwable()), null }); } } public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "fillRoundRect(" + x + ", " + y + ", " + width + ", " + height + ", " + arcWidth + ", " + arcHeight + ")", "", "", getStackTrace(new Throwable()), null }); } } public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "fillArc(" + x + ", " + y + ", " + width + ", " + height + ", " + startAngle + ", " + arcAngle + ")", "", "", getStackTrace(new Throwable()), null }); } } public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "drawArc(" + x + ", " + y + ", " + width + ", " + height + ", " + startAngle + ", " + arcAngle + ")", "", "", getStackTrace(new Throwable()), null }); } } public void setColor(int RGB) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "setColor(0x" + Integer.toHexString(RGB) + ")", "", "", getStackTrace(new Throwable()), null }); } } public void setAlpha(int alpha) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "setAlpha(0x" + Integer.toHexString(alpha) + ")", "", "", getStackTrace(new Throwable()), null }); } } public void drawString(String str, int x, int y) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "drawString(" + str + ", " + x + ", " + y + ")", "", "", getStackTrace(new Throwable()), null }); } } public void drawImage(Object img, int x, int y) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "drawImage(" + x + ", " + y + ")", "Image size: " + ((BufferedImage) img).getWidth() + "x" + ((BufferedImage) img).getHeight(), "", getStackTrace(new Throwable()), new ImageIcon((BufferedImage)img) }); } } public void drawImage(Object img, int x, int y, int w, int h) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "drawImage(" + x + ", " + y + ", " + w + ", " + h + ")", "Image size: " + ((BufferedImage) img).getWidth() + "x" + ((BufferedImage) img).getHeight(), "This version of the method is slow on feature phones", getStackTrace(new Throwable()), new ImageIcon((BufferedImage)img) }); } } public void fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "fillTriangle(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ", " + x3 + ", " + y3 + ")", "", "", getStackTrace(new Throwable()), null }); } } public void drawRGB(int[] rgbData, int offset, int x, int y, int w, int h, boolean processAlpha) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "drawRGB(" + x + ", " + y + ", " + w + ", " + h + ")", "Rgb data length " + rgbData.length, "This method is problematic on some devices!", getStackTrace(new Throwable()), null }); } } public void stringWidth(Object nativeFont, String str) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "stringWidth(" + str + ")", "", "Slow method, don't overuse", getStackTrace(new Throwable()), null }); } } public void charWidth(Object nativeFont, char ch) { if(trackDrawing && trackedDrawing != null) { trackedDrawing.addRow(new Object[] { "charWidth(" + ch + ")", "", "Slow method, don't overuse", getStackTrace(new Throwable()), null }); } } static class Stats { private String name; private String type; private String uiid; private String parentName; private String imageName; private int invocationCount; private long fastest = Long.MAX_VALUE; private long slowest; private long[] allInvocations = new long[20]; public Stats(Component c) { name = c.getName(); type = c.getClass().getName(); uiid = c.getUIID(); if(c instanceof Label) { Image l = ((Label)c).getIcon(); if(l != null) { imageName = l.getImageName(); } } if(c.getParent() != null) { parentName = c.getParent().getName(); } } public void updateInvocation(long t) { fastest = Math.min(t, fastest); slowest = Math.max(t, slowest); if(allInvocations.length <= invocationCount) { long[] arr = new long[allInvocations.length * 3]; System.arraycopy(allInvocations, 0, arr, 0, allInvocations.length); allInvocations = arr; } allInvocations[invocationCount] = t; invocationCount++; } public long getAverage() { long total = 0; for(int iter = 0 ; iter < invocationCount ; iter++) { total += allInvocations[iter]; } return total / invocationCount; } /** * @return the name */ public String getName() { return name; } /** * @return the type */ public String getType() { return type; } /** * @return the parentName */ public String getParentName() { return parentName; } /** * @return the invocationCount */ public int getInvocationCount() { return invocationCount; } /** * @return the fastest */ public long getFastest() { return fastest; } /** * @return the slowest */ public long getSlowest() { return slowest; } /** * @return the allInvocations */ public long[] getAllInvocations() { return allInvocations; } /** * @return the uiid */ public String getUiid() { return uiid; } /** * @return the imageName */ public String getImageName() { return imageName; } } class Model implements TableModel { private List<TableModelListener> listeners = new ArrayList<TableModelListener>(); @Override public int getRowCount() { return componentStats.size(); } @Override public int getColumnCount() { return COLUMNS.length; } @Override public String getColumnName(int i) { return COLUMNS[i]; } @Override public Class<?> getColumnClass(int i) { return COLUMN_CLASSES[i]; } @Override public boolean isCellEditable(int i, int i1) { return false; } @Override public Object getValueAt(int row, int column) { Stats s = componentStats.get(row); switch(column) { case 0: return s.getName(); case 1: return s.getType(); case 2: return s.getUiid(); case 3: return s.getParentName(); case 4: return s.getImageName(); case 5: return s.getInvocationCount(); case 6: return s.getFastest(); case 7: return s.getSlowest(); default: return s.getAverage(); } } public void clear() { componentStats.clear(); componentId = 0; idString += "x"; fireUpdate(); } @Override public void setValueAt(Object o, int i, int i1) { } void fireUpdate() { for(TableModelListener t : listeners) { t.tableChanged(new TableModelEvent(this)); } } private boolean first; @Override public void addTableModelListener(TableModelListener tl) { listeners.add(tl); if(!first) { first = true; javax.swing.Timer t = new javax.swing.Timer(2000, new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { if(!paused) { fireUpdate(); } } }); t.setRepeats(true); t.start(); } } @Override public void removeTableModelListener(TableModelListener tl) { listeners.remove(tl); } } private static JTable createJTable() { final JTable t = new JTable(createTableModel()) { MultilineTableCell wordWrapRenderer = new MultilineTableCell(); public TableCellRenderer getCellRenderer(int row, int column) { if (column < 3 ) { return wordWrapRenderer; } else { return super.getCellRenderer(row, column); } } }; Action view = new AbstractAction() { public void actionPerformed(ActionEvent e) { JTable table = (JTable)e.getSource(); int modelRow = Integer.valueOf( e.getActionCommand() ); String s = (String)((DefaultTableModel)table.getModel()).getValueAt(modelRow, 3); JOptionPane.showMessageDialog(t, s, "Stack", JOptionPane.INFORMATION_MESSAGE); } }; ButtonColumn buttonColumn = new ButtonColumn(t, view, 3); buttonColumn.setMnemonic(KeyEvent.VK_D); return t; } public static class ButtonColumn extends AbstractCellEditor implements TableCellRenderer, TableCellEditor, ActionListener, MouseListener { private JTable table; private Action action; private int mnemonic; private Border originalBorder; private Border focusBorder; private JButton renderButton; private JButton editButton; private Object editorValue; private boolean isButtonColumnEditor; /** * Create the ButtonColumn to be used as a renderer and editor. The * renderer and editor will automatically be installed on the TableColumn * of the specified column. * * @param table the table containing the button renderer/editor * @param action the Action to be invoked when the button is invoked * @param column the column to which the button renderer/editor is added */ public ButtonColumn(JTable table, Action action, int column) { this.table = table; this.action = action; renderButton = new JButton(); editButton = new JButton(); editButton.setFocusPainted( false ); editButton.addActionListener( this ); originalBorder = editButton.getBorder(); setFocusBorder( new LineBorder(Color.BLUE) ); TableColumnModel columnModel = table.getColumnModel(); columnModel.getColumn(column).setCellRenderer( this ); columnModel.getColumn(column).setCellEditor( this ); table.addMouseListener( this ); table.setDefaultEditor(Action.class, this); table.setDefaultRenderer(Action.class, this); } /** * Get foreground color of the button when the cell has focus * * @return the foreground color */ public Border getFocusBorder() { return focusBorder; } /** * The foreground color of the button when the cell has focus * * @param focusBorder the foreground color */ public void setFocusBorder(Border focusBorder) { this.focusBorder = focusBorder; editButton.setBorder( focusBorder ); } public int getMnemonic() { return mnemonic; } /** * The mnemonic to activate the button when the cell has focus * * @param mnemonic the mnemonic */ public void setMnemonic(int mnemonic) { this.mnemonic = mnemonic; renderButton.setMnemonic(mnemonic); editButton.setMnemonic(mnemonic); } @Override public java.awt.Component getTableCellEditorComponent( JTable table, Object value, boolean isSelected, int row, int column) { if (value == null) { editButton.setText( "" ); editButton.setIcon( null ); } else if (value instanceof Icon) { editButton.setText( "" ); editButton.setIcon( (Icon)value ); } else { editButton.setText( value.toString() ); editButton.setIcon( null ); } this.editorValue = value; return editButton; } @Override public Object getCellEditorValue() { return editorValue; } // // Implement TableCellRenderer interface // public java.awt.Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (isSelected) { renderButton.setForeground(table.getSelectionForeground()); renderButton.setBackground(table.getSelectionBackground()); } else { renderButton.setForeground(table.getForeground()); renderButton.setBackground(UIManager.getColor("Button.background")); } if (hasFocus) { renderButton.setBorder( focusBorder ); } else { renderButton.setBorder( originalBorder ); } // renderButton.setText( (value == null) ? "" : value.toString() ); if (value == null) { renderButton.setText( "" ); renderButton.setIcon( null ); } else if (value instanceof Icon) { renderButton.setText( "" ); renderButton.setIcon( (Icon)value ); } else { renderButton.setText( value.toString() ); renderButton.setIcon( null ); } return renderButton; } // // Implement ActionListener interface // /* * The button has been pressed. Stop editing and invoke the custom Action */ public void actionPerformed(ActionEvent e) { int row = table.convertRowIndexToModel( table.getEditingRow() ); fireEditingStopped(); // Invoke the Action ActionEvent event = new ActionEvent( table, ActionEvent.ACTION_PERFORMED, "" + row); action.actionPerformed(event); } // // Implement MouseListener interface // /* * When the mouse is pressed the editor is invoked. If you then then drag * the mouse to another cell before releasing it, the editor is still * active. Make sure editing is stopped when the mouse is released. */ public void mousePressed(MouseEvent e) { if (table.isEditing() && table.getCellEditor() == this) isButtonColumnEditor = true; } public void mouseReleased(MouseEvent e) { if (isButtonColumnEditor && table.isEditing()) table.getCellEditor().stopCellEditing(); isButtonColumnEditor = false; } public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} } static class MultilineTableCell implements TableCellRenderer { class CellArea extends DefaultTableCellRenderer { /** * */ private static final long serialVersionUID = 1L; private String text; protected int rowIndex; protected int columnIndex; protected JTable table; protected java.awt.Font font; private int paragraphStart,paragraphEnd; private LineBreakMeasurer lineMeasurer; public CellArea(String s, JTable tab, int row, int column,boolean isSelected) { text = s; rowIndex = row; columnIndex = column; table = tab; font = table.getFont(); if (isSelected) { setForeground(table.getSelectionForeground()); setBackground(table.getSelectionBackground()); } } public void paintComponent(java.awt.Graphics gr) { super.paintComponent(gr); if ( text != null && !text.isEmpty() ) { Graphics2D g = (Graphics2D) gr; if (lineMeasurer == null) { AttributedCharacterIterator paragraph = new AttributedString(text).getIterator(); paragraphStart = paragraph.getBeginIndex(); paragraphEnd = paragraph.getEndIndex(); FontRenderContext frc = g.getFontRenderContext(); lineMeasurer = new LineBreakMeasurer(paragraph,BreakIterator.getWordInstance(), frc); } float breakWidth = (float)table.getColumnModel().getColumn(columnIndex).getWidth(); float drawPosY = 0; // Set position to the index of the first character in the paragraph. lineMeasurer.setPosition(paragraphStart); // Get lines until the entire paragraph has been displayed. while (lineMeasurer.getPosition() < paragraphEnd) { // Retrieve next layout. A cleverer program would also cache // these layouts until the component is re-sized. TextLayout layout = lineMeasurer.nextLayout(breakWidth); // Compute pen x position. If the paragraph is right-to-left we // will align the TextLayouts to the right edge of the panel. // Note: this won't occur for the English text in this sample. // Note: drawPosX is always where the LEFT of the text is placed. float drawPosX = layout.isLeftToRight() ? 0 : breakWidth - layout.getAdvance(); // Move y-coordinate by the ascent of the layout. drawPosY += layout.getAscent(); // Draw the TextLayout at (drawPosX, drawPosY). layout.draw(g, drawPosX, drawPosY); // Move y-coordinate in preparation for next layout. drawPosY += layout.getDescent() + layout.getLeading(); } table.setRowHeight(rowIndex,(int) drawPosY); } } } public java.awt.Component getTableCellRendererComponent(JTable table, Object value,boolean isSelected, boolean hasFocus, int row,int column ) { if(value == null) { value = ""; } CellArea area = new CellArea(value.toString(),table,row,column,isSelected); return area; } } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton clearData; private javax.swing.JTree componentHierarchy; private javax.swing.JLabel imageMemory; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel2; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane2; private javax.swing.JScrollPane jScrollPane3; private javax.swing.JScrollPane jScrollPane4; private javax.swing.JScrollPane jScrollPane5; private javax.swing.JSplitPane jSplitPane1; private javax.swing.JTabbedPane jTabbedPane1; private javax.swing.JTable jTable1; private javax.swing.JToolBar jToolBar1; private javax.swing.JButton pauseContinue; private javax.swing.JTextArea performanceLog; private javax.swing.JButton refreshFrame; private javax.swing.JTable renderedItems; private javax.swing.JTable resultData; private javax.swing.JButton runGC; // End of variables declaration//GEN-END:variables }