001: package tijmp.ui;
002:
003: import java.awt.BorderLayout;
004: import java.awt.Dimension;
005: import java.awt.FlowLayout;
006: import java.awt.event.ActionEvent;
007: import java.awt.event.ActionListener;
008: import java.awt.event.MouseAdapter;
009: import java.awt.event.MouseEvent;
010: import java.awt.event.WindowAdapter;
011: import java.awt.event.WindowEvent;
012: import java.util.ArrayList;
013: import java.util.List;
014: import javax.swing.JFrame;
015: import javax.swing.JLabel;
016: import javax.swing.JMenu;
017: import javax.swing.JMenuItem;
018: import javax.swing.JPanel;
019: import javax.swing.JPopupMenu;
020: import javax.swing.JScrollPane;
021: import javax.swing.JTable;
022: import javax.swing.JTextField;
023: import javax.swing.ListSelectionModel;
024: import javax.swing.SwingUtilities;
025: import javax.swing.table.AbstractTableModel;
026: import javax.swing.table.TableColumn;
027: import tijmp.HeapWalkEntry;
028: import tijmp.ProfilerHandler;
029: import tijmp.actions.DefaultFilter;
030: import tijmp.actions.FilterOutClass;
031: import tijmp.actions.FilterOutPackage;
032: import tijmp.actions.InspectClass;
033: import tijmp.actions.OnlyPackage;
034: import tijmp.actions.ShowAllInstances;
035: import tijmp.actions.ShowAllStrings;
036: import tijmp.actions.ShowOwners;
037: import tijmp.actions.StrictPackage;
038: import tijmp.filter.Filter;
039: import tijmp.filter.RegexpFilter;
040:
041: /** A class that show a table with heap walk statistics.
042: */
043: class HeapWalkTable implements FilterListener {
044: private ATM m;
045: private List<HeapWalkEntry> ls;
046: private ProfilerHandler ph;
047: private FilterConfig fc;
048:
049: private static int windowId = 1;
050:
051: public HeapWalkTable(List<HeapWalkEntry> ls, ProfilerHandler ph,
052: FilterConfig fc) {
053: this .ls = ls;
054: this .ph = ph;
055: this .fc = fc;
056: }
057:
058: public void showFrame() {
059: m = new ATM(ls, fc.getFilter());
060: JTable table = new JTable(m);
061: table.setAutoCreateRowSorter(true);
062: JScrollPane scrollPane = new JScrollPane(table);
063: table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
064: table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
065: TableColumn column = null;
066: ReadableSizeRenderer r = new ReadableSizeRenderer();
067: for (int i = 0; i < m.getColumnCount(); i++) {
068: column = table.getColumnModel().getColumn(i);
069: if (i == ATM.COL_NAME)
070: column.setPreferredWidth(350);
071: else
072: column.setPreferredWidth(75);
073: if (i == ATM.COL_SIZE || i == ATM.COL_SIZE_DIFF)
074: column.setCellRenderer(r);
075: }
076: table.setDefaultRenderer(Class.class, new ClassRenderer());
077: table.removeColumn(table.getColumnModel()
078: .getColumn(ATM.COL_HWE));
079: table
080: .setPreferredScrollableViewportSize(new Dimension(750,
081: 200));
082: table.addMouseListener(new MouseHandler(table, ph, fc));
083: table.getRowSorter().toggleSortOrder(ATM.COL_SIZE);
084: table.getRowSorter().toggleSortOrder(ATM.COL_SIZE);
085:
086: JFrame f = new JFrame("Heap Walk Result " + windowId++);
087: JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
088: JLabel l = new JLabel("Set filter (regexp matching)");
089: p.add(l);
090: JTextField tf = new JTextField(30);
091: p.add(tf);
092: tf.addActionListener(new SetRegexpFilter(tf, fc));
093:
094: f.add(p, BorderLayout.NORTH);
095: f.add(scrollPane, BorderLayout.CENTER);
096: f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
097: fc.addFilterListener(this );
098: f.addWindowListener(new WindowAdapter() {
099: public void windowClosed(WindowEvent e) {
100: fc.removeFilterListener(HeapWalkTable.this );
101: }
102: });
103: f.pack();
104: f.setVisible(true);
105: }
106:
107: public void filterChanged(Filter filter) {
108: m.setNewFilter(filter);
109: }
110: }
111:
112: class SetRegexpFilter implements ActionListener {
113: private JTextField f;
114: private FilterConfig fc;
115:
116: public SetRegexpFilter(JTextField f, FilterConfig fc) {
117: this .f = f;
118: this .fc = fc;
119: }
120:
121: public void actionPerformed(ActionEvent e) {
122: fc.setFilter(new RegexpFilter(f.getText()));
123: }
124: }
125:
126: class MouseHandler extends MouseAdapter {
127: private JTable table;
128: private ProfilerHandler ph;
129: private FilterConfig fc;
130:
131: public MouseHandler(JTable table, ProfilerHandler ph,
132: FilterConfig fc) {
133: this .table = table;
134: this .ph = ph;
135: this .fc = fc;
136: }
137:
138: public void mousePressed(MouseEvent e) {
139: if (!SwingUtilities.isRightMouseButton(e))
140: return;
141: int viewRow = table.rowAtPoint(e.getPoint());
142: int modelRow = table.convertRowIndexToModel(viewRow);
143: Class<?> c = (Class<?>) table.getModel().getValueAt(modelRow,
144: ATM.COL_NAME);
145: String name = Translator.translate(c);
146: table.getSelectionModel()
147: .setSelectionInterval(viewRow, viewRow);
148: String[] packages = name.split("\\.");
149: JPopupMenu m = new JPopupMenu();
150: for (int i = 0; i < packages.length - 1; i++) {
151: String packageName = getName(packages, 0, i);
152: JMenu menu = new JMenu(packageName);
153: JMenuItem mi = new JMenuItem(new FilterOutPackage(fc,
154: packageName));
155: menu.add(mi);
156: mi = new JMenuItem(new OnlyPackage(fc, packageName));
157: menu.add(mi);
158: mi = new JMenuItem(new StrictPackage(fc, packageName));
159: menu.add(mi);
160: m.add(menu);
161: }
162: HeapWalkEntry hwe = (HeapWalkEntry) table.getModel()
163: .getValueAt(modelRow, ATM.COL_HWE);
164: Class<?> clz = hwe.getEntryClass();
165: m.add(new JMenuItem(new FilterOutClass(fc, clz)));
166: m.add(new JMenuItem(new DefaultFilter(fc)));
167: m.addSeparator();
168: m.add(new JMenuItem(new ShowAllInstances(ph, clz)));
169: if (c == String.class || c == char[].class)
170: m.add(new JMenuItem(new ShowAllStrings(ph)));
171: m.add(new JMenuItem(new ShowOwners(ph, clz)));
172: m.addSeparator();
173: m.add(new JMenuItem(new InspectClass(clz)));
174: m.show(e.getComponent(), e.getX(), e.getY());
175: }
176:
177: private String getName(String[] p, int start, int end) {
178: if (start == end)
179: return p[start];
180: StringBuilder sb = new StringBuilder();
181: for (int i = start; i <= end; i++) {
182: if (i > start)
183: sb.append('.');
184: sb.append(p[i]);
185: }
186: return sb.toString();
187: }
188: }
189:
190: class ATM extends AbstractTableModel {
191: private List<HeapWalkEntry> orig;
192: private List<HeapWalkEntry> ls;
193:
194: private String[] columnNames = { "HWE", "Class", "Count",
195: "Count diff", "Size", "Size diff" };
196:
197: public static final int COL_HWE = 0;
198: public static final int COL_NAME = COL_HWE + 1;
199: public static final int COL_COUNT = COL_NAME + 1;
200: public static final int COL_COUNT_DIFF = COL_COUNT + 1;
201: public static final int COL_SIZE = COL_COUNT_DIFF + 1;
202: public static final int COL_SIZE_DIFF = COL_SIZE + 1;
203:
204: public ATM(List<HeapWalkEntry> ls, Filter filter) {
205: orig = ls;
206: setNewFilter(filter);
207: }
208:
209: public void setNewFilter(Filter filter) {
210: ls = new ArrayList<HeapWalkEntry>(orig.size());
211: for (HeapWalkEntry e : orig)
212: if (filter.accept(e.getEntryClass()))
213: ls.add(e);
214: fireTableDataChanged();
215: }
216:
217: public String getColumnName(int col) {
218: return columnNames[col];
219: }
220:
221: public int getRowCount() {
222: return ls.size();
223: }
224:
225: public int getColumnCount() {
226: return columnNames.length;
227: }
228:
229: public Object getValueAt(int row, int col) {
230: HeapWalkEntry hwe = ls.get(row);
231: switch (col) {
232: case COL_HWE:
233: return hwe;
234: case COL_NAME:
235: return hwe.getEntryClass();
236: case COL_COUNT:
237: return hwe.getInstanceCount();
238: case COL_COUNT_DIFF:
239: return hwe.getInstanceChange();
240: case COL_SIZE:
241: return hwe.getTotalSize();
242: case COL_SIZE_DIFF:
243: return hwe.getSizeChange();
244: default:
245: throw new IllegalArgumentException(
246: "do not know how to handle col: " + col);
247: }
248: }
249:
250: public Class<?> getColumnClass(int col) {
251: switch (col) {
252: case COL_HWE:
253: return HeapWalkEntry.class;
254: case COL_NAME:
255: return Class.class;
256: case COL_COUNT:
257: return Long.class;
258: case COL_COUNT_DIFF:
259: return Long.class;
260: case COL_SIZE:
261: return Long.class;
262: case COL_SIZE_DIFF:
263: return Long.class;
264: default:
265: throw new IllegalArgumentException(
266: "do not know how to handle col: " + col);
267: }
268: }
269:
270: public boolean isCellEditable(int row, int col) {
271: return false;
272: }
273:
274: public void setValueAt(Object value, int row, int col) {
275: throw new IllegalStateException("non editable table");
276: }
277: }
|