1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
|
/*
Wotonomy: OpenStep design patterns for pure Java applications.
Copyright (C) 2000 Michael Powers
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, see http://www.gnu.org
*/
package net.wotonomy.ui.swing.util;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
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.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.border.EmptyBorder;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
import net.wotonomy.ui.swing.components.MultiLineLabel;
/**
* The StackTraceInspector displays a JFrame containing stack trace information
* for a Throwable. <br>
* <br>
*
* There are also a few static methods for obtaining information about the
* current stack, which is useful for determining who's calling you at runtime.
*
* @author michael@mpowers.net
* @version $Revision: 904 $
*/
public class StackTraceInspector implements TableModel, MouseListener, ActionListener {
protected JTable table = null;
protected List tableModelListeners = null;
protected List methodNames = new ArrayList();
// key command to copy contents to clipboard
static public final String COPY = "COPY";
/**
* Displays the current stack trace at the time of instantiation in a table on a
* frame.
*/
public StackTraceInspector() {
initLayout(parseStackTrace(new RuntimeException()), null);
}
/**
* Displays the current stack trace at the time of instantiation in a table on a
* frame annotated with the specified message.
*/
public StackTraceInspector(String aMessage) {
initLayout(parseStackTrace(new RuntimeException()), aMessage);
}
/**
* Displays the stack trace for the given throwable in a table on a frame.
*
* @param aThrowable A Throwable whose stack will be examined.
*/
public StackTraceInspector(Throwable aThrowable) {
initLayout(parseStackTrace(aThrowable), aThrowable.getClass() + ": " + aThrowable.getMessage());
}
/**
* Simply displays the list items in a dialog. Presumably (but not necessarily)
* called from the other constructors. (I guess if you just want a frame with
* strings in table, you can call this.)
*
* @param aStringList A List containing Strings.
*/
public StackTraceInspector(List aStringList) {
initLayout(aStringList, null);
}
protected void initLayout(List items, String message) {
methodNames = new ArrayList(items);
table = new JTable(this); // this class is the table model
table.addMouseListener(this); // listen for double-clicks
// set up keyboard events for cut-copy: Ctrl-C, Ctrl-X
table.registerKeyboardAction(this, COPY,
KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
table.registerKeyboardAction(this, COPY,
KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
JPanel panel = new JPanel();
panel.setBorder(new EmptyBorder(new Insets(10, 10, 10, 10)));
panel.setLayout(new BorderLayout(10, 10));
if (message != null) {
panel.add(new MultiLineLabel(message), BorderLayout.NORTH);
}
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setPreferredSize(new Dimension(325, 350));
panel.add(scrollPane, BorderLayout.CENTER);
JFrame window = new JFrame();
window.setTitle("Stack Trace Inspector");
window.getContentPane().add(panel);
window.pack();
WindowUtilities.cascade(window);
window.show();
}
// interface TableModel
public int getRowCount() {
return methodNames.size();
}
public int getColumnCount() {
return 1;
}
public String getColumnName(int columnIndex) {
switch (columnIndex) {
case 0:
return "Methods";
case 1:
return "Property";
}
System.out.println("StackTraceInspector.getColumnName: unknown column: " + columnIndex);
return "";
}
public Class getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0:
return String.class;
case 1:
return String.class;
}
System.out.println("StackTraceInspector.getColumnClass: unknown column: " + columnIndex);
return Object.class;
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return methodNames.get(rowIndex);
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
}
public void addTableModelListener(TableModelListener l) {
if (tableModelListeners == null) {
tableModelListeners = new ArrayList();
}
tableModelListeners.add(l);
}
public void removeTableModelListener(TableModelListener l) {
if (tableModelListeners != null) {
tableModelListeners.remove(l);
}
}
// interface MouseListener
/**
* Double click to call invokeFileFromString.
*/
public void mouseClicked(MouseEvent e) {
if (e.getSource() == table) {
if (e.getClickCount() > 1) {
int row = table.rowAtPoint(e.getPoint());
int col = table.columnAtPoint(e.getPoint());
if ((row == -1) || (col != 0))
return;
invokeFileFromString(methodNames.get(row).toString());
}
}
}
public void mouseReleased(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
// interface ActionEventListener - for listening to key commands
public void actionPerformed(ActionEvent evt) {
if (COPY.equals(evt.getActionCommand())) {
copyToClipboard();
return;
}
}
/**
* Copies the contents of the table to the clipboard as a tab-delimited string.
*/
public void copyToClipboard() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Clipboard clipboard = toolkit.getSystemClipboard();
StringSelection selection = new StringSelection(getSelectedStackString());
clipboard.setContents(selection, selection);
}
/**
* Converts the selected contents of the table to a string.
*
* @return A String containing the text contents of the table.
*/
public String getSelectedStackString() {
StringBuffer result = new StringBuffer(64);
TableModel model = table.getModel();
Object o;
int[] selectedRows = table.getSelectedRows();
for (int i = 0; i < selectedRows.length; i++) {
o = model.getValueAt(selectedRows[i], 0);
if (o == null)
o = "";
result.append(o);
result.append('\n');
}
return result.toString();
}
// static methods
/**
* Obtains a list of strings representing the stack trace associated with this
* throwable starting with the most recent call.
*
* @param aThrowable A Throwable whose stack trace is parsed.
* @return a List containing the method names as Strings.
*/
static public List parseStackTrace(Throwable aThrowable) {
String trace = null;
// create new stream
ByteArrayOutputStream os = new ByteArrayOutputStream(256);
PrintStream newErr = new PrintStream(os);
aThrowable.printStackTrace(newErr); // prints to System.err
// convert to string
trace = os.toString();
List result = new ArrayList();
// populate list with parsed trace, starting from top
String token;
StringTokenizer tokens = new StringTokenizer(trace, "\n");
tokens.nextToken(); // strip off description of throwable
while (tokens.hasMoreTokens()) {
token = tokens.nextToken();
if (token.indexOf(StackTraceInspector.class.getName()) == -1) { // add only those methods not from this
// class
// strip whitespace, "at " from front, and \r from end
token.trim();
token = token.substring(4, token.length() - 1);
result.add(token);
}
}
return result;
}
/**
* Convenience method that obtains a String representing the caller's caller.
*
* @return a String representing a method in stack trace format.
*/
static public String getMyCaller() {
List trace = parseStackTrace(new RuntimeException());
if (trace.size() > 1) {
return trace.get(1).toString();
}
return null;
}
/**
* Prints a stack trace up to the first method whose fully qualified class name
* begins with "java" to System.out.
*/
static public void printShortStackTrace() {
String s;
Iterator i = parseStackTrace(new RuntimeException()).iterator();
while (i.hasNext()) {
System.out.println(" " + (s = i.next().toString()));
if (s.startsWith("java"))
break;
}
}
protected void invokeFileFromString(String aString) {
// strip off parentheses, if any
int openParam = aString.indexOf("(");
if (openParam != -1) {
aString = aString.substring(0, openParam);
}
// separate class name from method name
int lastDot = aString.lastIndexOf(".");
if (lastDot == -1)
return;
String className = aString.substring(0, lastDot);
String methodName = aString.substring(lastDot + 1);
// convert "."s to file separator characters
StringBuffer buf = new StringBuffer();
StringTokenizer tokens = new StringTokenizer(className, ".");
while (true) {
buf.append(tokens.nextToken());
if (!tokens.hasMoreTokens())
break;
buf.append(File.separator);
}
String path = buf.toString();
java.net.URL url = ClassLoader.getSystemResource(path + ".java");
if (url == null)
return; // do nothing
String name = url.getFile();
// try to launch the document
try {
// NOTE: This is Windows-dependent!
String args[] = new String[] { "cmd", "/c", "\"start \"\" \"" + name.substring(1) + "\"\"" };
// this translates to: cmd /c "start "" "path""
// apparently an array is more reliable for calling exec().
// all the extra quotes are to handle paths with spaces.
// trims off the first "/" before the drive letter.
// needed a dummy title for the console or it wouldn't work.
Runtime.getRuntime().exec(args);
} catch (Exception exc) {
System.out.println("DocumentLinkPanel.invokeDocument: " + exc);
}
return;
}
}
/*
* $Log$ Revision 1.2 2006/02/18 23:19:05 cgruber Update imports and maven
* dependencies.
*
* Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in
* eclipse-friendly maven-enabled packages.
*
* Revision 1.5 2003/08/06 23:07:53 chochos general code cleanup (mostly,
* removing unused imports)
*
* Revision 1.4 2002/11/16 16:33:31 mpowers Now using platform-specific
* accelerator key for shortcuts.
*
* Revision 1.3 2001/07/18 21:53:33 mpowers Added a string argument for display
* as a message.
*
* Revision 1.2 2001/07/17 14:01:43 mpowers Added short stack trace method.
*
* Revision 1.1.1.1 2000/12/21 15:51:34 mpowers Contributing wotonomy.
*
* Revision 1.5 2000/12/20 16:25:45 michael Added log to all files.
*
*
*/
|