0001: package tide.classsyntax;
0002:
0003: import tide.syntaxtree.ParserResult;
0004: import japa.parser.ast.ImportDeclaration;
0005: import snow.html.HTMLUtils;
0006: import tide.utils.ParsedID;
0007: import tide.sources.FileItem;
0008: import snow.texteditor.*;
0009: import tide.syntaxtree.SearchFunctions;
0010: import tide.sources.TypeLocator;
0011: import tide.project.ClassFilesManager;
0012: import tide.editor.MainEditorFrame;
0013: import tide.project.ProjClass;
0014: import tide.utils.SyntaxUtils;
0015: import tide.syntaxtree.SimplifiedSyntaxTree2;
0016: import javaparser.*;
0017: import java.util.*;
0018: import java.lang.reflect.*;
0019: import java.util.regex.Pattern;
0020:
0021: /** Resolves the types for a parsed ID chain as in
0022: * <pre> "frame.add(" => "javax.swing.jframe" and method "add"</pre>
0023: * Is called for completion purposes with an ID parsed before a "." or a "(".
0024: *
0025: * TODO: lazy search for local variables when no syntax tree available.
0026: */
0027: public final class IDChain {
0028: // also contains info about preceeding item ("new", " ", "(", "{", "import", "\""...)
0029: public final ParsedID pid;
0030: public final FileItem source;
0031: // let know what was pressed to trigger this resolver. The pid cannot know it because it
0032: // has not been entered yet in the document. In case of "(" this allow to know that we
0033: // are in a method or a constructor at the last chain element
0034: public final String enteredItem;
0035: public final SimpleDocument doc;
0036: public final int posInSource;
0037:
0038: private TypeInterface deepestTypeAtPos = null;
0039: private String[] splittedChain;
0040:
0041: // the recognized chain
0042: public final List<IDChainElement> chain = new ArrayList<IDChainElement>();
0043: private boolean isValid = true;
0044:
0045: private final SingleClassLoader scl;
0046:
0047: private int numberOfEltsTakenAsFirst = 1;
0048: private final boolean constructorMode; // iff new preceeds
0049: private boolean isConstructorAlreadyFound = false; // only one can appear, for example in "new Throwable().printStackTrace()", the second is a method !
0050:
0051: /** @param enteredItem is "." or "(" it allow to determine if the last element is a field or a method or constructor.
0052: * because the elt is not already in the document and then not in pid.
0053: */
0054: public IDChain(ParsedID pid, FileItem source, SimpleDocument doc,
0055: int posInSource, String enteredItem, SingleClassLoader scl) {
0056: this .pid = pid;
0057: this .source = source;
0058: this .doc = doc;
0059: this .posInSource = posInSource;
0060: this .enteredItem = enteredItem;
0061: this .scl = scl;
0062:
0063: this .constructorMode = pid.precedingItem.equals("new");
0064:
0065: //System.out.println("\n=== Parsing "+pid);
0066:
0067: // parse the chain...
0068: parseChain();
0069: }
0070:
0071: /** Parses the whole chain.
0072: */
0073: private void parseChain() {
0074: String idchainStr = pid.identifierChain;
0075: if (enteredItem.equals("(")) {
0076: idchainStr = idchainStr + "()";
0077: }
0078:
0079: splittedChain = idchainStr.split(Pattern.quote(".")); // BAD, do it manually...
0080:
0081: int n = 0;
0082: IDChainElement ide;
0083:
0084: if (pid.precedingItem != null
0085: && pid.precedingItem.endsWith("\"")) {
0086: System.out.println("special string mode: chain="
0087: + splittedChain.length);
0088: ide = new IDChainElement(this , "java.lang.String", null,
0089: false, true); // equals "new String(""))."
0090: chain.add(ide);
0091: ide.setTypeWithParamsAndResolve("java.lang.String");
0092: //ide.setIsDirectClassCall();
0093: isValid = true;
0094: }
0095:
0096: il: for (String idi : splittedChain) {
0097: if (idi.trim().length() == 0)
0098: continue il; // occurs !
0099:
0100: // fetch preparsed type vars
0101: String typeVars = null;
0102: if (idi.indexOf("{TV:") >= 0) {
0103: int st = idi.indexOf("{TV:");
0104: int en = idi.indexOf('}', st + 1);
0105: if (en >= 0) {
0106: String tn = idi.substring(st + 4, en);
0107:
0108: typeVars = pid.typeVariablesReplacements.get(tn); // without < >
0109: debugOut("typeVars=" + typeVars + " for " + tn
0110: + " in " + idi + ", map="
0111: + pid.typeVariablesReplacements);
0112: // remove the type variables from the idi
0113: idi = idi.substring(0, st) + idi.substring(en + 1);
0114: //debugOut("idi="+idi);
0115: }
0116: }
0117:
0118: // fetch preparsed class cast info
0119: boolean isClassCast = false; // since [April2007]
0120: if (idi.startsWith("{C:")) {
0121: int st = idi.indexOf("{C:");
0122: int en = idi.indexOf('}', st + 1);
0123: String cn = pid.castsReplacements.get(idi.substring(
0124: st + 3, en));
0125:
0126: isClassCast = true;
0127: idi = idi.substring(0, st) + cn + idi.substring(en + 1);
0128: }
0129:
0130: // sometimes (when type vars are in casts), a cast remains in idi
0131: if (idi.indexOf('<') > 0) {
0132: if (typeVars != null) {
0133: new Throwable("strange " + idi
0134: + " already has type vars " + typeVars)
0135: .printStackTrace();
0136: }
0137: int st = idi.indexOf('<');
0138: int en = SyntaxUtils
0139: .matchingEndBrace(idi, st, '<', '>');
0140: if (en > 0) {
0141: typeVars = idi.substring(st + 1, en);
0142: idi = idi.substring(0, st) + idi.substring(en + 1);
0143: }
0144: }
0145:
0146: n++;
0147: debugOut("Parse id element: " + idi
0148: + (isClassCast ? ", iscast" : ""));
0149: if (!SyntaxUtils.isValidIdentifier(idi)) {
0150: isValid = false;
0151: debugOut("### not valid id: " + idi);
0152: //new Throwable().printStackTrace();
0153: return;
0154: }
0155:
0156: ide = null;
0157: if (n == 1) // first
0158: {
0159: // maybe take more than one idi ! when absolute refs as in "javax.swing.JFrame"
0160: boolean enforceMethod = (n >= splittedChain.length && enteredItem
0161: .equals("("));
0162: ide = locateFirstElement(idi, enforceMethod, false,
0163: isClassCast); // no approximation here !
0164: } else {
0165: // ignore because was taken in account in the first elt.
0166: if (n <= numberOfEltsTakenAsFirst) {
0167: debugOut("Ignore " + idi);
0168: continue il;
0169: }
0170:
0171: //
0172: boolean enforceMethod = (n + numberOfEltsTakenAsFirst
0173: - 1 >= splittedChain.length && enteredItem
0174: .equals("("));
0175:
0176: ide = locateNextElement(idi, enforceMethod, isClassCast);
0177: }
0178:
0179: if (ide == null) {
0180: debugOut("### ide is null => not valid chain");
0181: isValid = false;
0182: return;
0183: }
0184:
0185: if (typeVars != null) {
0186: ide.setTypeParameters(typeVars);
0187: }
0188:
0189: chain.add(ide);
0190:
0191: } // end of chain loop
0192: }
0193:
0194: /** @return null if none. Used in the completion and popups.
0195: */
0196: public IDChainElement getLastChainComponent() {
0197: if (chain.isEmpty())
0198: return null;
0199: return chain.get(chain.size() - 1);
0200: }
0201:
0202: /** Field is also used for direct class call.
0203: * Method is also used for Constructors.
0204: */
0205: public FileItem getDeclaringClassForMethodOrField(
0206: IDChainElement methodOrField) {
0207: if (methodOrField.getKind() == ElementKind.Method) {
0208: return getDeclaringClassForMethod(methodOrField);
0209: } else if (methodOrField.getKind() == ElementKind.Field) {
0210: return getDeclaringClassForField(methodOrField);
0211: }
0212: return null;
0213: }
0214:
0215: /** May return null if not found or on errors.
0216: * reference field receives the reflected object, if found.
0217: * The field may be a direct inner class call, as in test.A$CCC [Feb2008]
0218: */
0219: public FileItem getDeclaringClassForField(final IDChainElement field) {
0220: if (field.reflectedObject != null) {
0221: if (field.reflectedObject instanceof Field) {
0222: Field f = (Field) field.reflectedObject;
0223: try {
0224: return this .locateTypeExact(f.getDeclaringClass()
0225: .getName());
0226: } catch (Exception ex) {
0227: ex.printStackTrace();
0228: }
0229: } else {
0230: debugOut("ERR: not a field: " + field);
0231: }
0232: }
0233:
0234: RType ret = new RType();
0235:
0236: int pos = chain.indexOf(field);
0237:
0238: // simple case, the source where this chain was issued is the class declaring the method or field
0239: if (pos == 0 || chain.isEmpty()) {
0240: ret.fitem = source;
0241: } else if (pos == -1) {
0242: // called from IDChainElement, if not already added, this is the previous = last component
0243: IDChainElement parent = chain.get(chain.size() - 1);
0244: ret.copyFrom(parent.resolvedType_);
0245: } else {
0246: IDChainElement parent = chain.get(pos - 1);
0247: ret.copyFrom(parent.resolvedType_);
0248: }
0249:
0250: try {
0251: if (!ret.isResolved()) {
0252: throw new NullPointerException(
0253: "getDeclaringClassForField(" + field.getName()
0254: + ") is not resolved (pos=" + pos
0255: + ", " + field + ")");
0256: }
0257:
0258: Class cla = ret.rclass;
0259: if (cla == null) {
0260: cla = scl.findClass(ret.fitem.getJavaName());
0261: }
0262:
0263: if (cla != null) {
0264: for (Field f : cla.getFields()) {
0265: if (f.getName().startsWith(field.getName())) {
0266: // remember !
0267: field.reflectedObject = f;
0268: return this .locateTypeExact(f
0269: .getDeclaringClass().getName());
0270: }
0271: }
0272:
0273: // not found, try inner classes
0274: for (Class ci : cla.getDeclaredClasses()) {
0275: System.out.println("Looking at " + ci.getName());
0276: if (ci.getName().endsWith(field.getName())) {
0277: System.out.println("Found !");
0278: field.resolvedType_.rclass = ci; // ??
0279: field.setIsDirectClassCall(); // yeah, it solves some wrong a-priori suppositions
0280: // returns null, no problem in case of inner classes, because they are not FileItem's...
0281: }
0282: }
0283:
0284: // declared fields... and in super (TODO...)
0285: debugOut("getDeclaringClassForField: "
0286: + field.getName() + " not found in " + cla);
0287: debugOut(" rtype=" + field.resolvedType_);
0288: debugOut(" ret = " + ret);
0289: //new Throwable().printStackTrace();
0290:
0291: }
0292: } catch (Exception e) {
0293: e.printStackTrace();
0294: } catch (Error e) {
0295: e.printStackTrace();
0296: } // VerifyError
0297:
0298: return ret.fitem;
0299: }
0300:
0301: /** The class declaring the method, taken from the previous chain component.
0302: * [Mar2008]: was not ok for method that has a field as parent...
0303: */
0304: public FileItem getDeclaringClassForMethod(IDChainElement method) {
0305: if (method.reflectedObject != null) {
0306: if (method.reflectedObject instanceof Method) {
0307: Method met = (Method) method.reflectedObject;
0308: try {
0309: return this .locateTypeExact(met.getDeclaringClass()
0310: .getName());
0311: } catch (Exception ex) {
0312: ex.printStackTrace();
0313: }
0314: } else {
0315: debugOut("ERR: REF not a method: " + method);
0316: }
0317: }
0318:
0319: FileItem fit = null;
0320:
0321: IDChainElement parent = null;
0322: int pos = chain.indexOf(method);
0323:
0324: // simple case, the source where this chain was issued is the class declaring the method or field
0325: if (pos == 0 || chain.isEmpty()) {
0326: fit = source;
0327: } else if (pos == -1) {
0328: // called from IDChainElement, if not already added, this is the previous = last component
0329: parent = chain.get(chain.size() - 1);
0330: fit = parent.resolvedType_.fitem;
0331: } else {
0332: parent = chain.get(pos - 1);
0333: //System.out.println("Parent is "+parent);
0334: //System.out.println("Parent type = "+parent.resolvedType_);
0335: fit = parent.resolvedType_.fitem;
0336: }
0337:
0338: Class cla = null;
0339: if (parent != null) {
0340: cla = parent.resolvedType_.rclass;
0341: }
0342:
0343: if (fit != null && cla == null) {
0344: try {
0345: cla = scl.findClass(fit.getJavaName()); // not loading !
0346: } catch (Exception e) {
0347: e.printStackTrace();
0348: } catch (Error e) {
0349: e.printStackTrace();
0350: } // sometimes VerifyError !!
0351: }
0352:
0353: if (cla != null) {
0354: try {
0355: //ClassUtils.lookAtSource(cla); // prints signatures, ...
0356: if (cla != null) {
0357: for (Method m : cla.getMethods()) {
0358: if (m.getName().startsWith(method.getName())) {
0359: // remember
0360: method.reflectedObject = m;
0361: return this .locateTypeExact(m
0362: .getDeclaringClass().getName());
0363: }
0364: }
0365: }
0366: } catch (Exception e) {
0367: e.printStackTrace();
0368: } catch (Error e) {
0369: e.printStackTrace();
0370: } // sometimes VerifyError !!
0371: } else {
0372: System.out.println("NO FileItem for method " + method);
0373: //new Throwable().printStackTrace();
0374: }
0375:
0376: return fit;
0377: }
0378:
0379: /** use for later types (not the first of a chain).
0380: * they may be located outside imports scope, for example
0381: * when resolving a method declared in some parent.
0382: */
0383: private FileItem locateTypeGeneralApprox(String name) {
0384: return TypeLocator.searchTypeForName(name, true, true); // ignore case & ends with
0385: }
0386:
0387: /** To use when one knows the type (class) name exactely, as in javax.swing.JFrame
0388: */
0389: private FileItem locateTypeExact(String exactJavaName) {
0390: return TypeLocator.locateQuick(exactJavaName);
0391: }
0392:
0393: /** To use for the first element (that lies in the source).
0394: */
0395: public FileItem locateTypeUsingImports(final String name) {
0396: final String _name = SyntaxUtils.removeAllArrayPartsAtEnd(name);
0397:
0398: FileItem it = TypeLocator.locateUsingImports(source, _name);
0399:
0400: if (it == null) {
0401: //NO return locateTypeGeneralApprox(name);
0402: }
0403:
0404: return it;
0405: /*
0406: // convenience, use the approximate search for this.
0407:
0408: //return locateTypeGeneralApprox(name);
0409:
0410: // old
0411: final SimplifiedSyntaxTree2 sst = source.getSimplifiedSyntaxTreeIfAlreadyMade();
0412: if(sst!=null)
0413: {
0414: System.out.println("Looking approx for "+name);
0415: // convenience, use the approximate search for this.
0416: return locateTypeGeneralApprox(name);
0417: }
0418: else
0419: {
0420: return TypeLocator.locateUsingImports(source, name);
0421: }*/
0422: }
0423:
0424: /** To use for the first element (that lies in the source).
0425: */
0426: public FileItem locateTypeUsingStaticImports(String name,
0427: boolean isMethod, SingleClassLoader scl) {
0428: name = SyntaxUtils.removeAllArrayPartsAtEnd(name);
0429:
0430: if (source.getParserResultIfAlreadyMade() != null
0431: && source.getParserResultIfAlreadyMade().compilationUnit != null) {
0432: return TypeLocator.locateUsingStaticImports(source
0433: .getParserResultIfAlreadyMade(), scl, name,
0434: isMethod, source.getPackageName());
0435: }
0436: return null;
0437: /* OLD
0438: SimplifiedSyntaxTree2 sst = source.getSimplifiedSyntaxTreeIfAlreadyMade();
0439: if(sst==null) return null;
0440: ParserTreeNode importsNode = sst.importsNode;
0441: return TypeLocator.locateUsingStaticImports(importsNode, scl, name, isMethod, source.getPackageName());
0442: */
0443: }
0444:
0445: /** Advantage: also finds the inner classes !
0446: * @param name is for example "String"
0447: * @return for example "java.lang.String" or null if not found, with "$" for inner classes ! (???)
0448: */
0449: private String locateClassUsingImports(String name) {
0450: name = SyntaxUtils.removeAllArrayPartsAtEnd(name);
0451: debugOut("locateClassUsingImports(" + name + ")");
0452:
0453: ClassFilesManager cfm = MainEditorFrame.instance
0454: .getActualProject().getClassFilesManager();
0455: // when proj not fully loaded
0456: if (cfm == null)
0457: return null;
0458:
0459: //1) direct match:
0460: ProjClass pc = cfm.getClassExact(name);
0461: if (pc != null)
0462: return pc.getJavaName();
0463:
0464: // 2) try to locate the class as an inner class or the source !
0465: String cn = this .source.getJavaName() + "$" + name; // full resolved !
0466: pc = cfm.getClassExact(cn);
0467: if (pc != null)
0468: return cn; // with the $
0469:
0470: pc = cfm.getClassExact("java.lang." + name);
0471: if (pc != null)
0472: return pc.getJavaName();
0473:
0474: // 3) try using imports...
0475: /*OLD
0476: SimplifiedSyntaxTree2 sst = source.getSimplifiedSyntaxTreeIfAlreadyMade();
0477: if(sst!=null)
0478: {
0479: ParserTreeNode importsNode = sst.importsNode;
0480: for(int i=0; i<importsNode.getChildCount(); i++)
0481: {
0482: String im = ((ImportNode) importsNode.getChildNodeAt(i)).getImportName();
0483: // either "xxx.yyy.*" or "xxx.yyy.Z"
0484: if(im.endsWith(".*"))
0485: {
0486: pc = cfm.getClassExact(im.substring(0,im.length()-2)+"."+name);
0487: if(pc!=null) return pc.getJavaName();
0488: }
0489: else
0490: {
0491: if(im.endsWith("."+name)) // THE DOT IS important, otherwise ZipFile will complete for File!
0492: {
0493: pc = cfm.getClassExact(im);
0494: if(pc!=null) return pc.getJavaName();
0495: }
0496: }
0497: }
0498: }*/
0499:
0500: final ParserResult pr = source.getParserResultIfAlreadyMade();
0501: if (pr != null && pr.compilationUnit != null
0502: && pr.compilationUnit.imports != null) {
0503: for (final ImportDeclaration it : pr.compilationUnit.imports) {
0504: if (it.isAsterisk) {
0505: pc = cfm.getClassExact("" + it.name + "." + name);
0506: if (pc != null)
0507: return pc.getJavaName();
0508: } else {
0509: String im = "" + it.name;
0510: if (im.endsWith("." + name)) // THE DOT IS important, otherwise ZipFile will complete for File!
0511: {
0512: pc = cfm.getClassExact(im);
0513: if (pc != null)
0514: return pc.getJavaName();
0515: }
0516: }
0517:
0518: }
0519: }
0520:
0521: //TODO: look in package
0522:
0523: // not found, sorry...
0524: // maybe perform an approximate search,
0525: // name ending with, and then with name appxox equals (edit distance, soudex, metaphone, ...)
0526: return null;
0527:
0528: }
0529:
0530: private String removeArgAndArrayPartAtEnd(String name) {
0531: int pos = name.indexOf('(');
0532: if (pos > 0)
0533: name = name.substring(0, pos);
0534: pos = name.indexOf('[');
0535: if (pos > 0)
0536: name = name.substring(0, pos);
0537: return name;
0538: }
0539:
0540: /** The most difficult and important... based on the syntax tree, if available
0541: * also uses the imports to locate the correct type !
0542: * @param allowAproximateSearch should be false. If not found, a right click in the editor will also
0543: * propose adding mising imports...
0544: */
0545: private IDChainElement locateFirstElement(final String name_,
0546: boolean enforceMethod, boolean allowAproximateSearch,
0547: boolean isClassCast) {
0548: MainEditorFrame.debugOut("Locate first elt: " + name_ + " (em:"
0549: + enforceMethod + ")");
0550: IDChainElement ide = createIDElement(name_, enforceMethod);
0551: ide.isClassCast = isClassCast;
0552: SimplifiedSyntaxTree2 sst = source
0553: .getSimplifiedSyntaxTreeIfAlreadyMade();
0554:
0555: final String nameToSearch = removeArgAndArrayPartAtEnd(name_); // was remo only array !
0556: if (sst == null) {
0557: // todo: locate lazy ?? use regex and other string searches
0558: debugOut("no syntax tree");
0559: } else {
0560: //ParserTreeNode importsNode = sst.importsNode;
0561: final int[] lineCol = DocumentUtils.getLineColumnNumbers(
0562: doc, posInSource);
0563: //final int col = DocumentUtils.getColumnNumber(doc, posInSource);
0564: deepestTypeAtPos = sst.getDeepestTypeAt(lineCol[0] + 1,
0565: lineCol[1] + 1, true);
0566: //System.out.println("Deepest type: " + deepestTypeAtPos.getTypeName());
0567: //getFirstNodeBeforePosition or getNodeTokenContaining
0568: String type = null; // not found now !
0569:
0570: if (pid.precedingItem.equals("\"")) {
0571: type = "java.lang.String";
0572: }
0573:
0574: // 0) special cases: this and super and class
0575: if (type == null && nameToSearch.equals("this")) {
0576: if (deepestTypeAtPos != null) {
0577: if (source.getPackageName().length() > 0) {
0578: // [Feb2008]: made correct !
0579: //System.out.println("deepestTypeAtPos="+deepestTypeAtPos.getTypeSimpleName()); // "ClassA"
0580: //System.out.println("source="+source.getJavaPartName()+ " ("+source.getJavaName()+")"); // test.ClassA
0581:
0582: if (deepestTypeAtPos.getTypeSimpleName()
0583: .equals(source.getJavaPartName())) {
0584: // ok if we are IN the toppest class
0585: type = source.getPackageName()
0586: + "."
0587: + deepestTypeAtPos
0588: .getTypeSimpleName();
0589: } else {
0590: type = source.getJavaName()
0591: + "$"
0592: + deepestTypeAtPos
0593: .getTypeSimpleName();
0594: }
0595:
0596: //System.out.println(""+source.getPackageName()+" + "+deepestTypeAtPos.getTypeSimpleName());
0597: } else {
0598: type = deepestTypeAtPos.getTypeSimpleName(); // todo: maybe false if inner chained classes
0599: }
0600: } else {
0601: debugOut("No deepest type found => using src instead of deepest type for this");
0602: type = source.getJavaName();
0603: }
0604:
0605: if (name_.indexOf('(') > 0) {
0606: ide.setIsConstructorCall(type);
0607: }
0608:
0609: //System.out.println("type found for \"this\" = "+type);
0610: } else if (type == null && nameToSearch.equals("super")) {
0611: if (deepestTypeAtPos != null) {
0612: if (deepestTypeAtPos instanceof ClassNode) {
0613: ClassNode cn = (ClassNode) deepestTypeAtPos;
0614: if (cn.extendsText != null) {
0615: type = cn.extendsText;
0616: } else {
0617: //System.out.println("No deepest type found => using this insteadof super");
0618: type = "java.lang.Object";
0619: }
0620: } else {
0621: MainEditorFrame
0622: .debugOut("Cannot find super from not classnode "
0623: + deepestTypeAtPos);
0624: }
0625: } else {
0626: type = source.getJavaName();
0627: // TODO: locate super through class !
0628: }
0629:
0630: if (name_.indexOf('(') > 0) {
0631: ide.setIsConstructorCall(type);
0632: }
0633:
0634: }
0635:
0636: if (ide.getKind() == ElementKind.Constructor) {
0637: String typeName = nameToSearch;
0638: int pos = typeName.indexOf('(');
0639: if (pos >= 0) {
0640: typeName = typeName.substring(0, pos);
0641: }
0642: // localize !
0643: type = this .locateClassUsingImports(typeName);
0644: }
0645:
0646: // 1) local variables (loc decl, in bloks, in loops, ...)
0647: // this search backward and only for variables preceeding !
0648: if (type == null) {
0649: if (sst.getRawParserResult() != null) {
0650: RAWParserTreeNode found = TreeUtils
0651: .getTokenNodeAtOrAfterPosition(sst
0652: .getRawParserResult(),
0653: lineCol[0] + 1, // [Dec2007]: added "+1" because completion was not available for elements on the line after method param variable !
0654: lineCol[1]);
0655: //System.out.println("Some node near: "+found);
0656: type = TreeUtils.searchTypeForVariableName(found,
0657: nameToSearch,
0658: ide.getKind() == ElementKind.Method);
0659:
0660: if (type == null) {
0661: debugOut(" is not a variable name.");
0662: }
0663: } else {
0664: debugOut("No raw syntax tree => cannot detect local variables");
0665: }
0666: }
0667:
0668: boolean met = enforceMethod
0669: || ide.getKind() == ElementKind.Method
0670: || ide.getKind() == ElementKind.Constructor;
0671:
0672: if (type == null) {
0673: // 2) methods or fields "frame"
0674:
0675: type = searchForTypeOfMethodOrField(ide, sst, met, !met); // either field or method, we know. (or cons or stat class)
0676:
0677: if (type == null) {
0678: debugOut(" "
0679: + ide.getName()
0680: + " is not a field or method in this syntax tree (m="
0681: + met + ")");
0682:
0683: // [Feb2008], yeah, finds inner classes !
0684: for (TypeNode tn : sst.getAllTopLevelTypes()) {
0685: if (tn.getJavaFullName().endsWith(nameToSearch)) {
0686: debugOut(" Candidate: toplevel type "
0687: + tn.getJavaFullName() + " for "
0688: + nameToSearch);
0689: //return tn.getJavaFullName();
0690: //type = tn.getJavaFullName();
0691: if (ide.getKind() == ElementKind.Method
0692: || ide.getKind() == ElementKind.Field) // Now we knows that this was NOT a method call but a direct class call. "CCC."
0693: {
0694: ide.setIsDirectClassCall();
0695: }
0696: //System.out.println(""+ ide.resolvedType_);
0697: ide.resolvedType_.rclass = scl.findClass(tn
0698: .getJavaFullName());
0699: //System.out.println("RTYPE of elt: "+ ide.resolvedType_);
0700: }
0701: }
0702:
0703: }
0704: }
0705:
0706: // [May2007]: use reflection
0707: if (type == null) {
0708: String deepestTypeName = source.getJavaName();
0709: if (deepestTypeAtPos != null) {
0710: if (source.getPackageName().length() > 0) {
0711: deepestTypeName = source.getPackageName() + "."
0712: + deepestTypeAtPos.getTypeSimpleName(); // todo: chained inner classes
0713: } else {
0714: deepestTypeName = deepestTypeAtPos
0715: .getTypeSimpleName();
0716: }
0717: }
0718: Class this Cl = getClassForName(deepestTypeName);
0719: if (this Cl != null) {
0720: if (met) {
0721: type = ClassUtils.getTypeForMethodInClass(
0722: this Cl, ide.getName(), null); // TODO: pass type params
0723: } else {
0724: //
0725: Class typeCl = ClassUtils
0726: .getTypeForFieldInClass(this Cl, ide
0727: .getName());
0728: if (typeCl != null) {
0729: type = typeCl.getName();
0730: ide.resolvedType_.rclass = typeCl;
0731: // Here some are erroneous marked as Field instead of Class call... (TODO).
0732: }
0733: }
0734:
0735: if (type == null) {
0736: debugOut(" "
0737: + ide.getName()
0738: + " is not a field or method in this class.");
0739: }
0740: } else {
0741: debugOut(" class not found for deepestTypeName="
0742: + deepestTypeName);
0743: }
0744: }
0745:
0746: if (type == null) {
0747: // 3) absolute referenced type, for example "javax.swing.JFrame"
0748: // TODO: use splittedChain and tell what remains !
0749: final StringBuilder id = new StringBuilder(nameToSearch);
0750: tl: for (int i = 1; i < splittedChain.length; i++) {
0751: id.append(".");
0752: id.append(splittedChain[i]);
0753: FileItem fi = TypeLocator.searchTypeForName(id
0754: .toString(), false, false); // exact look
0755: if (fi != null) {
0756: type = fi.getJavaName();
0757: numberOfEltsTakenAsFirst = i + 1;
0758: //System.out.println("Absolute type found: "+type);
0759: ide.setIsDirectClassCall();
0760: ide.resolvedType_.fitem = fi;
0761: break tl;
0762: }
0763: }
0764: }
0765:
0766: if (type == null) {
0767: // 4) raw type (class), for example "JFrame"
0768: FileItem fi = this .locateTypeUsingImports(nameToSearch);
0769:
0770: if (fi != null) {
0771: //System.out.println("Type found (using imp): "+fi);
0772: //BAD ide.setIsDirectClassCall(); // [Nov2007]: ERROR: some project constructors are here !
0773:
0774: if (this .isConstructorMode()) {
0775: // what ix "new XXX.YYY.ZZZ()" ??
0776: ide.setIsConstructorCall(fi.getJavaName());
0777: } else {
0778: ide.setIsDirectClassCall();
0779: }
0780:
0781: type = fi.getJavaName();
0782: ide.resolvedType_.fitem = fi;
0783: }
0784: }
0785:
0786: // try to locate the class from classmanager (finds also inner classes !)
0787: // TODO: finds corresponding FileItem ?
0788: if (type == null) {
0789: type = this .locateClassUsingImports(nameToSearch);
0790: if (type != null) {
0791: debugOut("Type found (using imp cla): " + type);
0792: //BAD[Nov2007]: ide.setIsDirectClassCall();
0793:
0794: if (this .isConstructorMode()) {
0795: // what ix "new XXX.YYY.ZZZ()" ??
0796: ide.setIsConstructorCall(type);
0797: } else {
0798: ide.setIsDirectClassCall();
0799: }
0800:
0801: try {
0802: ide.reflectedObject = scl.findClass(type); // not load !
0803: } catch (Exception e) {
0804: e.printStackTrace();
0805: }
0806: }
0807: }
0808:
0809: if (type == null) {
0810: // 5) static imports ! [April2007] TODO: test
0811: // ok, they may be overridden by methods in 2...
0812: FileItem fi = this .locateTypeUsingStaticImports(
0813: nameToSearch, enforceMethod, scl);
0814:
0815: if (fi != null) {
0816: type = fi.getJavaName();
0817: //ide.resolvedType = fi;
0818:
0819: IDChainElement parent = new IDChainElement(this , fi
0820: .getJavaName(), null, false, false);
0821: parent.setIsDirectClassCall();
0822: parent.resolvedType_.fitem = fi;
0823:
0824: // insert
0825: this .chain.add(0, parent);
0826: }
0827: }
0828:
0829: if (type == null) {
0830: // 6) package part, as for example "javax"
0831: // ?? may be present in several archives !! => the ui, later, resolves...
0832: FileItem fi = TypeLocator
0833: .locateFirstPackage(nameToSearch);
0834: if (fi != null) {
0835: //System.out.println("Package found: "+type);
0836: ide.setIsPackagePart(fi);
0837: return ide;
0838: }
0839: }
0840:
0841: // 7) approximate
0842: if (type == null && allowAproximateSearch) {
0843: // approx
0844: FileItem fi = TypeLocator.searchTypeForName(
0845: nameToSearch, false, true); // ending with
0846: if (fi != null) {
0847: debugOut("Approx type found: " + type);
0848: ide.setIsDirectClassCall();
0849: type = fi.getJavaName();
0850: ide.resolvedType_.fitem = fi;
0851: }
0852: }
0853:
0854: debugOut("type found for first ID chain elt "
0855: + nameToSearch + " = " + type);
0856:
0857: ide.setTypeWithParamsAndResolve(type);
0858:
0859: if (!ide.isResolved()) {
0860: debugOut("### not resolved type for " + nameToSearch);
0861: isValid = false;
0862: }
0863: }
0864:
0865: return ide;
0866: }
0867:
0868: /** TODO.
0869: */
0870: private String locateSuperType(final String type) {
0871: debugOut("Todo: locate super type");
0872: // TODO
0873: return type;
0874: }
0875:
0876: public static void debugOut(String s) {
0877: MainEditorFrame.debugOut(s);
0878: }
0879:
0880: /** Called only on subsequent chain elements, not for the first.
0881: * Uses the previous element to know the type.
0882: */
0883: private IDChainElement locateNextElement(final String name,
0884: boolean enforceMethod, boolean isClassCast) {
0885: IDChainElement previous = getLastChainComponent(); // null if first !!
0886: IDChainElement ide = createIDElement(name, enforceMethod);
0887: ide.isClassCast = isClassCast;
0888:
0889: String type = null;
0890:
0891: if (ide.getName().equals("this")) {
0892: ide.setTypeName(previous.getTypeMaybeResolved());
0893: if (previous.resolvedType_ != null) {
0894: ide.resolvedType_.copyFrom(previous.resolvedType_);
0895: }
0896: } else if (ide.getName().equals("super")) {
0897: String super Type = locateSuperType(previous
0898: .getTypeMaybeResolved());
0899: ide.setTypeName(super Type);
0900: ide.resolvedType_.fitem = TypeLocator
0901: .locateQuick(super Type);
0902: } else if (ide.getName().equals("class")) {
0903: type = "java.lang.Class<" + previous.getTypeMaybeResolved()
0904: + ">";
0905: //ide.setTypeName( "java.lang.Class" );
0906: //ide.setTypeParameters("HELLOO");
0907: //ide.resolvedType = TypeLocator.locateQuick("java.lang.Class");
0908: } else {
0909: //TODO
0910: debugOut("Search for chain element " + name
0911: + " ( previous type = "
0912: + previous.getTypeMaybeResolved() + ")");
0913: try {
0914: // if the previous was a package part:
0915: if (previous.getKind() == ElementKind.PackageNameFragment) {
0916: String st = previous.resolvedType_.getJavaName()
0917: + "." + name;
0918: // just look if one, at least exists. Maybe not the correct later (in the full name), but
0919: // now it's sufficient to know the fragment is ok (Ex: several libs defines "javax")
0920: FileItem pi = TypeLocator.locateFirstPackage(st);
0921: //System.out.println("Locate "+st+": "+pi);
0922: if (pi != null) {
0923: ide.setIsPackagePart(pi);
0924: return ide; // OK
0925: }
0926:
0927: // try as class ?
0928: // no, cause this was already tryed in the locatefirstelement above !
0929:
0930: }
0931:
0932: // search based on the guessed new type
0933: //
0934: Class previousCt = getClassForType(previous);
0935: if (previousCt == null) {
0936: // no chance
0937: debugOut("locateNextElement aborted: null class for previous element "
0938: + previous);
0939: }
0940:
0941: if (ide.getKind() == ElementKind.Method) {
0942: if (ide.reflectedObject != null) // don't happens
0943: {
0944: Method me = (Method) ide.reflectedObject;
0945: type = me.getReturnType().getName();
0946: debugOut("Reflected method return type = "
0947: + me.getGenericReturnType());
0948: } else {
0949: debugOut("### null previous reflected ="
0950: + previous);
0951: type = ClassUtils.getTypeForMethodInClass(
0952: previousCt, ide.getName(), previous
0953: .getTypeParameters());
0954: }
0955: } else if (ide.getKind() == ElementKind.Field) {
0956: if (ide.reflectedObject != null) {
0957: type = ((Field) ide.reflectedObject).getType()
0958: .getName();
0959: } else {
0960: Class typeCl = ClassUtils
0961: .getTypeForFieldInClass(previousCt, ide
0962: .getName());
0963: if (typeCl != null) {
0964: type = typeCl.getName();
0965: ide.resolvedType_.rclass = typeCl;
0966: }
0967: }
0968: } else if (ide.getKind() == ElementKind.Class) {
0969: type = ide.getName();
0970: } else if (ide.getKind() == ElementKind.Constructor) {
0971: // new JFrame() give a JFrame back !
0972: type = ide.getName();
0973: if (previous != null) {
0974: type = previous.getName() + "." + type; // [14Dec2006]: resolve !!
0975: }
0976: System.out.println("Constructor type is " + type); // ?? Point2D.Double ?
0977: } else {
0978: // ???
0979: }
0980: } catch (Exception e) {
0981: e.printStackTrace();
0982: }
0983: }
0984:
0985: ide.setTypeWithParamsAndResolve(type);
0986: if (type == null) {
0987: debugOut("### Type null => not valid");
0988: isValid = false;
0989: }
0990:
0991: return ide;
0992: }
0993:
0994: public Class getClassForType(IDChainElement ide) {
0995: String jn = ide.getTypeMaybeResolved();
0996:
0997: if (ide.reflectedObject instanceof Class) {
0998: return (Class) ide.reflectedObject;
0999: }
1000:
1001: if (!ide.resolvedType_.isResolved()) {
1002: MainEditorFrame.debugOut("### Not resolved : " + jn);
1003: //return null; ??
1004: }
1005:
1006: if (jn == null)
1007: return null; // [Sep2007]
1008:
1009: return getClassForName(jn);
1010: }
1011:
1012: /** javax.swing.JTree => assoc class. null on exceptions.
1013: */
1014: public Class getClassForName(String jn) {
1015: try {
1016: return scl.findClass(jn); // not load !
1017: } catch (Exception e) {
1018: e.printStackTrace();
1019: }
1020: return null;
1021: }
1022:
1023: /** Used for the first element, in the parser tree.
1024: * Also looks for inner classes and top level types and constructors.
1025: * (other are find via reflection) (TODO: ameliorate...)
1026: */
1027: private String searchForTypeOfMethodOrField(IDChainElement ide,
1028: SimplifiedSyntaxTree2 sst, boolean methods, boolean fields) {
1029: String nameToSearch = SyntaxUtils.removeAllArrayPartsAtEnd(ide
1030: .getName());
1031: debugOut(" Search for members named " + nameToSearch + " (m="
1032: + methods + ", f=" + fields + ")");
1033:
1034: List<TypeNode> tns = sst.getAllTopLevelTypes(); //TODO: use cache from SourceFile's dependencies
1035: for (TypeNode tn : tns) {
1036: debugOut(" looking in " + tn.getJavaFullName());
1037: String name = SearchFunctions.searchTypeForMethodOrField(
1038: nameToSearch, tn, methods, fields);
1039: if (name != null)
1040: return name;
1041: }
1042: /*
1043: // not found ! [Feb2008]: look if it is one of the type name !
1044: MOVED IN THE CALLER for more control
1045: for(TypeNode tn : tns)
1046: {
1047: if(tn.getJavaFullName().endsWith(nameToSearch))
1048: {
1049: debugOut(" Candidate: toplevel type "+tn.getJavaFullName());
1050: return tn.getJavaFullName();
1051: }
1052: }*/
1053:
1054: // not found !
1055: return null;
1056: }
1057:
1058: /** If new preceeds the id.
1059: * as in new Throwable("Hello").printStackTrace();
1060: * or new javax.swing.JFrame("Hello")
1061: * as you see, the constructor may NOT be the elt at chain end !
1062: * so use the last element kind to know the kind of the chain, NOT this.
1063: * This, if true, means that the first "(" is a constructor, all next "(" are
1064: * method calles
1065: */
1066: public boolean isConstructorMode() {
1067: return constructorMode;
1068: }
1069:
1070: public boolean isValid() {
1071: return isValid;
1072: }
1073:
1074: @Override
1075: public String toString() {
1076: StringBuilder sb = new StringBuilder();
1077: sb.append("IDChain");
1078: if (!isValid)
1079: sb.append(" (INVALID)");
1080: sb.append(" for " + pid + "\n at " + source.getJavaName()
1081: + ", posStart=" + posInSource + ":");
1082: for (IDChainElement id : chain) {
1083: sb.append("\n idelt: " + id);
1084: }
1085: return sb.toString();
1086: }
1087:
1088: public String toStringHTML() {
1089: StringBuilder sb = new StringBuilder();
1090: sb.append("<html><body><p>");
1091: sb.append(HTMLUtils.convertCharsToHTML(pid.toString()));
1092: sb.append("</p>in ");
1093: sb.append(source.getJavaName());
1094: sb.append(", posStart=");
1095: sb.append(posInSource);
1096: if (!isValid)
1097: sb.append(", <b>INVALID</b>");
1098: sb.append("<br>");
1099:
1100: for (IDChainElement id : chain) {
1101: sb.append("<br>");
1102: sb.append(HTMLUtils.convertCharsToHTML(id.toString()));
1103: }
1104: sb.append("</body></html>");
1105: return sb.toString();
1106: }
1107:
1108: private IDChainElement createIDElement(String name,
1109: boolean enforceMethod) {
1110: boolean isMethod = name.indexOf('(') >= 0;
1111: boolean enforceConstructor = isConstructorMode()
1112: && !isConstructorAlreadyFound
1113: && (isMethod || enforceMethod);
1114:
1115: if (enforceConstructor)
1116: isConstructorAlreadyFound = true;
1117:
1118: if (name.endsWith("(")) {
1119: name = name.substring(0, name.length() - 1);
1120: return new IDChainElement(this , name, "", enforceMethod,
1121: enforceConstructor);
1122: }
1123:
1124: int pos = name.indexOf('(');
1125:
1126: if (pos > 0) {
1127: String args = name.substring(pos + 1, name.length() - 1);
1128: name = name.substring(0, pos);
1129: return new IDChainElement(this , name, args, enforceMethod,
1130: enforceConstructor);
1131: }
1132: return new IDChainElement(this, name, null, enforceMethod,
1133: enforceConstructor);
1134: }
1135:
1136: }
|