001: package tide.syntaxtree;
002:
003: import javaparser.javacc_gen.*;
004: import javaparser.*;
005: import java.util.*;
006: import javax.swing.tree.*;
007: import tide.editor.UIConstants;
008: import java.io.StringReader;
009: import tide.sources.FileItem;
010: import tide.editor.MainEditorFrame;
011:
012: /** Extracts the human readeable info for class, methods, fields...
013: from the complete syntax tree.
014: We also take the information requested to provide code completion. (variables and their scope).
015: This simplified tree contains at the end the complete original tree (RAW node).
016: */
017: public class SimplifiedSyntaxTree2 {
018: // the new tree, use it as root for a DefaultTreeModel.
019: public ParserTreeNode root = new ParserTreeNode("sst root");
020:
021: // if not found, not added, correspond to "" root package (unnamed scope)
022: public PackageNode packageNode = new PackageNode();
023: public ParserTreeNode importsNode = new MainNode("", "imports",
024: UIConstants.blue, false);
025: private ParserTreeNode parserStateNode = new MainNode("",
026: "parser state", UIConstants.blue, false);
027:
028: private ParserTreeNode warningsNode = new MainNode("", "warnings",
029: UIConstants.red, true);
030: private ParserTreeNode errorsNode = new MainNode("", "errors",
031: UIConstants.red, true);
032:
033: public ParserTreeNode getErrorsNode() {
034: return errorsNode;
035: }
036:
037: // these are the toplevel types defined in this source (class, interface, enum, annotation)
038: // not used in the tree but for the search / completion functions. guarantee to contain all the types (recursively)!
039: // only one can be public, the others have other class names than the source !!! AVOID THAT AS POSSIBLE !!!
040: final List<TypeNode> allTopLevelTypes = new ArrayList<TypeNode>();
041: // including the toplevel one, of course
042: final Collection<TypeNode> allTypes = new HashSet<TypeNode>();
043:
044: // null if no public top level type found. At most one per file !
045: public TypeNode publicTopLevelType = null;
046:
047: // don't use this directly, used for completion of local variables...
048: private RAWParserTreeNode rawParserResult;
049:
050: public RAWParserTreeNode getRawParserResult() {
051: return rawParserResult;
052: }
053:
054: // only for debug purposes
055: private ParserTreeNode rawParserResultDebug = null;
056:
057: public ParserTreeNode getRawParserResultDebug() {
058: return rawParserResultDebug;
059: }
060:
061: /** passed in the constructor, not necessary the class name (if private).
062: * example: "javax.swing.JTree".
063: * used as identifier for tIDE to know which parsed element is currently displayed.
064: */
065: String aprioriJavaName;
066:
067: /** @param rawParserTree is the result of the RAW parsed
068: */
069: public SimplifiedSyntaxTree2(final RAWParserTreeNode rawParserTree,
070: final String aprioriJavaName) {
071:
072: this .aprioriJavaName = aprioriJavaName;
073:
074: if (aprioriJavaName == null)
075: new Throwable("Please don't pass null").printStackTrace();
076:
077: RAWParserTreeNode compUnitNode = getCompilationUnitNode(rawParserTree);
078:
079: // 1 extract the package infos (NOT USEFUL !)
080: //root.insert(importsNode, 0);
081: //importsNode.expandInView = false;
082: // we call it but don't add the node. (the node is required for the checks).
083: extractPackageAndImportInfos(compUnitNode, false);
084:
085: // scan for types (Class, interfaces, annotations, ...)
086: // one of them may be public
087: scanForTypesAtTopLevel(compUnitNode, root);
088:
089: // Debug
090: compUnitNode.removeFromParent();
091: rawParserResult = compUnitNode;
092: rawParserResult.parent = null; // helpful to limit param search in the completion // => is ROOT
093:
094: if (MainEditorFrame.instance.includeRAWCCTree.isSelected()) {
095: //System.out.println("added raw tree");
096: rawParserResultDebug = new MainNode("", "raw tree",
097: UIConstants.red, false);
098: rawParserResultDebug.expandInView = false;
099: //root.add(rawParserResult);
100:
101: try {
102: createDebugRAWTree();
103: } catch (Exception e) {
104: e.printStackTrace();
105: }
106:
107: root.insert(rawParserResultDebug, 0);
108: //rawParserRes.add(rawParserResultDebug);
109:
110: }
111:
112: callAtEndToMakeWarningsVisible();
113: }
114:
115: /** Only on demand.
116: * convert RAW => ParserTreeNode
117: * add to rawParserResultDebug the tree
118: */
119: private void createDebugRAWTree() {
120: createDebugRAWTreeRecurse(rawParserResult, rawParserResultDebug);
121: }
122:
123: @tide.annotations.Recurse
124: private void createDebugRAWTreeRecurse(RAWParserTreeNode src,
125: ParserTreeNode dest) {
126: for (RAWParserTreeNode ci : src.childs) {
127: ParserTreeNode nci = create(ci);
128: dest.add(nci);
129:
130: createDebugRAWTreeRecurse(ci, nci);
131: }
132: }
133:
134: private ParserTreeNode create(RAWParserTreeNode n) {
135: if (n.isToken())
136: return new ParserTreeNode(n.getToken());
137: return new ParserTreeNode(n.toString());
138:
139: }
140:
141: public Collection<TypeNode> getAllTypes() {
142: return allTypes;
143: }
144:
145: public List<TypeNode> getAllTopLevelTypes() {
146: return allTopLevelTypes;
147: }
148:
149: /** Used in project load util.
150: */
151: public static SimplifiedSyntaxTree2 parse2(StringReader sr,
152: String javaName) throws Exception {
153: JavaParser pa = new JavaParser(sr);
154: pa.disable_tracing();
155: RAWSyntaxTree st = new RAWSyntaxTree(javaName);
156: pa.parserOutputProcessor = st;
157:
158: // this may cause exception
159: pa.CompilationUnit();
160:
161: return new SimplifiedSyntaxTree2(st.root, javaName);
162: }
163:
164: public static SimplifiedSyntaxTree2 parse2(FileItem f)
165: throws Exception {
166: StringReader sr = new StringReader(f.getContent());
167: JavaParser pa = new JavaParser(sr);
168: pa.disable_tracing();
169:
170: //long t0 = System.currentTimeMillis();
171: RAWSyntaxTree st = new RAWSyntaxTree(f.getJavaName());
172: pa.parserOutputProcessor = st;
173: pa.CompilationUnit();
174: //System.out.println("\ntime:"+(System.currentTimeMillis()-t0));
175:
176: return new SimplifiedSyntaxTree2(st.root, f.getJavaName());
177: }
178:
179: // TODO: also for enums !!
180: public boolean hasMainMethod() {
181: if (publicTopLevelType == null)
182: return false;
183: if (!(publicTopLevelType instanceof ClassNode))
184: return false;
185: ClassNode cn = (ClassNode) publicTopLevelType;
186:
187: /*OLD if(cn.publicChilds==null || cn.publicChilds.getChildCount()==0) return false;
188: for(int i=0; i<cn.publicChilds.getChildCount(); i++)
189: {
190: ParserTreeNode ptn = cn.publicChilds.getChildNodeAt(i);
191: if(ptn instanceof MethodNode)
192: {
193: MethodNode mptn = (MethodNode) ptn;
194: if(mptn.isPublicStaticMainMethod()) return true;
195: }
196: }
197: */
198: for (int i = 0; i < cn.getChildCount(); i++) {
199: ParserTreeNode ptn = cn.getChildNodeAt(i);
200: if (ptn instanceof MethodNode) {
201: MethodNode mptn = (MethodNode) ptn;
202: if (mptn.isPublicStaticMainMethod())
203: return true;
204: }
205: }
206:
207: return false;
208: }
209:
210: /** Try to help the GC !
211: */
212: public void terminateSST() {
213: if (rawParserResult == null)
214: return; // already terminated... occurs... in the dependencies detection, that "forces" the deletion
215:
216: //MainEditorFrame.debugOut("Terminate sst2 "+this.aprioriJavaName);
217:
218: RAWParserTreeNodeFactory.terminateRecurse(rawParserResult);
219:
220: if (root != null && !root.isterminated) {
221: removeAllNodesRecurse(root);
222: } else {
223: new Throwable("already terminated: " + aprioriJavaName)
224: .printStackTrace();
225: }
226:
227: if (rawParserResultDebug != null
228: && !rawParserResultDebug.isterminated) {
229: removeAllNodesRecurse(rawParserResultDebug);
230: }
231:
232: this .root = null;
233: this .rawParserResult = null;
234: this .rawParserResultDebug = null;
235:
236: // [June2007]: discovered with jHAT
237: this .aprioriJavaName = null;
238: this .importsNode = null;
239: this .packageNode = null;
240: this .publicTopLevelType = null;
241: this .allTopLevelTypes.clear();
242: this .allTypes.clear();
243: this .errorsNode = null;
244: this .parserStateNode = null;
245: this .warningsNode = null;
246:
247: }
248:
249: /** Call terminate on all childs recursively.
250: */
251: private void removeAllNodesRecurse(ParserTreeNode n) {
252: if (n == null)
253: return;
254: int i = 0;
255: while (n.getChildCount() > 0) // IMPORTANT: the node must remove itself from parent, so we have a decrasing count...
256: {
257: i++;
258: if (i > 1000000) {
259: new Throwable("ERROR " + i + " removals and remaining "
260: + n.getChildCount() + " childs")
261: .printStackTrace();
262: break;
263: }
264: ParserTreeNode tn = n.getChildNodeAt(0);
265: tn.terminate();
266: removeAllNodesRecurse(tn);
267: }
268:
269: // [June2007]
270: n.terminate();
271: }
272:
273: public void callAtEndToMakeWarningsVisible() {
274: if (errorsNode.getParent() == null
275: && errorsNode.getChildCount() > 0) {
276: root.insert(errorsNode, 0);
277: }
278:
279: if (warningsNode.getParent() == null
280: && warningsNode.getChildCount() > 0) {
281: root.insert(warningsNode, 0);
282: }
283: }
284:
285: public void removeErrors() {
286: errorsNode.removeAllChildren();
287: }
288:
289: /** Used for the parser error.
290: */
291: @Deprecated
292: public ErrorNode addError_(String e) {
293: ErrorNode en = new ErrorNode(e);
294: errorsNode.add(en);
295: return en;
296: }
297:
298: public void addWarning(WarningNode w) {
299: warningsNode.add(w);
300:
301: //NO only later, because when this is called
302: // one is iterating over the entries of the tree...
303: // callAtEndToMakeWarningsVisible();
304: }
305:
306: public int getWarningsCount() {
307: return warningsNode.getChildCount();
308: }
309:
310: public WarningNode getWarningNodeAt(int i) {
311: return (WarningNode) warningsNode.getChildNodeAt(i);
312: }
313:
314: /* public static void main(String[] args) throws Throwable
315: {
316: TreeExplorer.main(null);
317: }*/
318:
319: /** Extract the package infos, (NO more called, make not sense, everybod knows that
320: * these infos can be read from the top of the file. or even in the tab tooltip.
321: */
322: private void extractPackageAndImportInfos(
323: RAWParserTreeNode compUnitNode, boolean addToView) {
324: for (RAWParserTreeNode ci : compUnitNode.childs) {
325: // these are package and imports statements
326: if (ci.toString().equals("PackageDeclaration")) {
327: packageNode = new PackageNode(ci);
328: if (addToView) {
329: root.add(packageNode);
330: }
331: } else if (ci.toString().equals("ImportDeclaration")) {
332: ImportNode in = new ImportNode(ci);
333: importsNode.add(in);
334: } else {
335: // ?
336: }
337: }
338: }
339:
340: private RAWParserTreeNode getCompilationUnitNode(
341: RAWParserTreeNode completeTreeRoot) {
342: for (RAWParserTreeNode ci : completeTreeRoot.childs) {
343: if (ci.toString().equals("CompilationUnit"))
344: return ci;
345: }
346: throw new RuntimeException("no compilation unit node found !");
347: }
348:
349: /** Look for classes and interfaces AND enums (and annotation interfaces) at top level.
350: */
351: private void scanForTypesAtTopLevel(
352: RAWParserTreeNode compilationUnitNode,
353: ParserTreeNode destination) {
354: for (RAWParserTreeNode ci : compilationUnitNode.childs) {
355: if (ci.toString().equals("TypeDeclaration")) {
356: if (ci.getChildCount() < 2) {
357: String mess = "TypeDeclaration has only "
358: + ci.getChildCount() + " childs"; // ???
359: if (ci.getChildCount() == 1
360: && ci.childs.get(0).toString().equals(";")) {
361: if (!ProblemsSearch.detectUnusefulSemicolons)
362: continue;
363: mess = "Unuseful semicolon";
364:
365: }
366:
367: WarningNode wn = new WarningNode(mess, 2);
368: wn
369: .setStartPosFrom(CCTreeUtils
370: .getFirstSubchild(ci));
371: wn.setEndPosFrom(CCTreeUtils.getFirstSubchild(ci));
372: this .warningsNode.add(wn);
373: continue;
374: }
375:
376: RAWParserTreeNode modNode = ci.childs.get(0);
377: RAWParserTreeNode typeNode = ci.childs.get(1);
378:
379: if (typeNode.toString().equals(
380: "ClassOrInterfaceDeclaration")) {
381: ClassNode cn = new ClassNode(this , typeNode,
382: modNode, destination, null);
383: allTopLevelTypes.add(cn);
384: allTypes.add(cn);
385: } else if (typeNode.toString()
386: .equals("EnumDeclaration")) {
387: EnumNode en = new EnumNode(this , typeNode, modNode,
388: null);
389: destination.add(en);
390: allTopLevelTypes.add(en);
391: allTypes.add(en);
392: } else if (typeNode.toString().equals(
393: "AnnotationTypeDeclaration")) {
394: AnnotationDeclNode adn = new AnnotationDeclNode(
395: this , typeNode, modNode, null);
396: destination.add(adn);
397: allTopLevelTypes.add(adn);
398: allTypes.add(adn);
399: }
400: }
401: }
402:
403: // search for foundPublicJavaPartName
404: for (TypeNode tn : this .allTopLevelTypes) {
405: if (tn.isPublic()) {
406: if (publicTopLevelType != null) {
407: if (ProblemsSearch.detectMultiplePublicTopLevelTypes) {
408: // problem: more than one public java name per file !
409: WarningNode wn = new WarningNode(
410: "More than one public top level type: "
411: + tn.getTypeSimpleName()
412: + " and "
413: + publicTopLevelType
414: .getTypeSimpleName(), 1);
415: wn.setPositionFrom(tn);
416: this .warningsNode.add(wn);
417: }
418: } else {
419: //foundPublicJavaPartName = tn.getTypeName();
420: publicTopLevelType = tn;
421: }
422: }
423: }
424: }
425:
426: // Utils
427: //
428:
429: /** null if none, depth first search.
430: */
431: public TypeInterface getDeepestTypeAt(int line, int col,
432: boolean includeAnonymous) {
433: for (TypeNode tn : this .allTopLevelTypes) {
434: TypeNode found = tn.getDeepestContainingTypeAt(line, col);
435: if (found != null) {
436: // TODO:
437: if (includeAnonymous) {
438: AnonymousType ptn = found
439: .getDeepestContainingAnonymousClassBlocForLocation(
440: line, col);
441: if (ptn != null)
442: return ptn;
443: }
444:
445: return found;
446: }
447: }
448: // not found
449: return null;
450: }
451:
452: }
|