001: package tide.execute.stacktracebrowser;
002:
003: import snow.lookandfeel.ThemesManager;
004: import snow.utils.gui.GUIUtils;
005: import snow.utils.gui.ProgressModalDialog;
006: import java.awt.Component;
007: import java.awt.EventQueue;
008: import javax.swing.border.EmptyBorder;
009: import tide.editor.OutputPanels;
010: import tide.sources.FileItem;
011: import java.util.*;
012: import javax.swing.tree.*;
013: import snow.utils.gui.CloseControlPanel;
014: import snow.texteditor.DocumentUtils;
015: import tide.editor.MainEditorFrame;
016: import snow.texteditor.SimpleDocument;
017: import javax.swing.*;
018: import javax.swing.event.*;
019: import java.awt.BorderLayout;
020: import java.awt.event.*;
021:
022: /** The stack must contain only the CTRL + SHIFT + F1 results.
023: */
024: public final class SwingTreeDebugBrowser extends JDialog {
025: StackTraceItem root = new StackTraceItem("", "");
026: DefaultTreeModel treeModel = new DefaultTreeModel(root);
027: final private JTree stackTree = new JTree(treeModel);
028:
029: // on flow tree, stores the representation as key.
030: Map<String, StackTraceItem> items = new HashMap<String, StackTraceItem>();
031: JTextArea status = new JTextArea(
032: "... details for selected node ...", 4, 120);
033: JLabel warnLabel = new JLabel();
034:
035: final private static String titleBase = "Swing debug tree";
036: Renderer renderer = new Renderer();
037:
038: private boolean closed = false;
039:
040: public SwingTreeDebugBrowser(final SimpleDocument doc) {
041: super (MainEditorFrame.instance, titleBase, false); //not modal
042: MainEditorFrame.instance.globalProperties
043: .setComponentSizeFromINIFile(this ,
044: "SwingTreeDebugBrowser", 400, 500, 10, 10);
045:
046: add(new JScrollPane(stackTree), BorderLayout.CENTER);
047: stackTree.setRootVisible(false);
048: this .stackTree.setCellRenderer(renderer);
049:
050: final CloseControlPanel ccp = new CloseControlPanel(this ,
051: false, false, "Close");
052: add(ccp, BorderLayout.SOUTH);
053:
054: JPanel controlPanel = new JPanel(new BorderLayout());
055: JComponent descr = GUIUtils
056: .createReadOnlyDescriptionArea("This dislays the tree of the swing tree parsed from the execution pane"
057: + "\nPress Ctrl+Shift+F1 in the focused window you want to analyse to"
058: + "\nproduce such a dump in the console. You can of course paste one from another application.");
059: controlPanel.add(descr, BorderLayout.CENTER);
060: controlPanel.add(warnLabel, BorderLayout.SOUTH);
061:
062: JPanel northPanel = new JPanel(new BorderLayout());
063: northPanel.add(new JScrollPane(status), BorderLayout.SOUTH);
064:
065: northPanel.add(controlPanel, BorderLayout.NORTH);
066:
067: add(northPanel, BorderLayout.NORTH);
068: status.setBorder(new EmptyBorder(3, 5, 3, 0));
069: status.setEditable(false);
070:
071: this .addWindowListener(new WindowAdapter() {
072: @Override
073: public void windowClosing(WindowEvent we) {
074: onClose();
075: }
076:
077: @Override
078: public void windowClosed(WindowEvent we) {
079: onClose();
080: }
081: });
082:
083: parse(doc);
084:
085: stackTree.addMouseListener(new MouseAdapter() {
086: @Override
087: public void mousePressed(MouseEvent me) {
088: if (stackTree.getSelectionPath() != null) {
089: viewDetailsForSelected((StackTraceItem) stackTree
090: .getSelectionPath().getLastPathComponent());
091: }
092: }
093: /*@Override public void mouseReleased(MouseEvent me)
094: {
095: }*/
096: });
097:
098: ActionListener upda = new ActionListener() {
099: public void actionPerformed(ActionEvent ae) {
100: parse(doc);
101: }
102: };
103:
104: setVisible(true); // NOT modal
105: }
106:
107: /** On selected element.
108: */
109: private void viewDetailsForSelected(StackTraceItem it) {
110: status.setText("" + it.getUserObject());
111: status.setCaretPosition(0);
112:
113: FileItem fi = OutputPanels
114: .recognizeStackTraceLine_AndSelectInEditorIfFound("\t at "
115: + it.stringRepresentation + ":");
116: if (fi == null) {
117: //status.setText("CANNOT FIND: "+it.getUserObject());
118: }
119: }
120:
121: private void parse(final SimpleDocument doc) {
122: // clear
123: items.clear();
124: this .removeRecurse(root);
125: final ProgressModalDialog pmd = new ProgressModalDialog(this ,
126: "Parsing stacktraces", true);
127:
128: // progress ?
129: Thread t = new Thread() {
130: public void run() {
131: try {
132: pmd.start();
133: _parse(doc, pmd);
134: } finally {
135: pmd.closeDialog();
136: }
137: }
138: };
139: t.start();
140:
141: }
142:
143: /** we build the complete tree offline and at end call reload...
144: */
145: private void _parse(SimpleDocument doc, ProgressModalDialog pmd) {
146: int stacktracesFound = 0;
147:
148: int lines = DocumentUtils.getLineNumber(doc, doc.getLength());
149: pmd.setProgressBounds(lines);
150:
151: String lastNotStack = "?";
152: StackTraceItem previousItem = root;
153: int lastDepth = 0;
154: int awtElts = 0;
155:
156: for (int i = 0; i < lines; i++) {
157: pmd.incrementProgress(1);
158: stacktracesFound++;
159: String line = DocumentUtils.getTextOfLine(doc, i);
160:
161: if (line.trim().length() == 0)
162: continue;
163:
164: String beginning = null;
165: int posB = line.indexOf("> ");
166: if (posB >= 0) {
167: beginning = line.substring(0, posB + 2);
168: } else {
169: beginning = ""; // may occur if the trace is pasted from outside.
170: }
171:
172: line = line.substring(beginning.length());
173: final String fullLine = line.trim();
174:
175: if (line.trim().length() == 0)
176: continue;
177:
178: // count the number of spaces
179: int depth = 0;
180: for (int ci = 0; ci < line.length(); ci++) {
181: if (line.charAt(ci) == ' ')
182: depth++;
183: else
184: break;
185: }
186:
187: line = line.substring(depth);
188:
189: if (depth < lastDepth) {
190: int n = lastDepth - depth;
191: for (int j = 0; j < n; j++) {
192: StackTraceItem par = (StackTraceItem) previousItem
193: .getParent();
194: if (par != null) {
195: previousItem = par;
196: } else {
197: System.out
198: .println("SwingTreeDebugBrowser: problem at line "
199: + i
200: + ": depth="
201: + depth
202: + ", last=" + lastDepth);
203: }
204: }
205: }
206:
207: int pos = line.indexOf('[');
208: if (pos > 0)
209: line = line.substring(0, pos);
210: StackTraceItem actual = new StackTraceItem(fullLine, line);
211: if (actual.isAWT)
212: awtElts++;
213: insert(actual, previousItem);
214:
215: previousItem = actual;
216: lastDepth = depth + 1; // yes
217:
218: } // for lines
219:
220: final int fstacktracesFound = stacktracesFound;
221: final int fawtElts = awtElts;
222: EventQueue.invokeLater(new Runnable() {
223: public void run() {
224: setTitle(titleBase + " [" + fstacktracesFound
225: + " elements], " + fawtElts + " awt elements");
226: treeModel.reload();
227: expandRecurse(root);
228:
229: if (fawtElts > 0) {
230: warnLabel
231: .setText(" Don't mix awt and swing components");
232: warnLabel.setForeground(ThemesManager.getInstance()
233: .getRed());
234: }
235: }
236: });
237: }
238:
239: private void insert(final DefaultMutableTreeNode node,
240: final DefaultMutableTreeNode parent) {
241: if (parent == null) {
242: System.out.println("###Null parent to add " + node);
243: return;
244: }
245: if (node == null) {
246: System.out.println("###Null node to insert in " + parent);
247: return;
248: }
249:
250: parent.add(node);
251: }
252:
253: private void removeRecurse(StackTraceItem it) {
254: if (it.getParent() != null) {
255: this .treeModel.removeNodeFromParent(it);
256: }
257: //it.removeFromParent();
258: while (it.getChildCount() > 0) {
259: removeRecurse(it.getChildItemAt(0));
260: }
261: }
262:
263: private void expandRecurse(StackTraceItem it) {
264: if (it.isLeaf()) {
265: stackTree.makeVisible(new TreePath(it.getPath()));
266: }
267:
268: for (int i = 0; i < it.getChildCount(); i++) {
269: expandRecurse(it.getChildItemAt(i));
270: }
271: }
272:
273: public void onClose() {
274: if (!closed) {
275: MainEditorFrame.instance.globalProperties
276: .saveComponentSizeInINIFile(this ,
277: "SwingTreeDebugBrowser");
278: }
279:
280: items.clear();
281: removeRecurse(root);
282:
283: closed = true;
284: }
285:
286: static class StackTraceItem extends DefaultMutableTreeNode {
287: /*int exceptionCount = 0;
288: int errorCount = 0;
289: int throwableCount = 0;*/
290:
291: String stringRepresentation;
292: //int line = -1;
293: //String fileSimpleName = "??";
294: //int visits = 1;
295: String structTreeName;
296:
297: public boolean isAWT = false;
298:
299: /** For intermediate packages AND ROOTS.
300: */
301: public StackTraceItem(String path, String intermediateSimpleName) {
302: super (path);
303: stringRepresentation = intermediateSimpleName;
304: structTreeName = path;
305: isAWT = intermediateSimpleName.startsWith("java.awt.");
306: }
307:
308: public StackTraceItem getChildItemAt(int i) {
309: return (StackTraceItem) super .getChildAt(i);
310: }
311:
312: public StackTraceItem getChildNamed(String simpleName) {
313: for (int i = 0; i < getChildCount(); i++) {
314: if (getChildItemAt(i).stringRepresentation
315: .equals(simpleName))
316: return getChildItemAt(i);
317: }
318: return null;
319: }
320:
321: @Override
322: public final String toString() {
323: return stringRepresentation;
324: }
325: }
326:
327: static class Renderer extends DefaultTreeCellRenderer {
328: @Override
329: public Component getTreeCellRendererComponent(JTree tree,
330: Object value, boolean selected, boolean expanded,
331: boolean leaf, int row, boolean hasFocus) {
332: StackTraceItem sti = (StackTraceItem) value;
333: JLabel comp = (JLabel) super.getTreeCellRendererComponent(
334: tree, value, selected, expanded, leaf, row,
335: hasFocus);
336: if (sti.isAWT) {
337:
338: comp
339: .setForeground(ThemesManager.getInstance()
340: .getRed());
341: }
342:
343: return comp;
344: }
345:
346: }
347:
348: }
|