001: package tide.syntaxtree;
002:
003: import snow.utils.CollectionUtils;
004: import java.util.regex.*;
005: import javaparser.*;
006: import tide.editor.*;
007: import tide.sources.*;
008: import snow.concurrent.*;
009: import snow.utils.StringUtils;
010: import java.util.*;
011: import java.io.*;
012: import java.awt.event.*;
013: import javax.swing.*;
014: import javax.swing.text.*;
015: import javax.swing.tree.*;
016:
017: /** Finds dependencies on other sources.
018: * look at referenced (used) classes of other sources of the project (extends, interfaces, enums, variables, ...).
019: * calls the source setDependenciesOnOtherSources() that manages the dependencies graph.
020: *
021: * [Dec2006]: collect also info about main methods and subclasses
022: *
023: * TODO: add all superclasses chain XXX, because they can contain classes YY that can be used in this without
024: * qualifying them (YY insteadof XXX.YY).
025: * other possibility: store all declared types in the SourceFile and allow to locate in the tree.
026: * BUT also the super classes method calls are a kind of dependencies
027: * so it is better to say all superclasses that this exists...
028: *
029: * TODO: must also detect Name nodes Name { "Math", ".", "PI" }, this is the way constants are defined...
030: *
031: * TODO: should also parse the ID's !!!
032: * for example a.b.c.d.e() => depends on b,c,d,e !! even if not directly imported !!
033: *
034: * TODO: in the RAW tree, these are "Expression".
035: *
036: * TODO: staic imports too !
037: */
038: public final class DependenciesDetector {
039: // these are the type names found in the source.
040: // that mean that it these other types changes, this source must be recompiled with... (not the contrary) !
041: private final HashSet<String> typesFound = new HashSet<String>();
042:
043: public static void updateDependencies(final SourceFile sf,
044: final boolean writeOut) {
045: new DependenciesDetector(sf, writeOut, false); //sf.getJavaName().startsWith("test."));
046:
047: //new Throwable().printStackTrace();
048:
049: if (true)
050: return; //TODO
051:
052: //if(!MainEditorFrame.useNewAST) return;
053:
054: // new version !! better, finds more deps !
055: //
056: try {
057: JapaDependenciesDetector ndd = JapaDependenciesDetector
058: .analyseDependencies(sf, null);
059: // debug
060: //if(writeOut)
061: if (sf.sourceFileDependencies.getClassesUsedBy_REF_()
062: .size() != ndd.dependencies.size()) {
063:
064: System.out.println("\n"
065: + sf
066: + " uses "
067: + sf.sourceFileDependencies
068: .getClassesUsedBy_REF_().size()
069: + " other sources, " + ndd.dependencies.size()
070: + " newly");
071: if (CollectionUtils.notIn(
072: sf.sourceFileDependencies
073: .getClassesUsedBy_REF_(),
074: ndd.dependencies).size() > 0) {
075: System.out.println("not in old: "
076: + CollectionUtils.notIn(ndd.dependencies,
077: sf.sourceFileDependencies
078: .getClassesUsedBy_REF_()));
079: System.out.println("not in new: "
080: + CollectionUtils.notIn(
081: sf.sourceFileDependencies
082: .getClassesUsedBy_REF_(),
083: ndd.dependencies));
084: // System.out.println(" to res: "+ndd.expressionsToResolve);
085: }
086: //System.out.println(" "+sf.sourceFileDependencies.getClassesUsedBy_REF());
087: } else {
088: //System.out.println("same deps: "+ndd.dependencies.size()+"");
089: if (!CollectionUtils.sameElements(
090: sf.sourceFileDependencies
091: .getClassesUsedBy_REF_(),
092: ndd.dependencies)) {
093: System.out
094: .println("ERROR in dependencies analysis of "
095: + sf);
096: if (CollectionUtils.notIn(
097: sf.sourceFileDependencies
098: .getClassesUsedBy_REF_(),
099: ndd.dependencies).size() > 0) {
100: System.out
101: .println("not in old: "
102: + CollectionUtils
103: .notIn(
104: ndd.dependencies,
105: sf.sourceFileDependencies
106: .getClassesUsedBy_REF_()));
107: System.out
108: .println("not in new: "
109: + CollectionUtils
110: .notIn(
111: sf.sourceFileDependencies
112: .getClassesUsedBy_REF_(),
113: ndd.dependencies));
114: }
115:
116: }
117: // Todo: really same ?
118: }
119:
120: } catch (Exception e) {
121: e.printStackTrace();
122: }
123: }
124:
125: //private boolean deepDebug=false;
126:
127: // OLD but still used.
128: private DependenciesDetector(final SourceFile sf, boolean writeOut,
129: boolean deepDebug) // for debug mode
130: {
131: // this.deepDebug = deepDebug;
132: final HashSet<SourceFile> dependencies = new HashSet<SourceFile>();
133:
134: if (deepDebug) {
135: System.out.println("\n\n===== Deps for " + sf);
136: }
137:
138: // scan typesFound
139: SimplifiedSyntaxTree2 sst = null;
140: try {
141: // be careful: if being just edited, the tree may be deleted 2 seconds after another has been made...
142:
143: if (MainEditorFrame.instance.editorPanel
144: .getActualDisplayedFile() == sf) {
145: sst = sf.getSimplifiedSyntaxTreeIfAlreadyMade();
146: }
147:
148: if (sst == null) {
149: sst = SimplifiedSyntaxTree2.parse2(sf);
150: sf.setSimplifiedSyntaxTree(sst); // [March2008]: was missing !!!
151: }
152:
153: //sf.setParserResult( );
154: // TEST.
155: //sst = SimplifiedSyntaxTree2.parse(sf);
156: } catch (Exception e) {
157: // return !
158: if (e == null) {
159: new Throwable("NULL").printStackTrace();
160: return;
161: }
162:
163: if (e.getMessage() == null) {
164: System.out.println("NULL message");
165: e.printStackTrace();
166: return;
167: }
168:
169: String errLine = StringUtils.firstLine(e.getMessage());
170:
171: int lineNb = getLineNumber(errLine);
172: int colNb = getColumnNumber(errLine);
173:
174: if (writeOut) {
175: // ?? sst = sf.getSimplifiedSyntaxTreeIfAlreadyMade();
176: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
177: .appendErrorLine(sf.getJavaName() + ".java:"
178: + lineNb + ":" + colNb
179: + ": Cannot parse for dependencies: "
180: + errLine);
181: }
182: return;
183: }
184:
185: // parse is valid and complete
186:
187: RAWParserTreeNode rawTree = sst.getRawParserResult();
188: //NO: skip imports, look only TypeDeclaration
189: for (int i = 0; i < rawTree.getChildCount(); i++) {
190: RAWParserTreeNode ci = rawTree.getChildNodeAt(i);
191: if (ci.toString().equals("TypeDeclaration")) {
192: analyseRecuse(ci);
193: }
194: }
195:
196: if (deepDebug) {
197: System.out.println(" " + typesFound.size()
198: + " typesFound: " + typesFound);
199: }
200:
201: // the source itself does not "depend" on itself.
202: //typesFound.remove( sf.getName() );
203: typesFound.remove(sf.getJavaName());
204:
205: // todo: update in all cases
206: sf.sourceFileDependencies.hasStaticMain = sst.hasMainMethod(); // [Dec2006]
207: //System.out.println("HasMain: "+sf.sourceFileDependencies.hasStaticMain);
208:
209: sf.sourceFileDependencies.getDeclaredTypesNames_REF_().clear();
210: for (final TypeNode tn : sst.allTypes) {
211: sf.sourceFileDependencies.getDeclaredTypesNames_REF_().add(
212: tn.getTypeRelativeName()); //TEST !
213: }
214:
215: sf.topKind = SourceFile.Kind.Unknown;
216: if (sst.publicTopLevelType != null) {
217: if (sst.publicTopLevelType instanceof AnnotationDeclNode) {
218: AnnotationDeclNode ann = (AnnotationDeclNode) sst.publicTopLevelType;
219: //System.out.println("Is an annotation: "+ann.getJavaFullName());
220: sf.topKind = SourceFile.Kind.Annotation;
221: } else if (sst.publicTopLevelType instanceof ClassNode) {
222: ClassNode cn = (ClassNode) sst.publicTopLevelType;
223: if (cn.isInterface) {
224: sf.topKind = SourceFile.Kind.Interface;
225: } else {
226: sf.topKind = SourceFile.Kind.Class;
227: }
228: }
229: }
230:
231: MainEditorFrame.instance.sourcesTreePanel.getTreeModel()
232: .updateAnnotationsCache(sf);
233:
234: // link the type names with types (source or lib) and add the sources to the used sources list
235: //
236: final SourcesTreeModel stm = MainEditorFrame.instance
237: .getActualProject().sourcesTreeModel;
238:
239: List<String> notFound = new ArrayList<String>();
240: int foundUsingImport1 = 0;
241: int foundUsingImport2 = 0;
242:
243: if (deepDebug) {
244: // sf.getSimplifiedSyntaxTreeIfAlreadyMade().
245: }
246:
247: fl: for (final String t : typesFound) {
248: // case sensitive exact match
249: FileItem fi = null;
250: if (t.contains(".")) // otherwise (Small) PROBLEM: finds A.java in the root instead of test.A.java !
251: {
252: fi = stm.quickGet(t, true);
253: }
254:
255: if (fi == null) {
256: fi = TypeLocator.locateUsingImports(sf, t);
257: if (fi != null)
258: foundUsingImport1++;
259: }
260:
261: if (fi == null) {
262: // our identifier is AAA.BB.CC. Just try with AAA.BB and AAA, corresponding to an existing class (hopefully !)
263: String startingName = t;
264: wl: while (startingName.contains(".")) {
265: startingName = StringUtils.removeAfterLastIncluded(
266: startingName, ".");
267: // if(deepDebug) System.out.println("try "+startingName);
268:
269: String ts = StringUtils
270: .extractFromStartUpToFirstExcluded(t, ".");
271: FileItem tfi = TypeLocator.locateUsingImports(sf,
272: startingName);
273: if (tfi != null && tfi.isJavaFile()) {
274: foundUsingImport2++;
275: fi = tfi;
276: break wl;
277: }
278: }
279: }
280:
281: if (fi == null) {
282: // look if it is an inner class ot this source !
283: for (final TypeInterface ttt : sst.allTypes) {
284: if (ttt.getTypeSimpleName().equals(t)) {
285: // ok, we are happy, this is an inner class of the source itself => not a dependency !
286: continue fl;
287: }
288: }
289:
290: // not found !
291: // the type parameters as in ClassA<K> { public Enumeration<K> keys() {...} } may be called
292: // => we then let single letter class names now. (TODO)
293: //
294: if (t.length() == 1)
295: continue fl;
296:
297: // not found !
298: // some times, the searched type is a (at least) protected class or the superclass (extends)
299: // in this case: no problem. but we write it out
300: // also a lot of noise comming from the names.
301: notFound.add(t);
302:
303: // if(deepDebug) System.out.println(" not found:"+t);
304:
305: if (writeOut) {
306:
307: //MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc.appendErrorLine(sf.getJavaName()+".java:: (just a debug information): cannot locate type: "+t);
308: //MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc.appendLine(""+sst.allTypes.size()+" declared types="+sst.allTypes);
309: }
310:
311: }
312:
313: if (fi != null) {
314: if (fi.isDirectory()) {
315: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
316: .appendErrorLine(sf.getJavaName()
317: + ".java: not a type: " + t);
318: } else if (fi instanceof SourceFile) {
319: dependencies.add((SourceFile) fi);
320: }
321: }
322: }
323:
324: // remove dependences on itself. (may have been resolved in a chain ...)
325: dependencies.remove(sf);
326:
327: if (writeOut || deepDebug) {
328: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
329: .append("\nUses "
330: + dependencies.size()
331: + " sources (old: "
332: + sf.sourceFileDependencies
333: .getClassesUsedBy_REF_().size()
334: + "): ");
335: for (final SourceFile sfi : dependencies) {
336: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
337: .append(sfi.getJavaName() + ", ");
338: }
339:
340: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
341: .appendLine(" (is used by "
342: + sf.sourceFileDependencies
343: .getClassesUsingThis_REF_().size()
344: + ", found=" + typesFound.size() + ", not="
345: + notFound.size() + ", ["
346: + foundUsingImport1 + ", "
347: + foundUsingImport2 + "])");
348:
349: /*if(MainEditorFrame.debug)
350: {
351: System.out.println(""+notFound.size()+" not resolved types candidates (not an error, just debug info!):\n"+notFound);
352: }*/
353: }
354:
355: // TODO: also tell all declared types to the source. this may be very useful later for exact type search !!
356: if (deepDebug) {
357: System.out.println("deps=" + dependencies);
358: }
359:
360: // Here we do the job to tell to "File" that it uses "String"
361: sf.sourceFileDependencies
362: .setDependenciesOnOtherSources_(dependencies);
363:
364: // GC!
365: //dependencies.clear();
366: typesFound.clear();
367:
368: if (MainEditorFrame.instance.editorPanel
369: .getActualDisplayedFile() != sf) {
370: if (sst != null) { // [June2007]
371: sst.terminateSST();
372: }
373:
374: sf.setSimplifiedSyntaxTree(null);
375: }
376: /*else
377: {
378: System.out.println("Keep sst of "+sf.getJavaName());
379: }*/
380: }
381:
382: /** Look for types in the RAW tree, recursively.
383: */
384: @tide.annotations.Recurse
385: private void analyseRecuse(final RAWParserTreeNode node) {
386: //nodeCount++;
387: for (int i = 0; i < node.getChildCount(); i++) {
388: RAWParserTreeNode ni = node.getChildNodeAt(i);
389:
390: if (ni.getChildCount() > 0) {
391: if (ni.toString().equals("ClassOrInterfaceType")) {
392: // The childs are for example "ClassA", ".", "InnerClassB"
393: // or "javax.swing.JFrame"
394: String typeName = getClassOrInterfaceTypeTypeName(ni); //Utils.getImageOfAllSubElements(ni); // ni.getChildNodeAt(0).toString()
395: typesFound.add(typeName);
396: } else if (ni.toString().equals("Name")) {
397: // be careful: the variables also appear here :-(
398: // but we need to check, because the Interfaces and static access for variables are so, as in Math.PI
399:
400: // BAD: all constants ot this source are also there...
401: // and imports => avoid...
402:
403: String name = CCTreeUtils
404: .getImageOfAllSubElements(ni); // may also direct add "name", but there are a lot of "noise"
405: typesFound.add(name); //no, keep them [Oct2007] ( also finds throws ExceptionXX, ...)
406:
407: /* OLD
408: if(name.indexOf('.')>0) // the names doesn't appear "raw" ??
409: {
410: name = StringUtils.removeAfterLastIncluded(name, ".");
411: }
412: else
413: {
414: name = ""; // HACK: only consider XXX in XXX.YY and ignore if only XXX appears ! (TODO)
415: }
416:
417: if(name.length()>0 && Character.isUpperCase(name.charAt(0))) // HACK !, we may maintain a list of classes named with lowercase ! (TODO)
418: {
419: typesFound.add( name );
420: }*/
421: //System.out.println("Name = "+name);
422: } else if (ni.toString().equals("Expression")) {
423: //System.out.println("Expr: "+Utils.getImageOfAllSubElements(ni));
424: // TODO.
425: }
426:
427: // also recurse if found !
428: analyseRecuse(ni);
429: } // end of if have childs
430:
431: }
432: }
433:
434: /** the type node may contain params, as in java.lang.Vector<Object>, we want only "java.lang.Vector" here
435: */
436: private String getClassOrInterfaceTypeTypeName(RAWParserTreeNode nn) {
437: StringBuilder sb = new StringBuilder();
438: fl: for (int i = 0; i < nn.getChildCount(); i++) {
439: RAWParserTreeNode ci = nn.getChildNodeAt(i);
440: if (ci.getChildCount() > 0) {
441: break fl;
442: }
443: sb.append(ci.toString());
444: }
445: return sb.toString();
446: }
447:
448: private Pattern lineNumberPattern = Pattern
449: .compile("line\\s(\\d*)");
450: private Pattern columnNumberPattern = Pattern
451: .compile("column\\s(\\d*)");
452:
453: private int getLineNumber(String line) {
454: Matcher m = lineNumberPattern.matcher(line);
455:
456: if (m.find()) {
457: return Integer.parseInt(m.group(1));
458: } else {
459: return -1;
460: }
461: }
462:
463: private int getColumnNumber(String line) {
464: Matcher m = columnNumberPattern.matcher(line);
465:
466: if (m.find()) {
467: return Integer.parseInt(m.group(1));
468: } else {
469: return -1;
470: }
471: }
472:
473: }
|