0001: package tide.syntaxtree;
0002:
0003: import tide.execute.StaticLauncher;
0004: import japa.parser.ast.CompilationUnit;
0005: import snow.utils.gui.Icons;
0006: import snow.utils.gui.GUIUtils;
0007: import tide.bytecode.DecompileManager;
0008: import tide.bytecode.DecompiledClass;
0009: import java.awt.Insets;
0010: import java.awt.Dimension;
0011: import snow.html.HTMLUtils;
0012: import java.awt.Rectangle;
0013: import java.awt.EventQueue;
0014: import java.awt.Color;
0015: import javaparser.*;
0016: import javaparser.javacc_gen.*;
0017: import tide.editor.*;
0018: import tide.sources.*;
0019: import snow.concurrent.*;
0020: import java.io.*;
0021: import java.util.*;
0022: import java.awt.BorderLayout;
0023: import java.awt.event.*;
0024: import javax.swing.*;
0025: import javax.swing.text.*;
0026: import javax.swing.tree.*;
0027: import snow.utils.StringUtils;
0028:
0029: /** Held in the MainEditorFrame,
0030: contains, delayed, the parsed syntax of the displayed source and the error label poping up the full parser error message.
0031: BE AWARE OF MEMORY!
0032: => don't cache treenodes, they are terminated and userobjects set to null => keep paths as strings !!
0033: */
0034: public class SyntaxTreePanel extends JPanel {
0035: final private DefaultTreeModel treeModel = new DefaultTreeModel(
0036: new ParserTreeNode("Parser not started"));
0037: final private JTree tree = new JTree(treeModel);
0038: private SyntaxTreeRenderer syntaxTreeRenderer = new SyntaxTreeRenderer();
0039: Highlighter.HighlightPainter hpainter = new DefaultHighlighter.DefaultHighlightPainter(
0040: new Color(120, 120, 120, 40));
0041:
0042: private ParserTreeNode actualRoot = null;
0043: private SimplifiedSyntaxTree2 actualSyntaxTree = null;
0044: private ParserResult actualParserResult = null;
0045:
0046: private FileItem actualParsedAndDisplayed = null;
0047:
0048: private SyntaxTreeSearchPanel syntaxTreeSearchPanel;
0049:
0050: // shows the parser error and jumps if clicked
0051: private final JButton statusLabel = new JButton();
0052:
0053: List<String> lastClickedPath;
0054: private String itemToSelectAtNextParse = null;
0055: public JCheckBoxMenuItem hideInnerTypes = new JCheckBoxMenuItem(
0056: "Hide inner types", false);
0057:
0058: public SyntaxTreePanel() {
0059: super (new BorderLayout(0, 0));
0060: this .setBorder(null);
0061:
0062: JScrollPane sp = new JScrollPane(tree);
0063: sp.setBorder(null);
0064: sp.getViewport().setBorder(null);
0065: add(sp, BorderLayout.CENTER);
0066:
0067: syntaxTreeSearchPanel = new SyntaxTreeSearchPanel(this );
0068: add(syntaxTreeSearchPanel, BorderLayout.SOUTH);
0069: add(statusLabel, BorderLayout.NORTH);
0070: statusLabel.setMinimumSize(new Dimension(0, 0)); // so we can reduce the splits !
0071: statusLabel.setMargin(new Insets(0, 2, 0, 2));
0072: statusLabel.setFocusPainted(false);
0073: statusLabel.setVisible(false);
0074: statusLabel.addMouseListener(new MouseAdapter() {
0075: @Override
0076: public void mousePressed(MouseEvent me) {
0077: if (me.isPopupTrigger())
0078: showMessage(me);
0079: }
0080:
0081: @Override
0082: public void mouseReleased(MouseEvent me) {
0083: if (me.isPopupTrigger())
0084: showMessage(me);
0085: }
0086:
0087: void showMessage(MouseEvent me) {
0088: JPopupMenu pop = new JPopupMenu();
0089: pop.add(HTMLUtils.createCODEHTMLFromText(statusLabel
0090: .getText()));
0091: pop.show(statusLabel, 0, statusLabel.getHeight());
0092: }
0093: });
0094:
0095: statusLabel.addActionListener(new ActionListener() {
0096: public void actionPerformed(ActionEvent ae) {
0097: // jump to the position of the error
0098: String err = statusLabel.getText();
0099: jumpToError(err);
0100:
0101: }
0102: });
0103:
0104: // Doesn't work
0105: hideInnerTypes.addActionListener(new ActionListener() {
0106: public void actionPerformed(ActionEvent ae) {
0107: makeClassNodesVisible(getActualRoot());
0108: //tree.setCellRenderer(syntaxTreeRenderer);
0109: }
0110: });
0111:
0112: statusLabel
0113: .setToolTipText("Right-click to see the error message, left-click to jump");
0114:
0115: syntaxTreeSearchPanel.setVisible(false);
0116:
0117: tree.setRootVisible(false);
0118:
0119: tree.addMouseListener(new MouseAdapter() {
0120: @Override
0121: public void mousePressed(MouseEvent e) {
0122: if (e.isPopupTrigger()) {
0123: showPopup(e);
0124: e.consume();
0125: return;
0126: }
0127:
0128: int selRow = tree.getRowForLocation(e.getX(), e.getY());
0129: if (selRow != -1) {
0130: final JTextPane textPane = MainEditorFrame.instance.editorPanel
0131: .getTextPane();
0132: removeAllHighlights();
0133:
0134: TreePath selPath = tree.getPathForLocation(
0135: e.getX(), e.getY());
0136: ParserTreeNode ptn = (ParserTreeNode) selPath
0137: .getLastPathComponent();
0138: lastClickedPath = getPathAsString(selPath);
0139:
0140: selectTextForNode(ptn, textPane, e.isShiftDown());
0141:
0142: /* if(ptn.getUserObject() instanceof Token)
0143: {
0144: Token t = (Token) ptn.getUserObject();
0145: //displayTokenInfo(t);
0146: }*/
0147: } else {
0148: lastClickedPath = null;
0149: }
0150: }
0151:
0152: public void mouseReleased(MouseEvent e) {
0153: if (e.isPopupTrigger()) {
0154: showPopup(e);
0155: e.consume();
0156: return;
0157: }
0158: }
0159: });
0160:
0161: // pass typed chars to the search panel
0162: tree.addKeyListener(new KeyAdapter() {
0163: @Override
0164: public void keyTyped(KeyEvent ke) {
0165: syntaxTreeSearchPanel.appendTypedChar(ke.getKeyChar());
0166: }
0167: });
0168:
0169: }
0170:
0171: /** Notify the renderer (necessary, because he's not in the UI tree, as shared ref for the tree, just his paint is call )
0172: */
0173: @Override
0174: public void updateUI() {
0175: super .updateUI();
0176: if (syntaxTreeRenderer != null) {
0177: //this.syntaxTreeRenderer.updateUI(); // not working well
0178: // creates a new instead of updating, because this component is not a real swing comp, it has been
0179: // optimized and reused for all cells => not good UI update !
0180: syntaxTreeRenderer = new SyntaxTreeRenderer();
0181: tree.setCellRenderer(syntaxTreeRenderer);
0182: }
0183: }
0184:
0185: private void showPopup(MouseEvent me) {
0186: JPopupMenu popup = new JPopupMenu("syntax tree popup");
0187: int selRow = tree.getRowForLocation(me.getX(), me.getY());
0188: if (selRow >= 0) {
0189: TreePath selPath = tree.getPathForLocation(me.getX(), me
0190: .getY());
0191: final ParserTreeNode ptn = (ParserTreeNode) selPath
0192: .getLastPathComponent();
0193: String shortDescr = StringUtils.shortFormForDisplay(""
0194: + ptn, 70);
0195: popup.add("syntax element: " + shortDescr);
0196: if (ptn.isToken()) {
0197: Token t = ptn.getToken();
0198: popup.add("Token type = " + t.kind + " "
0199: + JavaParserConstants.tokenImage[t.kind]);
0200: }
0201:
0202: if (ptn instanceof TypeNode) {
0203: final TypeNode tn = (TypeNode) ptn;
0204: popup.add("Full name: " + tn.getJavaFullName());
0205:
0206: JMenuItem decomp = new JMenuItem(
0207: "View bytecode of disassembled class",
0208: SourcesTreeIcon.createSimpleLetterIcon("B"));
0209: popup.addSeparator();
0210: popup.add(decomp);
0211: decomp.addActionListener(new ActionListener() {
0212: public void actionPerformed(ActionEvent ae) {
0213: // TODO: compile !
0214: DecompileManager.getInstance()
0215: .removeDecompForSources(); // remove "old", and maybe without bytecode
0216: DecompiledClass dc = DecompileManager
0217: .getInstance().getDecompiledType(
0218: tn.getJavaFullName(), false,
0219: true);
0220: final String cont;
0221: if (dc != null) {
0222: cont = dc.toString();
0223: } else {
0224: cont = "/* No class found for "
0225: + tn.getJavaFullName() + " */";
0226: }
0227:
0228: //VirtualSource vs = new VirtualSource( tn.getJavaFullName(), cont); // TOD O... ??
0229: //MainEditorFrame.instance.editorPanel;
0230:
0231: JTextPane tp = new JTextPane();
0232: tp.setText(cont);
0233: tp.setCaretPosition(0);
0234: tp.setEditable(false);
0235: tp
0236: .setFont(MainEditorFrame.fixedWidthFontForProcesses);
0237: GUIUtils.displayInDialog(
0238: MainEditorFrame.instance,
0239: "Disassembled code of "
0240: + tn.getJavaFullName(),
0241: new JScrollPane(tp));
0242:
0243: }
0244: });
0245: } else if (ptn instanceof MethodNode) {
0246: final MethodNode met = (MethodNode) ptn;
0247: if (met.isPublicStaticMainMethod()) {
0248: JMenuItem run = new JMenuItem("Execute",
0249: Icons.sharedSmallStart);
0250: popup.addSeparator();
0251: popup.add(run);
0252: run.addActionListener(new ActionListener() {
0253: public void actionPerformed(ActionEvent ae) {
0254: MainEditorFrame.instance.execute(
0255: actualParsedAndDisplayed, false,
0256: null, null);
0257: }
0258: });
0259: } else if (met.isStatic()
0260: && actualParsedAndDisplayed != null) {
0261: JMenuItem run = new JMenuItem(
0262: "Execute (Static call from another JVM)",
0263: new Icons.StartIcon(12, 12, true, false,
0264: Color.orange));
0265: popup.addSeparator();
0266: popup.add(run);
0267: run.addActionListener(new ActionListener() {
0268: public void actionPerformed(ActionEvent ae) {
0269: System.out.println("mn=" + met.name);
0270:
0271: System.out.println("s1="
0272: + met.getSignatureSimplified1());
0273: System.out.println("s2="
0274: + met.getSignatureSimplified2());
0275: new StaticLauncher(
0276: actualParsedAndDisplayed,
0277: actualParsedAndDisplayed
0278: .getJavaName(), met.name,
0279: met.params);
0280: //MainEditorFrame.instance.execute(actualParsedAndDisplayed, false, null, null);
0281: }
0282: });
0283: }
0284: }
0285:
0286: // todo: only if spans several lines
0287: JMenuItem jumpToEnd = new JMenuItem(
0288: "Jump to the item end (Hint: Shift+click)",
0289: Icons.sharedRightArrow);
0290: popup.add(jumpToEnd);
0291: jumpToEnd.addActionListener(new ActionListener() {
0292: public void actionPerformed(ActionEvent ae) {
0293:
0294: final JTextPane textPane = MainEditorFrame.instance.editorPanel
0295: .getTextPane();
0296: removeAllHighlights();
0297:
0298: //TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
0299: //ParserTreeNode ptn = (ParserTreeNode) selPath.getLastPathComponent();
0300: //lastClickedPath = getPathAsString(selPath);
0301:
0302: selectTextForNode(ptn, textPane, true); // shift down!
0303: }
0304: });
0305:
0306: List<Action> anodes = ActionsForNode
0307: .generateActionsForNode(ptn);
0308: if (anodes.size() > 0) {
0309: popup.addSeparator();
0310: for (Action a : anodes) {
0311: JMenuItem mi = new JMenuItem(a);
0312: mi.setIcon(Icons.sharedWiz);
0313: popup.add(mi);
0314: }
0315: }
0316: }
0317:
0318: if (popup.getComponentCount() > 0) {
0319: popup.addSeparator();
0320: }
0321:
0322: JMenuItem searchItem = new JMenuItem("Search",
0323: Icons.sharedSearch);
0324: searchItem.setAccelerator(Accelerators.search); //another ??
0325: popup.add(searchItem);
0326: searchItem.addActionListener(new ActionListener() {
0327: public void actionPerformed(ActionEvent ae) {
0328: syntaxTreeSearchPanel.showSearchPanel();
0329: }
0330: });
0331:
0332: popup.addSeparator();
0333: popup.add(hideInnerTypes);
0334:
0335: //no result... me.consume();
0336: popup.show(tree, me.getX(), me.getY());
0337: }
0338:
0339: public ParserTreeNode getActualRoot() {
0340: return actualRoot;
0341: }
0342:
0343: private void removeAllHighlights() {
0344: final JTextPane textPane = MainEditorFrame.instance.editorPanel
0345: .getTextPane();
0346: textPane.getHighlighter().removeAllHighlights();
0347: }
0348:
0349: /** @param line first line has index 0
0350: */
0351: public static int getDocPos(JTextPane textPane, int line, int col) {
0352: Element begLine = Utils.getLine(textPane.getDocument(), line);
0353: int pos = col + begLine.getStartOffset();
0354: return pos;
0355: }
0356:
0357: public static int getDocPosBegin(JTextPane textPane, Token t) {
0358: return getDocPos(textPane, t.beginLine - 1, t.beginColumn - 1);
0359: }
0360:
0361: public static int getDocPosEnd(JTextPane textPane, Token t) {
0362: return getDocPos(textPane, t.endLine - 1, t.endColumn - 1);
0363: }
0364:
0365: /** Selects textpane corresponding portion of text for the given node.
0366: * the positions are stored in the node.
0367: */
0368: public int selectTextForNode(ParserTreeNode tn, JTextPane textPane,
0369: boolean shiftDown) {
0370: // [Jan2007] useful: jump to the end...
0371: if (shiftDown) {
0372: int[] eLinCol = tn.getEndLinCol();
0373: if (eLinCol[0] >= 0) {
0374: Element endLine = Utils.getLine(textPane.getDocument(),
0375: eLinCol[0] - 1);
0376: this .selectDocumentPortion(endLine.getEndOffset() - 1,
0377: endLine.getEndOffset());
0378: return endLine.getEndOffset() - 1;
0379: }
0380: }
0381:
0382: //new Throwable().printStackTrace();
0383: int[] sLinCol = tn.getStartLinCol();
0384: if (sLinCol[0] == -1)
0385: return -1;
0386:
0387: Element begLine = Utils.getLine(textPane.getDocument(),
0388: sLinCol[0] - 1);
0389: int begDocPos = sLinCol[1] + begLine.getStartOffset() - 1;
0390:
0391: int endPos = -1;
0392:
0393: if (tn instanceof ClassNode) {
0394: ClassNode cn = (ClassNode) tn;
0395: if (cn.blokStart != null)
0396: endPos = getDocPosBegin(textPane, cn.blokStart);
0397: } else if (tn instanceof MethodNode) {
0398: MethodNode cn = (MethodNode) tn;
0399: if (cn.blokStart != null)
0400: endPos = getDocPosBegin(textPane, cn.blokStart);
0401: } else if (tn instanceof ConstructorNode) {
0402: ConstructorNode cn = (ConstructorNode) tn;
0403: if (cn.blokStart != null)
0404: endPos = getDocPosBegin(textPane, cn.blokStart);
0405: }
0406:
0407: if (endPos < 0) {
0408: int[] eLinCol = tn.getEndLinCol();
0409: if (eLinCol[0] == -1) {
0410: endPos = begDocPos + 10;
0411: if (endPos > textPane.getDocument().getLength()) {
0412: endPos = textPane.getDocument().getLength();
0413: }
0414: } else {
0415: Element endLine = Utils.getLine(textPane.getDocument(),
0416: eLinCol[0] - 1);
0417: endPos = eLinCol[1] + endLine.getStartOffset();
0418: }
0419: }
0420:
0421: selectDocumentPortion(begDocPos, endPos);
0422:
0423: return begDocPos;
0424: }
0425:
0426: private void selectDocumentPortion(int start, int end) {
0427: final JTextPane textPane = MainEditorFrame.instance.editorPanel
0428: .getTextPane();
0429: // textPane.setSelectionStart(start);
0430: // textPane.setSelectionEnd(end);
0431: textPane.getHighlighter().removeAllHighlights();
0432: try {
0433: textPane.getHighlighter().addHighlight(start, end,
0434: this .hpainter);
0435: //textPane.scrollRectToVisible( textPane.modelToView( start ));
0436: MainEditorFrame.instance.editorPanel.scrollPosMiddle(start,
0437: false);
0438: if (end <= textPane.getDocument().getLength()) {
0439: textPane.setCaretPosition(end); // [Feb2008]: clicking on syntax tree should also show the line numbers ! => move the caret
0440: }
0441: } catch (Exception e) {
0442: e.printStackTrace();
0443: }
0444: }
0445:
0446: /* May be null or old !
0447: use st.aprioriJavaName to know which source is being displayed
0448: */
0449: public SimplifiedSyntaxTree2 getActualSimplifiedSyntaxTree() {
0450: return actualSyntaxTree;
0451: }
0452:
0453: public void setNodeToPreselect(TreeNode tn) {
0454: this .syntaxTreeRenderer.preselectedNodes.clear();
0455: if (tn != null) {
0456: syntaxTreeRenderer.preselectedNodes.add(tn);
0457: }
0458: if (tn != null) {
0459: ParserTreeNode ptn = (ParserTreeNode) tn;
0460: TreePath tp = new TreePath(ptn.getPath());
0461: tree.makeVisible(tp);
0462: tree.scrollPathToVisible(tp);
0463: }
0464: tree.repaint();
0465:
0466: }
0467:
0468: /** [Oct2007]: using the new java parser.
0469: Parses the given source ( in a separate thread, later ! ).
0470: @return a InterruptableTask that can be cleanly interrupted
0471: TODO: interrupt if already parsing another src...
0472: */
0473: public InterruptableTask setSource2(final FileItem fileItem,
0474: final String src) {
0475: this .statusLabel.setText("");
0476:
0477: if (!fileItem.isSourceFile()) {
0478: // last chance: special case for class that has been decompiled !
0479: if (fileItem instanceof LibFileItem
0480: && ((LibFileItem) fileItem).decompiledContentOrSource != null) {
0481: // ok, let it display below...
0482: } else {
0483: tree.setVisible(false);
0484: return null;
0485: }
0486: }
0487:
0488: // directly display the cached old tree
0489: if (fileItem.getParserResultIfAlreadyMade() != null) {
0490: actualParserResult = fileItem
0491: .getParserResultIfAlreadyMade();
0492: EventQueue.invokeLater(new Runnable() {
0493: public void run() {
0494: // ok, it worked !
0495: actualParsedAndDisplayed = fileItem;
0496:
0497: //actualRoot = actualSyntaxTree.root;
0498: //setTreeModelAndMakeNodesVisible(actualRoot);
0499:
0500: //selectMethodOrField();
0501: }
0502: });
0503: }
0504:
0505: if (!tree.isVisible()) {
0506: tree.setVisible(true);
0507: }
0508:
0509: final InterruptableTask task = new InterruptableTask() {
0510: public void run() {
0511: final StringReader sr = new StringReader(src);
0512:
0513: final long t0 = System.currentTimeMillis();
0514:
0515: try {
0516: if (interrupter.getShouldStopEvaluation())
0517: return;
0518:
0519: // this may cause exception
0520: final CompilationUnit cu = japa.parser.JavaParser
0521: .parse(sr);
0522: ParserResult pr = new ParserResult(cu);
0523:
0524: if (interrupter.getShouldStopEvaluation())
0525: return;
0526:
0527: long dt = (System.currentTimeMillis() - t0);
0528: if (dt > 500) {
0529: System.out.println("JavaParser2 took " + dt
0530: + "ms for " + fileItem);
0531: }
0532: //actualSyntaxTree = new SimplifiedSyntaxTree2(st.root, fileItem.getJavaName());
0533: fileItem.setParserResult(pr);
0534:
0535: if (interrupter.getShouldStopEvaluation())
0536: return;
0537: /*
0538: if(MainEditorFrame.instance.enableParserWarnings.isSelected())
0539: {
0540: TreeFunctions.searchForCommonProblems(actualSyntaxTree, actualSyntaxTree.root, fileItem.getJavaName());
0541: actualSyntaxTree.callAtEndToMakeWarningsVisible();
0542: }*/
0543:
0544: if (interrupter.getShouldStopEvaluation())
0545: return;
0546:
0547: // ok, it worked !
0548: actualParsedAndDisplayed = fileItem;
0549:
0550: EventQueue.invokeLater(new Runnable() {
0551: public void run() {
0552: //actualRoot = actualSyntaxTree.root;
0553: //setTreeModelAndMakeNodesVisible(actualSyntaxTree.root);
0554: //selectMethodOrField();
0555: }
0556: });
0557: EventQueue.invokeLater(new Runnable() {
0558: public void run() {
0559: statusLabel.setText(""); // successful;
0560: statusLabel.setVisible(false);
0561: }
0562: });
0563: } catch (final Throwable e) // Exception and also sometimes Error ! like TokenMgrError
0564: {
0565: EventQueue.invokeLater(new Runnable() {
0566: public void run() {
0567: statusLabel
0568: .setText(""
0569: + e.getMessage().replace(
0570: '\n', ' '));
0571: statusLabel.setVisible(true);
0572:
0573: //occurs often during editing...
0574: // MainEditorFrame.debugOut(e);
0575: }
0576: });
0577:
0578: //e.printStackTrace();
0579: }
0580: }
0581: };
0582:
0583: Thread t = new Thread(task);
0584: t.setName("SyntaxTreePanel source analyse (parser2)");
0585: t.setPriority(t.MIN_PRIORITY);
0586: t.start();
0587:
0588: return task;
0589: }
0590:
0591: /** Parses the given source ( in a separate thread, later ! ).
0592: @return a InterruptableTask that can be cleanly interrupted
0593: */
0594: public InterruptableTask setSource(final FileItem fileItem,
0595: final String src) {
0596: this .statusLabel.setText("");
0597:
0598: if (!fileItem.isSourceFile()) {
0599: // last chance: special case for class that has been decompiled !
0600: if (fileItem instanceof LibFileItem
0601: && ((LibFileItem) fileItem).decompiledContentOrSource != null) {
0602: // ok, let it display below...
0603: } else {
0604: tree.setVisible(false);
0605: return null;
0606: }
0607: }
0608:
0609: // directly display the cached old tree
0610: if (fileItem.getSimplifiedSyntaxTreeIfAlreadyMade() != null) {
0611: actualSyntaxTree = fileItem
0612: .getSimplifiedSyntaxTreeIfAlreadyMade();
0613: EventQueue.invokeLater(new Runnable() {
0614: public void run() {
0615: //[Dec2006]NO sense
0616: // final DefaultTreeModel tm = new DefaultTreeModel(actualSyntaxTree.root);
0617:
0618: // ok, it worked !
0619: actualParsedAndDisplayed = fileItem;
0620:
0621: actualRoot = actualSyntaxTree.root;
0622: setTreeModelAndMakeNodesVisible(actualRoot);
0623:
0624: //selectMethodOrField();
0625: }
0626: });
0627: }
0628:
0629: if (!tree.isVisible()) {
0630: tree.setVisible(true);
0631: }
0632:
0633: final InterruptableTask task = new InterruptableTask() {
0634: public void run() {
0635: final StringReader sr = new StringReader(src);
0636:
0637: final long t0 = System.currentTimeMillis();
0638: final JavaParser pa = new JavaParser(sr);
0639: pa.disable_tracing();
0640: final RAWSyntaxTree st = new RAWSyntaxTree(fileItem
0641: .getJavaName());
0642: pa.parserOutputProcessor = st;
0643: try {
0644: if (interrupter.getShouldStopEvaluation())
0645: return;
0646:
0647: // this may cause exception
0648: pa.CompilationUnit();
0649:
0650: if (interrupter.getShouldStopEvaluation())
0651: return;
0652:
0653: long dt = (System.currentTimeMillis() - t0);
0654: if (dt > 2000) {
0655: MainEditorFrame.debugOut("JavaParser took "
0656: + dt + "ms for " + fileItem);
0657: }
0658: actualSyntaxTree = new SimplifiedSyntaxTree2(
0659: st.root, fileItem.getJavaName());
0660: fileItem.setSimplifiedSyntaxTree(actualSyntaxTree);
0661:
0662: if (interrupter.getShouldStopEvaluation())
0663: return;
0664:
0665: if (MainEditorFrame.instance.enableParserWarnings
0666: .isSelected()) {
0667: TreeFunctions.searchForCommonProblems(
0668: actualSyntaxTree,
0669: actualSyntaxTree.root, fileItem
0670: .getJavaName());
0671: actualSyntaxTree
0672: .callAtEndToMakeWarningsVisible();
0673: }
0674:
0675: if (interrupter.getShouldStopEvaluation())
0676: return;
0677:
0678: // ok, it worked !
0679: actualParsedAndDisplayed = fileItem;
0680:
0681: EventQueue.invokeLater(new Runnable() {
0682: public void run() {
0683: actualRoot = actualSyntaxTree.root;
0684: setTreeModelAndMakeNodesVisible(actualSyntaxTree.root);
0685: //selectMethodOrField();
0686: }
0687: });
0688: EventQueue.invokeLater(new Runnable() {
0689: public void run() {
0690: statusLabel.setText(""); // successful;
0691: statusLabel.setVisible(false);
0692: }
0693: });
0694: } catch (final Throwable e) // Exception and also sometimes Error ! like TokenMgrError
0695: {
0696: EventQueue.invokeLater(new Runnable() {
0697: public void run() {
0698: statusLabel
0699: .setText(""
0700: + e.getMessage().replace(
0701: '\n', ' '));
0702: statusLabel.setVisible(true);
0703:
0704: //occurs often during editing...
0705: // MainEditorFrame.debugOut(e);
0706: }
0707: });
0708:
0709: //e.printStackTrace();
0710: }
0711: }
0712: };
0713:
0714: Thread t = new Thread(task);
0715: t.setName("SyntaxTreePanel source analyse");
0716: t.setPriority(t.MIN_PRIORITY);
0717: t.start();
0718:
0719: return task;
0720: }
0721:
0722: /** DON't CREATE TreeModels at each call, because of memory leaks !
0723: * this reuses the existing model, and realoads a new root.
0724: */
0725: private void setTreeModelAndMakeNodesVisible(ParserTreeNode newRoot) {
0726: //NO NO NO, due to memory optims, the actual is already OLD and has been removed (parents, and user objects set to null)
0727: //List<String> selPath = getSelectedTreePathMadeOfNames();
0728: Rectangle vr = tree.getVisibleRect();
0729:
0730: treeModel.setRoot(newRoot);
0731: treeModel.reload();
0732:
0733: if (newRoot != null) // was actualSyntaxTree.root
0734: {
0735: makeClassNodesVisible(newRoot);
0736: }
0737:
0738: tree.setCellRenderer(syntaxTreeRenderer);
0739:
0740: if (lastClickedPath != null) {
0741: TreePath tp = createPathFromNames(lastClickedPath);
0742: if (tp != null) {
0743: tree.setSelectionPath(tp);
0744: tree.expandPath(tp);
0745: }
0746: }
0747:
0748: // keep the original view
0749: tree.scrollRectToVisible(vr);
0750:
0751: // eventually select some method (set by a "jump to" function of output panel line click)
0752: selectMethodOrField();
0753: }
0754:
0755: /** Class nodes and their first childs are made visible.
0756: * (can be ameliorated)
0757: */
0758: @tide.annotations.Recurse
0759: private void makeClassNodesVisible(final ParserTreeNode root) {
0760: if (root == null)
0761: return;
0762:
0763: for (int i = 0; i < root.getChildCount(); i++) {
0764: final ParserTreeNode ci = root.getChildNodeAt(i);
0765: if (ci.expandInView) // true for classes
0766: {
0767: tree.makeVisible(new TreePath(ci.getPath()));
0768:
0769: // make all childs visible
0770: //if(!hideInnerTypes.isSelected())
0771: {
0772: Utils.makeChildsExpanded(ci, tree, 0); // was 5 was 1
0773: }
0774: }
0775:
0776: // recursively look for deeper classes
0777: if (!hideInnerTypes.isSelected()) {
0778: makeClassNodesVisible(ci);
0779: } else {
0780: // tree.collapsePath( new TreePath( ci.getPath() ));
0781: }
0782: }
0783: }
0784:
0785: /** allow to restore the selection after update.
0786: * objects are no more the same, but if path matches...
0787: */
0788: private List<String> getPathAsString(TreePath sp) {
0789: if (sp == null)
0790: return null;
0791:
0792: List<String> p = new ArrayList<String>();
0793: ParserTreeNode ptn = (ParserTreeNode) sp.getLastPathComponent();
0794: while (ptn != null) {
0795: p.add(0, ptn.toString());
0796: ptn = ptn.getParentNode();
0797: }
0798:
0799: return p;
0800: }
0801:
0802: /** used to keep selection when updating
0803: */
0804: private TreePath createPathFromNames(List<String> pathNames) {
0805: ParserTreeNode ptn = this .getActualRoot();
0806: if (ptn == null)
0807: return null;
0808:
0809: Object[] path = new Object[pathNames.size()];
0810: if (path.length == 0)
0811: return null;
0812:
0813: path[0] = ptn; // root
0814: for (int i = 1; i < pathNames.size(); i++) // first is root => skip
0815: {
0816: ptn = TreeUtils
0817: .getChildWithStringRep(ptn, pathNames.get(i));
0818: if (ptn == null)
0819: return null;
0820: path[i] = ptn;
0821: }
0822: //ptn.getChildNamed()
0823:
0824: return new TreePath(path);
0825: }
0826:
0827: /** selects the nearest simplified node (method, field, ...)
0828: */
0829: public ParserTreeNode getSimplifiedTreeElementForSourceLocation(
0830: int line, int col, FileItem item) {
0831: if (actualSyntaxTree == null)
0832: return null;
0833: if (this .actualParsedAndDisplayed != item)
0834: return null;
0835:
0836: return TreeUtils.getNearestSimplifiedNode(
0837: actualSyntaxTree.root, line, col);
0838: }
0839:
0840: // not perfect.
0841: public ParserTreeNode selectNextTo(ParserTreeNode from) {
0842: if (actualSyntaxTree == null)
0843: return null;
0844: List<ParserTreeNode> maf = new ArrayList<ParserTreeNode>();
0845: for (TypeNode tn : actualSyntaxTree.getAllTypes()) {
0846: maf.addAll(TreeUtils.getAllMethodsAndFieldsForType(tn));
0847: }
0848:
0849: ParserTreeNode found = null;
0850: int smallestD = 1000000;
0851: for (ParserTreeNode pi : maf) {
0852: if (pi == from)
0853: continue;
0854:
0855: int d = pi.getStartLinCol()[0] - from.getStartLinCol()[0];
0856: if (d >= 0 && d < smallestD) {
0857: smallestD = d;
0858: found = pi;
0859: }
0860: }
0861:
0862: //System.out.println("Search next:"+found);
0863:
0864: if (found != null) {
0865: TreePath tp = new TreePath(found.getPath());
0866: tree.setSelectionPath(tp);
0867: tree.makeVisible(tp);
0868: tree.scrollPathToVisible(tp);
0869: return found;
0870: } else {
0871: System.out.println("No next node found");
0872: return null;
0873: }
0874: }
0875:
0876: // not perfect.
0877: public ParserTreeNode selectPreviousTo(ParserTreeNode from) {
0878: if (actualSyntaxTree == null)
0879: return null;
0880: List<ParserTreeNode> maf = new ArrayList<ParserTreeNode>();
0881: for (TypeNode tn : actualSyntaxTree.getAllTypes()) {
0882: maf.addAll(TreeUtils.getAllMethodsAndFieldsForType(tn));
0883: }
0884:
0885: ParserTreeNode found = null;
0886: int smallestD = 1000000;
0887: for (ParserTreeNode pi : maf) {
0888: if (pi == from)
0889: continue;
0890: int d = -pi.getStartLinCol()[0] + from.getStartLinCol()[0];
0891: if (d >= 0 && d < smallestD) {
0892: smallestD = d;
0893: found = pi;
0894: }
0895: }
0896:
0897: //System.out.println("Search prev:"+found);
0898:
0899: if (found != null) {
0900: TreePath tp = new TreePath(found.getPath());
0901: tree.setSelectionPath(tp);
0902: tree.makeVisible(tp);
0903: tree.scrollPathToVisible(tp);
0904: return found;
0905: } else {
0906: System.out.println("No prev node found");
0907: return null;
0908: }
0909: }
0910:
0911: /** Selects the nearest simplified node (method, field, ...)
0912: */
0913: public ParserTreeNode selectSimplifiedTreeElementForSourceLocation(
0914: int line, int col, FileItem item) {
0915: if (actualSyntaxTree == null)
0916: return null;
0917: if (this .actualParsedAndDisplayed != item)
0918: return null;
0919:
0920: ParserTreeNode found = TreeUtils.getNearestSimplifiedNode(
0921: actualSyntaxTree.root, line, col);
0922: if (found != null) {
0923: TreePath tp = new TreePath(found.getPath());
0924: tree.setSelectionPath(tp);
0925: tree.makeVisible(tp);
0926: tree.scrollPathToVisible(tp);
0927: return found;
0928: } else {
0929: System.out.println("No node found at " + line + ", " + col);
0930: return null;
0931: }
0932: }
0933:
0934: /** Selects the first enclosing (deepest) type for the given location
0935: * corresponds to the "this" for the given location.
0936: */
0937: public void selectTreeTypeForSourceLocation(int line, int col,
0938: FileItem item) {
0939: if (actualSyntaxTree == null)
0940: return;
0941: if (this .actualParsedAndDisplayed != item)
0942: return;
0943:
0944: /* Vector<TypeNode> allTypes = new Vector<TypeNode>();
0945: for(TypeNode tn : actualSyntaxTree.allTopLevelTypes)
0946: {
0947: addAllRecurseNotDepthFirst(allTypes, tn );
0948: }
0949:
0950: // search from end (=> depth first)
0951: for(int i=allTypes.size()-1; i>=0; i--)
0952: {
0953: TypeNode ti = allTypes.get(i);
0954: if(TreeUtils.contains(ti, line, col))
0955: { */
0956: TypeInterface found = actualSyntaxTree.getDeepestTypeAt(line,
0957: col, false);
0958: if (found != null) {
0959: TypeNode tn = (TypeNode) found;
0960: TreePath tp = new TreePath(tn.getPath());
0961: tree.setSelectionPath(tp);
0962: tree.makeVisible(tp);
0963: tree.scrollPathToVisible(tp);
0964: return;
0965: }
0966: }
0967:
0968: /** @return the first enclosing type at given location, (= this scope)
0969: */
0970: public TypeInterface getDeepestTypeForLocation(int line, int col,
0971: FileItem item, boolean includeAnonymous) {
0972: if (actualSyntaxTree == null)
0973: return null;
0974: if (this .actualParsedAndDisplayed != item)
0975: return null;
0976:
0977: return actualSyntaxTree.getDeepestTypeAt(line, col,
0978: includeAnonymous);
0979: }
0980:
0981: /** files doesn't normally not a lot of types...
0982: */
0983: private void addAllRecurseNotDepthFirst(List<TypeNode> allTypes,
0984: TypeNode t) {
0985: allTypes.add(t);
0986: for (TypeNode tn : t.directChildTypes) {
0987: addAllRecurseNotDepthFirst(allTypes, tn);
0988: }
0989: }
0990:
0991: /*
0992: public void selectRAWTreeElementForSourceLocation(int line, int col, FileItem item)
0993: {
0994: if(actualSyntaxTree==null) return;
0995: if(this.actualParsedAndDisplayed != item) return;
0996:
0997: RAWParserTreeNode found = TreeUtils.getNodeTokenContaining(actualSyntaxTree.getRawParserResult(), line, col);
0998: if(found!=null)
0999: {
1000: TreePath tp = new TreePath(found.getPath());
1001: tree.setSelectionPath( tp );
1002: tree.makeVisible( tp );
1003: tree.scrollPathToVisible( tp );
1004:
1005: }
1006: else
1007: {
1008: System.out.println("None found at "+line+", "+col);
1009: }
1010: }
1011:
1012:
1013: public void selectRAWTreeElementJustBeforeForSourceLocation(int line, int col, FileItem item)
1014: {
1015: if(actualSyntaxTree==null) return;
1016: if(this.actualParsedAndDisplayed != item) return;
1017: RAWParserTreeNode found = TreeUtils.getFirstTokenNodeBeforePosition(actualSyntaxTree.getRawParserResult(), line, col);
1018: if(found!=null)
1019: {
1020: TreePath tp = new TreePath(found.getPath());
1021: tree.setSelectionPath( tp );
1022: tree.makeVisible( tp );
1023: tree.scrollPathToVisible( tp );
1024: }
1025: else
1026: {
1027: System.out.println("None found at "+line+", "+col);
1028: }
1029: }
1030:
1031: public void selectRAWTreeElementJustAfterForSourceLocation(int line, int col, FileItem item)
1032: {
1033: if(actualSyntaxTree==null) return;
1034: if(this.actualParsedAndDisplayed != item) return;
1035:
1036: RAWParserTreeNode found = TreeUtils.getTokenNodeAtOrAfterPosition(actualSyntaxTree.getRawParserResult(), line, col);
1037: if(found!=null)
1038: {
1039: TreePath tp = new TreePath(found.getPath());
1040: tree.setSelectionPath( tp );
1041: tree.makeVisible( tp );
1042: tree.scrollPathToVisible( tp );
1043: }
1044: else
1045: {
1046: System.out.println("None found at "+line+", "+col);
1047: }
1048: }*/
1049:
1050: public void analyse(String text, int line, int col) {
1051: System.out.println("Analyse " + text + " at line " + line
1052: + " col " + col);
1053: if (actualRoot == null)
1054: return;
1055:
1056: }
1057:
1058: /** the next setSource() will jump to this method
1059: */
1060: public void setMethodToJumpAtNextParse(String methodName) {
1061: itemToSelectAtNextParse = methodName + "()";
1062: }
1063:
1064: /** The next setSource() will jump to this field.
1065: * TRICKY. BAD when jumping from stacktrace, need two clicks... TODO.
1066: */
1067: public void setFieldToJumpAtNextParse(String methodName) {
1068: itemToSelectAtNextParse = methodName;
1069: }
1070:
1071: /** This is called after the parser has build the tree and selects
1072: * some method or field to jump to.
1073: * Also called from the EditorPanel
1074: */
1075: public void selectMethodOrField() // recursive
1076: {
1077: //System.out.println("itemToSelectAtNextParse= "+itemToSelectAtNextParse);
1078: if (itemToSelectAtNextParse == null)
1079: return;
1080:
1081: if (itemToSelectAtNextParse.indexOf('(') > 0) {
1082: if (!selectMethodOrField(this .actualRoot, StringUtils
1083: .removeAfterLastIncluded(itemToSelectAtNextParse,
1084: ")"))) // keep opening ( to avoid selecting fields
1085: {
1086: System.out.println("STP: Method not found: "
1087: + itemToSelectAtNextParse);
1088: System.out.println("Actual displayed: "
1089: + this .actualParsedAndDisplayed);
1090: }
1091: } else {
1092: if (!selectMethodOrField(this .actualRoot,
1093: itemToSelectAtNextParse + " ")) // field names in tree have spaces after
1094: {
1095: System.out.println("STP: Field not found: "
1096: + itemToSelectAtNextParse);
1097: }
1098: }
1099: itemToSelectAtNextParse = null;
1100: }
1101:
1102: private void jumpToError(String message) {
1103: int line = 0;
1104: int pos = message.indexOf("line ");
1105: if (pos > 0) {
1106: int posE = message.indexOf(',', pos + 5);
1107: if (posE > 0) {
1108: String ls = message.substring(pos + 5, posE);
1109: //System.out.println("Error node: ls="+ls);
1110: try {
1111: line = Integer.parseInt(ls) - 1;
1112: } catch (Exception ee) {
1113: }
1114: }
1115: }
1116:
1117: int column = 0;
1118: pos = message.indexOf("column ", pos);
1119: if (pos > 0) {
1120: int posE = message.indexOf('.', pos + 7);
1121: if (posE > 0) {
1122: String ls = message.substring(pos + 7, posE);
1123: try {
1124: column = Integer.parseInt(ls) - 1;
1125: } catch (Exception ee) {
1126: }
1127: }
1128: }
1129:
1130: final JTextPane textPane = MainEditorFrame.instance.editorPanel
1131: .getTextPane();
1132: Element startLine = Utils.getLine(textPane.getDocument(), line);
1133: int startPos = startLine.getStartOffset() + column;
1134:
1135: int endPos = startLine.getEndOffset();
1136: if (endPos <= startPos) {
1137: endPos = Math.max(startPos + 1, textPane.getDocument()
1138: .getLength());
1139: }
1140:
1141: selectDocumentPortion(startPos, endPos);
1142:
1143: }
1144:
1145: /** search recurse, stops at the first found
1146: */
1147: private boolean selectMethodOrField(ParserTreeNode root, String m) {
1148: int count = root.getChildCount();
1149: for (int i = 0; i < count; i++) {
1150: final ParserTreeNode ci = root.getChildNodeAt(i);
1151: if (ci.toString().startsWith(m)) {
1152: TreePath tp = new TreePath(ci.getPath());
1153: tree.makeVisible(tp);
1154: final JTextPane textPane = MainEditorFrame.instance.editorPanel
1155: .getTextPane();
1156:
1157: selectTextForNode(ci, textPane, false);
1158: return true;
1159: }
1160: }
1161:
1162: // not found => recurse into branches
1163: for (int i = 0; i < root.getChildCount(); i++) {
1164: final ParserTreeNode ci = root.getChildNodeAt(i);
1165: if (ci.getChildCount() > 0) {
1166: boolean found = selectMethodOrField(ci, m);
1167: if (found)
1168: return true;
1169: }
1170: }
1171:
1172: return false;
1173: }
1174:
1175: }
|