001: package tide.execute.stacktracebrowser;
002:
003: import java.awt.Component;
004: import snow.utils.gui.ProgressModalDialog;
005: import tide.sources.LibFileItem;
006: import java.awt.FlowLayout;
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 snow.utils.StringUtils;
013: import javax.swing.tree.*;
014: import snow.utils.gui.CloseControlPanel;
015: import snow.texteditor.DocumentUtils;
016: import tide.editor.MainEditorFrame;
017: import snow.texteditor.SimpleDocument;
018: import javax.swing.*;
019: import javax.swing.event.*;
020: import java.awt.BorderLayout;
021: import java.awt.event.*;
022:
023: /** TODO: search, filter.
024: * Represent according: types view (structure), flow view (time)
025: */
026: public final class StacktraceBrowser extends JDialog {
027: JComboBox viewType = new JComboBox(
028: new String[] { "Flow", "Syntax" });
029: JCheckBox onlyProjectCB = new JCheckBox("View only project items",
030: false);
031: JCheckBox groupBranchsCB = new JCheckBox("Group branchs", true);
032: JCheckBox includeThrowableCB = new JCheckBox("Include Throwables",
033: true); // throwables are not exceptions
034:
035: StackTraceItem root = new StackTraceItem("");
036: DefaultTreeModel treeModel = new DefaultTreeModel(root);
037: final private JTree stackTree = new JTree(treeModel);
038:
039: // on flow tree, stores the representation as key.
040: Map<String, StackTraceItem> items = new HashMap<String, StackTraceItem>();
041: JLabel status = new JLabel();
042:
043: final private static String titleBase = "Stacktraces found";
044: Renderer renderer = new Renderer();
045:
046: public StacktraceBrowser(final SimpleDocument doc) {
047: super (MainEditorFrame.instance, titleBase, false); //not modal
048: MainEditorFrame.instance.globalProperties
049: .setComponentSizeFromINIFile(this , "StacktraceBrowser",
050: 550, 500, 10, 10);
051:
052: add(new JScrollPane(stackTree), BorderLayout.CENTER);
053: stackTree.setRootVisible(false);
054: this .stackTree.setCellRenderer(renderer);
055:
056: CloseControlPanel ccp = new CloseControlPanel(this , false,
057: false, "Close");
058: add(ccp, BorderLayout.SOUTH);
059:
060: JPanel controlPanel = new JPanel(new FlowLayout(
061: FlowLayout.LEFT, 5, 3));
062:
063: controlPanel.add(new JLabel("View "));
064: controlPanel.add(viewType);
065: controlPanel.add(onlyProjectCB);
066: controlPanel.add(groupBranchsCB);
067: controlPanel.add(includeThrowableCB);
068:
069: JPanel northPanel = new JPanel(new BorderLayout());
070: northPanel.add(status, BorderLayout.SOUTH);
071: northPanel.add(controlPanel, BorderLayout.NORTH);
072:
073: add(northPanel, BorderLayout.NORTH);
074: status.setBorder(new EmptyBorder(3, 5, 3, 0));
075:
076: this .addWindowListener(new WindowAdapter() {
077: @Override
078: public void windowClosing(WindowEvent we) {
079: onClose();
080: }
081:
082: @Override
083: public void windowClosed(WindowEvent we) {
084: onClose();
085: }
086: });
087:
088: parse(doc);
089:
090: stackTree.addMouseListener(new MouseAdapter() {
091: @Override
092: public void mousePressed(MouseEvent me) {
093: if (stackTree.getSelectionPath() != null) {
094: jumpToSelected((StackTraceItem) stackTree
095: .getSelectionPath().getLastPathComponent());
096: }
097: }
098:
099: @Override
100: public void mouseReleased(MouseEvent me) {
101: }
102: });
103:
104: ActionListener upda = new ActionListener() {
105: public void actionPerformed(ActionEvent ae) {
106: parse(doc);
107: }
108: };
109:
110: viewType.addActionListener(upda);
111: onlyProjectCB.addActionListener(upda);
112: groupBranchsCB.addActionListener(upda);
113: includeThrowableCB.addActionListener(upda);
114:
115: setVisible(true); // NOT modal
116: }
117:
118: private void jumpToSelected(StackTraceItem it) {
119: status.setText("" + it.getUserObject());
120: FileItem fi = OutputPanels
121: .recognizeStackTraceLine_AndSelectInEditorIfFound("\t at "
122: + it.getUserObject());
123: if (fi == null) {
124: status.setText("CANNOT FIND: " + it.getUserObject());
125: }
126: }
127:
128: private void parse(final SimpleDocument doc) {
129: // clear
130: items.clear();
131: this .removeRecurse(root);
132: final ProgressModalDialog pmd = new ProgressModalDialog(this ,
133: "Parsing stacktraces", true);
134:
135: // progress ?
136: Thread t = new Thread() {
137: public void run() {
138: try {
139: pmd.start();
140: _parse(doc, pmd);
141: } finally {
142: pmd.closeDialog();
143: }
144: }
145: };
146: t.start();
147:
148: }
149:
150: /** we build the complete tree offline and at end call reload...
151: */
152: private void _parse(SimpleDocument doc, ProgressModalDialog pmd) {
153: int stacktracesFound = 0;
154:
155: int lines = DocumentUtils.getLineNumber(doc, doc.getLength());
156: pmd.setProgressBounds(lines);
157:
158: String lastNotStack = "?";
159: StackTraceItem actualBranch = null;
160: StackTraceItem previousItem = null;
161: for (int i = 0; i < lines; i++) {
162: pmd.incrementProgress(1);
163: String line = DocumentUtils.getTextOfLine(doc, i).trim();
164: int ps = line.indexOf("> ");
165: if (ps >= 0) // [Sep2007]
166: {
167: line = line.substring(ps + 2).trim();
168: }
169:
170: if (line.startsWith("at ")) {
171: //int pos = line.indexOf("\tat");
172: line = line.substring(3).trim();
173: }
174:
175: if (line.contains(".java:")
176: || line.contains("Unknown Source")) {
177: if (actualBranch == null) {
178: stacktracesFound++;
179: actualBranch = new StackTraceItem(lastNotStack,
180: lastNotStack);
181: // must be add ! otherwise, the adds to the model of "flying childs" fails.
182: insert(actualBranch, root);
183: }
184:
185: StackTraceItem actualItem = new StackTraceItem(line);
186: if (this .onlyProjectCB.isSelected()
187: && actualItem.isNotProject)
188: continue;
189:
190: if (previousItem == null) {
191: previousItem = actualBranch;
192: }
193:
194: if (viewType.getSelectedIndex() == 0) {
195: // (FLOW)
196: previousItem = addItemInTreeAccordingFlow(
197: actualItem, previousItem);
198: } else if (viewType.getSelectedIndex() == 1) {
199: // (SYNTAX) create tree structure according to javaNames
200: // BEtter ?
201: previousItem = addItemInTreeAccordingClassesStruct(
202: actualItem,
203: groupBranchsCB.isSelected() ? root
204: : actualBranch);
205: } else if (viewType.getSelectedIndex() == 2) {
206: // ??
207: }
208: } else {
209: // not a stack elt...
210: lastNotStack = line;
211: if (actualBranch != null) {
212: if (actualBranch.getChildCount() == 0
213: && actualBranch.getLevel() == 1) // just in root
214: {
215: //System.out.println("removing empty branch "+actualBranch);
216: actualBranch.removeFromParent();
217: } else if (!includeThrowableCB.isSelected()
218: && actualBranch.toString().contains(
219: "java.lang.Throwable")) {
220: actualBranch.removeFromParent();
221: }
222:
223: actualBranch = null;
224: previousItem = null;
225: }
226: }
227: } // for lines
228:
229: //remove empty branchs
230: if (actualBranch != null) {
231: if (actualBranch.getChildCount() == 0
232: || (!includeThrowableCB.isSelected() && actualBranch
233: .toString().contains("java.lang.Throwable"))) {
234: actualBranch.removeFromParent();
235: }
236: }
237:
238: final int fstacktracesFound = stacktracesFound;
239: EventQueue.invokeLater(new Runnable() {
240: public void run() {
241: setTitle(titleBase + " [" + fstacktracesFound
242: + " traces]");
243: treeModel.reload();
244: expandRecurse(root);
245:
246: }
247: });
248: //stackTree.makeVisible(root);
249: }
250:
251: private void insert(final DefaultMutableTreeNode node,
252: final DefaultMutableTreeNode parent) {
253: parent.add(node);
254: }
255:
256: boolean closed = false;
257:
258: public void onClose() {
259: if (!closed) {
260: MainEditorFrame.instance.globalProperties
261: .saveComponentSizeInINIFile(this ,
262: "StacktraceBrowser");
263: }
264:
265: // TODO: delete everything (tree) !!!
266: items.clear();
267: removeRecurse(root);
268:
269: closed = true;
270: }
271:
272: /** @return the added item (or the incremented visits item)
273: */
274: private StackTraceItem addItemInTreeAccordingFlow(
275: final StackTraceItem item, StackTraceItem previous) {
276: if (groupBranchsCB.isSelected()
277: && items.containsKey(item.stringRepresentation)) {
278: // don't work...
279: System.out.println("contains " + item.stringRepresentation);
280: StackTraceItem it = items.get(item.stringRepresentation);
281: it.visits++;
282: return it; // was return previous
283: } else {
284: // works fine
285: insert(item, previous);
286: items.put(item.stringRepresentation, item);
287: return item;
288: }
289: }
290:
291: private StackTraceItem addItemInTreeAccordingClassesStruct(
292: final StackTraceItem enditem, StackTraceItem actualTraceRoot) {
293: String[] path = enditem.structTreeName.split("\\."); //ex: java.lang.XXX
294: StringBuilder actPath = new StringBuilder();
295: StackTraceItem previous = actualTraceRoot;
296: List<String> pathv = new ArrayList<String>(Arrays.asList(path));
297:
298: pathv.add("" + enditem); // also add the last item, not only the path, so we can group (mark multiplicities) instead of repeating
299:
300: for (int i = 0; i < pathv.size(); i++) {
301: String pi = pathv.get(i);
302:
303: if (actPath.length() > 0)
304: actPath.append(".");
305: actPath.append(pi);
306:
307: StackTraceItem sti = null;
308: if (i == pathv.size() - 1) {
309: if (groupBranchsCB.isSelected()) {
310: sti = previous.getChildNamed(pi);
311: if (sti != null
312: && sti.getUserObject().equals(
313: enditem.getUserObject())) {
314: //System.out.println("grouping");
315: sti.visits++;
316: } else {
317: sti = null;
318: }
319: }
320:
321: if (sti == null) {
322: sti = enditem; // last trace component
323: insert(sti, previous);
324: }
325: } else {
326: sti = previous.getChildNamed(pi);
327: if (sti != null) {
328: sti.visits++;
329: } else {
330: sti = new StackTraceItem(actPath.toString(), pi);
331: }
332: insert(sti, previous);
333: }
334: previous = sti;
335: }
336:
337: return previous;
338:
339: }
340:
341: @tide.annotations.Recurse
342: private void removeRecurse(StackTraceItem it) {
343: if (it.getParent() != null) {
344: this .treeModel.removeNodeFromParent(it);
345: }
346: //it.removeFromParent();
347: while (it.getChildCount() > 0) {
348: removeRecurse(it.getChildItemAt(0));
349: }
350: }
351:
352: @tide.annotations.Recurse
353: private void expandRecurse(StackTraceItem it) {
354: if (it.isLeaf()) {
355: stackTree.makeVisible(new TreePath(it.getPath()));
356: }
357:
358: for (int i = 0; i < it.getChildCount(); i++) {
359: expandRecurse(it.getChildItemAt(i));
360: }
361: }
362:
363: static class StackTraceItem extends DefaultMutableTreeNode {
364: int exceptionCount = 0;
365: int errorCount = 0;
366: int throwableCount = 0;
367:
368: String stringRepresentation;
369: int line = -1;
370: String fileSimpleName = "??";
371: int visits = 1;
372: String structTreeName;
373:
374: public boolean isNotProject = false;
375:
376: /** for intermediate packages AND ROOTS
377: */
378: public StackTraceItem(String path, String intermediateSimpleName) {
379: super (path);
380: stringRepresentation = intermediateSimpleName;
381: structTreeName = path;
382: }
383:
384: public StackTraceItem(String stackString) {
385: super (stackString);
386: stringRepresentation = StringUtils.removeAfterLastIncluded(
387: stackString, "(");
388: structTreeName = stringRepresentation;
389:
390: int posJava = stackString.indexOf(".java");
391: if (posJava > 0) {
392: try {
393: line = Integer.parseInt(StringUtils
394: .extractFromFirstToNext_Excluded(
395: stackString, ".java:", ")"));
396: stringRepresentation += ":" + line;
397: } catch (Exception e) {
398: //
399: }
400:
401: fileSimpleName = StringUtils
402: .extractFromFirstToNext_Excluded(stackString,
403: "(", ".java");
404: //stringRepresentation+= ", (file "+fileSimpleName+")";
405:
406: // relativize the paths
407: int pos = stringRepresentation.indexOf("."
408: + fileSimpleName);
409: if (pos > 0) {
410: stringRepresentation = stringRepresentation
411: .substring(pos + 1);
412: }
413:
414: } else {
415: // also simplify the API classes if found
416: if (stackString.indexOf("(Unknown Source)") > 0) {
417: FileItem fi = MainEditorFrame.instance.getFileItem(
418: stringRepresentation, "");
419: if (fi != null) {
420: stringRepresentation = fi.getJavaPartName()
421: + stringRepresentation.substring(fi
422: .getJavaName().length());
423: if (fi instanceof LibFileItem) {
424: isNotProject = true;
425: }
426: }
427: }
428: }
429:
430: FileItem fi = MainEditorFrame.instance.getFileItem(
431: stackString, "");
432: if (fi != null) {
433: if (fi instanceof LibFileItem) {
434: isNotProject = true;
435: }
436: }
437:
438: }
439:
440: public StackTraceItem getChildItemAt(int i) {
441: return (StackTraceItem) super .getChildAt(i);
442: }
443:
444: public StackTraceItem getChildNamed(String simpleName) {
445: for (int i = 0; i < getChildCount(); i++) {
446: if (getChildItemAt(i).stringRepresentation
447: .equals(simpleName))
448: return getChildItemAt(i);
449: }
450: return null;
451: }
452:
453: @Override
454: public final String toString() {
455: return stringRepresentation
456: + (visits > 1 ? " (" + visits + " calls)" : "");
457: }
458:
459: /* CATASTROPHIC ! THE REMOVE OPERATIONS IN THE TREE MODEL CAUSE BAD REMOVAL !!
460: // (removefromparent uses indexof in collections and remove the false)
461: @Override public boolean equals(Object o2)
462: {
463: return stringRepresentation.equals(((StackTraceItem) o2).stringRepresentation);
464: }*/
465:
466: } // StackTraceItem
467:
468: // icon: count {errors, exceptions, throwables}
469: class Renderer extends DefaultTreeCellRenderer {
470: @Override
471: public Component getTreeCellRendererComponent(JTree tree,
472: Object value, boolean selected, boolean expanded,
473: boolean leaf, int row, boolean hasFocus) {
474: StackTraceItem sti = (StackTraceItem) value;
475: JLabel comp = (JLabel) super.getTreeCellRendererComponent(
476: tree, value, selected, expanded, leaf, row,
477: hasFocus);
478: return comp;
479: }
480:
481: }
482:
483: }
|