0001: /*
0002: * Sun Public License Notice
0003: *
0004: * The contents of this file are subject to the Sun Public License
0005: * Version 1.0 (the "License"). You may not use this file except in
0006: * compliance with the License. A copy of the License is available at
0007: * http://www.sun.com/
0008: *
0009: * The Original Code is NetBeans. The Initial Developer of the Original
0010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
0011: * Microsystems, Inc. All Rights Reserved.
0012: */
0013:
0014: package org.netbeans.editor.ext.java;
0015:
0016: import java.util.ArrayList;
0017: import java.util.Arrays;
0018: import java.util.List;
0019:
0020: import javax.swing.text.BadLocationException;
0021: import javax.swing.text.JTextComponent;
0022:
0023: import org.netbeans.editor.BaseDocument;
0024: import org.netbeans.editor.Formatter;
0025: import org.netbeans.editor.SyntaxSupport;
0026: import org.netbeans.editor.TokenID;
0027: import org.netbeans.editor.ext.CompletionQuery;
0028: import org.netbeans.editor.ext.ExtFormatter;
0029:
0030: /**
0031: * Java completion support finder
0032: *
0033: * @author Miloslav Metelka
0034: * @version 1.00
0035: */
0036:
0037: public class JavaCompletionQuery implements CompletionQuery {
0038:
0039: public CompletionQuery.Result query(JTextComponent component,
0040: int offset, SyntaxSupport support) {
0041: return query(component, offset, support, false);
0042: }
0043:
0044: /**
0045: * Perform the query on the given component. The query usually gets the
0046: * component's document, the caret position and searches back to find the
0047: * last command start. Then it inspects the text up to the caret position
0048: * and returns the result.
0049: *
0050: * @param component
0051: * the component to use in this query.
0052: * @param offset
0053: * position in the component's document to which the query will
0054: * be performed. Usually it's a caret position.
0055: * @param support
0056: * syntax-support that will be used during resolving of the
0057: * query.
0058: * @param sourceHelp
0059: * whether the help is retrieved to open the source file. The
0060: * query behavior is slightly modified if this flag is true
0061: * @return result of the query or null if there's no result.
0062: */
0063: public CompletionQuery.Result query(JTextComponent component,
0064: int offset, SyntaxSupport support, boolean sourceHelp) {
0065: BaseDocument doc = (BaseDocument) component.getDocument();
0066: JavaSyntaxSupport sup = (JavaSyntaxSupport) support
0067: .get(JavaSyntaxSupport.class);
0068: JavaResult ret = null;
0069:
0070: try {
0071: // find last separator position
0072: int lastSepOffset = sup.getLastCommandSeparator(offset);
0073: JCTokenProcessor tp = new JCTokenProcessor();
0074: sup.tokenizeText(tp, lastSepOffset + 1, offset, true);
0075:
0076: // Check whether there's a comment under the cursor
0077: boolean inComment = false;
0078: TokenID lastValidTokenID = tp.getLastValidTokenID();
0079: if (lastValidTokenID != null) {
0080: switch (lastValidTokenID.getNumericID()) {
0081: case JavaTokenContext.BLOCK_COMMENT_ID:
0082: if (tp.getLastValidTokenText() == null
0083: || !tp.getLastValidTokenText().endsWith(
0084: "*/")) {
0085: inComment = true;
0086: }
0087: break;
0088:
0089: case JavaTokenContext.LINE_COMMENT_ID:
0090: inComment = true;
0091: break;
0092: }
0093: }
0094:
0095: if (!inComment) {
0096: // refresh classes info before querying
0097: sup.refreshClassInfo();
0098:
0099: Context ctx = new Context(tp, component, sup,
0100: sourceHelp, offset);
0101: JCExpression exp = tp.getResultExp();
0102: ctx.resolveExp(exp);
0103: ret = ctx.result;
0104: }
0105: } catch (BadLocationException e) {
0106: e.printStackTrace();
0107: }
0108:
0109: return ret;
0110: }
0111:
0112: /**
0113: * Finds the fields, methods and the inner classes.
0114: */
0115: static List findFieldsAndMethods(JCFinder finder, JCClass cls,
0116: String name, boolean exactMatch, boolean staticOnly,
0117: boolean inspectOuterClasses) {
0118: // Find inner classes
0119: List ret = new ArrayList();
0120: if (staticOnly) {
0121: JCPackage pkg = finder
0122: .getExactPackage(cls.getPackageName());
0123: if (pkg != null) {
0124: ret = finder.findClasses(pkg, cls.getName() + '.'
0125: + name, false);
0126: }
0127: }
0128:
0129: // Add fields
0130: ret.addAll(finder.findFields(cls, name, exactMatch, staticOnly,
0131: inspectOuterClasses));
0132: // Add methods
0133: ret.addAll(finder.findMethods(cls, name, exactMatch,
0134: staticOnly, inspectOuterClasses));
0135: return ret;
0136: }
0137:
0138: static class Context {
0139:
0140: /** Token processor used for parsing the input */
0141: private JCTokenProcessor tp;
0142:
0143: /** Text component */
0144: private JTextComponent component;
0145:
0146: /** Syntax support for the given document */
0147: private JavaSyntaxSupport sup;
0148:
0149: /**
0150: * Whether get the source help or not. The source help has slightly
0151: * different handling in some situations.
0152: */
0153: private boolean sourceHelp;
0154:
0155: /** End position of the scanning - usually the caret position */
0156: private int endOffset;
0157:
0158: /**
0159: * If set to true true - find the type of the result expression. It's
0160: * stored in the lastType variable or lastPkg if it's a package. The
0161: * result variable is not populated. False means that the code
0162: * completion output should be collected.
0163: */
0164: private boolean findType;
0165:
0166: /**
0167: * Whether currently scanning either the package or the class name so
0168: * the results should limit the search to the static fields and methods.
0169: */
0170: private boolean staticOnly = true;
0171:
0172: /** Last package found when scanning dot expression */
0173: private JCPackage lastPkg;
0174:
0175: /** Last type found when scanning dot expression */
0176: private JCType lastType;
0177:
0178: /** Result list when code completion output is generated */
0179: private JavaResult result;
0180:
0181: /** Helper flag for recognizing constructors */
0182: private boolean isConstructor;
0183:
0184: public Context(JCTokenProcessor tp, JTextComponent component,
0185: JavaSyntaxSupport sup, boolean sourceHelp, int endOffset) {
0186: this .tp = tp;
0187: this .component = component;
0188: this .sup = sup;
0189: this .sourceHelp = sourceHelp;
0190: this .endOffset = endOffset;
0191: }
0192:
0193: public void setFindType(boolean findType) {
0194: this .findType = findType;
0195: }
0196:
0197: protected Object clone() {
0198: return new Context(tp, component, sup, sourceHelp,
0199: endOffset);
0200: }
0201:
0202: /*
0203: * private List getBaseHelp(String baseName) { if (sourceHelp) {
0204: * JCFinder finder = JavaCompletion.getFinder(); List res =
0205: * finder.findPackages(baseName, false, false); // find all subpackages
0206: * if (res == null) { res = new ArrayList(); }
0207: *
0208: * if (baseName != null && baseName.length() > 0) {
0209: * res.addAll(finder.findClasses(null, baseName, false)); // add
0210: * matching classes } return res; } return null; }
0211: *
0212: * private JavaResult getBaseHelpResult(String baseName, JCExpression
0213: * exp) { List res = getBaseHelp(baseName); if (res != null && exp !=
0214: * null) { return new JavaResult(res, formatName(baseName, true), exp,
0215: * exp.getTokenOffset(0), 0, 0); } return null; }
0216: */
0217:
0218: private String formatName(String name, boolean appendStar) {
0219: return (name != null) ? (appendStar ? (name + '*') : name)
0220: : (appendStar ? "*" : ""); // NOI18N
0221:
0222: }
0223:
0224: private String formatType(JCType type, boolean useFullName,
0225: boolean appendDot, boolean appendStar) {
0226: StringBuffer sb = new StringBuffer();
0227: if (type != null) {
0228: sb.append(type.format(useFullName));
0229: }
0230: if (appendDot) {
0231: sb.append('.');
0232: }
0233: if (appendStar) {
0234: sb.append('*');
0235: }
0236: return sb.toString();
0237: }
0238:
0239: private JCType resolveType(JCExpression exp) {
0240: Context ctx = (Context) clone();
0241: ctx.setFindType(true);
0242: JCType typ = null;
0243: if (ctx.resolveExp(exp)) {
0244: typ = ctx.lastType;
0245: }
0246: return typ;
0247: }
0248:
0249: boolean resolveExp(JCExpression exp) {
0250: boolean lastDot = false; // dot at the end of the whole
0251: // expression?
0252: JCFinder finder = JavaCompletion.getFinder();
0253: boolean ok = true;
0254:
0255: switch (exp.getExpID()) {
0256: case JCExpression.DOT_OPEN: // Dot expression with the dot at the
0257: // end
0258: lastDot = true;
0259: // let it flow to DOT
0260: case JCExpression.DOT: // Dot expression
0261: int parmCnt = exp.getParameterCount(); // Number of items in
0262: // the dot exp
0263:
0264: for (int i = 0; i < parmCnt && ok; i++) { // resolve all items
0265: // in a dot exp
0266: ok = resolveItem(exp.getParameter(i), (i == 0),
0267: (!lastDot && i == parmCnt - 1));
0268: }
0269:
0270: if (ok && lastDot) { // Found either type or package help
0271: // Need to process dot at the end of the expression
0272: int tokenCntM1 = exp.getTokenCount() - 1;
0273: int substPos = exp.getTokenOffset(tokenCntM1)
0274: + exp.getTokenLength(tokenCntM1);
0275: if (lastType != null) { // Found type
0276: JCClass cls;
0277: if (lastType.getArrayDepth() == 0) { // Not array
0278: cls = lastType.getClazz();
0279: } else { // Array of some depth
0280: cls = JavaCompletion.OBJECT_CLASS_ARRAY; // Use
0281: // Object
0282: // in
0283: // this
0284: // case
0285: }
0286: List res;
0287: if (sourceHelp) {
0288: res = new ArrayList();
0289: res.add(lastType.getClazz());
0290: } else { // not source-help
0291: res = findFieldsAndMethods(finder, cls, "",
0292: false, staticOnly, false); // NOI18N
0293: }
0294: // Get all fields and methods of the cls
0295: result = new JavaResult(component, res,
0296: formatType(lastType, true, true, true),
0297: exp, substPos, 0, cls.getName()
0298: .length() + 1);
0299: } else { // Found package (otherwise ok would be false)
0300: String searchPkg = lastPkg.getName() + '.';
0301: List res;
0302: if (sourceHelp) {
0303: res = new ArrayList();
0304: res.add(lastPkg); // return only the package
0305: } else {
0306: res = finder.findPackages(searchPkg, false,
0307: false); // find
0308: // all
0309: // subpackages
0310: res.addAll(Arrays.asList(lastPkg
0311: .getClasses())); // package
0312: // classes
0313: }
0314: result = new JavaResult(component, res,
0315: searchPkg + '*', exp, substPos, 0, 0);
0316: }
0317: }
0318: break;
0319:
0320: case JCExpression.NEW: // 'new' keyword
0321: List res = finder.findClasses(null, "", false); // Find all
0322: // classes by
0323: // name //
0324: // NOI18N
0325: result = new JavaResult(component, res, "*", exp,
0326: endOffset, 0, 0); // NOI18N
0327: break;
0328:
0329: default: // The rest of the situations is resolved as a singleton
0330: // item
0331: ok = resolveItem(exp, true, true);
0332: break;
0333: }
0334:
0335: return ok;
0336: }
0337:
0338: /**
0339: * Resolve one item from the expression connected by dots.
0340: *
0341: * @param item
0342: * expression item to resolve
0343: * @param first
0344: * whether this expression is the first one in a dot
0345: * expression
0346: * @param last
0347: * whether this expression is the last one in a dot
0348: * expression
0349: */
0350: boolean resolveItem(JCExpression item, boolean first,
0351: boolean last) {
0352: boolean cont = true; // whether parsing should continue or not
0353: boolean methodOpen = false; // helper flag for unclosed methods
0354: JCFinder finder = JavaCompletion.getFinder();
0355:
0356: switch (item.getExpID()) {
0357: case JCExpression.CONSTANT: // Constant item
0358: if (first) {
0359: lastType = item.getType(); // Get the constant type
0360: staticOnly = false;
0361: } else { // Not the first item in a dot exp
0362: cont = false; // impossible to have constant inside the
0363: // expression
0364: }
0365: break;
0366:
0367: case JCExpression.VARIABLE: // Variable or special keywords
0368: switch (item.getTokenID(0).getNumericID()) {
0369: case JavaTokenContext.THIS_ID: // 'this' keyword
0370: if (first) { // first item in expression
0371: JCClass cls = sup.getClass(item
0372: .getTokenOffset(0));
0373: if (cls != null) {
0374: lastType = JavaCompletion.getType(cls, 0);
0375: staticOnly = false;
0376: }
0377: } else { // 'something.this'
0378: staticOnly = false;
0379: }
0380: break;
0381:
0382: case JavaTokenContext.SUPER_ID: // 'super' keyword
0383: if (first) { // only allowed as the first item
0384: JCClass cls = sup.getClass(item
0385: .getTokenOffset(0));
0386: if (cls != null) {
0387: cls = finder.getExactClass(cls
0388: .getFullName());
0389: if (cls != null) {
0390: cls = cls.getSuperclass();
0391: if (cls != null) {
0392: lastType = JavaCompletion.getType(
0393: cls, 0);
0394: staticOnly = false;
0395: }
0396: }
0397: }
0398: } else {
0399: cont = false;
0400: }
0401: break;
0402:
0403: case JavaTokenContext.CLASS_ID: // 'class' keyword
0404: if (!first) {
0405: lastType = JavaCompletion.CLASS_TYPE;
0406: staticOnly = false;
0407: } else {
0408: cont = false;
0409: }
0410: break;
0411:
0412: default: // Regular constant
0413: String var = item.getTokenText(0);
0414: int varPos = item.getTokenOffset(0);
0415: if (first) { // try to find variable for the first item
0416: if (last && !findType) { // both first and last item
0417: List res = new ArrayList();
0418: JCClass cls = sup.getClass(varPos); // get document
0419: // class
0420: if (cls != null) {
0421: res.addAll(findFieldsAndMethods(finder,
0422: cls, var, false, sup
0423: .isStaticBlock(varPos),
0424: true));
0425: }
0426: if (var.length() > 0 || !sourceHelp) {
0427: res.addAll(finder.findPackages(var,
0428: false, false)); // add
0429: // matching
0430: // packages
0431: if (var.length() > 0) { // if at least one char
0432: res.addAll(finder.findClasses(null,
0433: var, false)); // add
0434: // matching
0435: // classes
0436: if (cls != null) {
0437: // add matching inner classes too
0438: JCPackage pkg = finder
0439: .getExactPackage(cls
0440: .getPackageName());
0441: res.addAll(finder.findClasses(
0442: pkg, cls.getName()
0443: + "." + var,
0444: false));
0445: }
0446: }
0447: }
0448: result = new JavaResult(component, res,
0449: var + '*', item, 0);
0450: } else { // not last item or finding type
0451: lastType = (JCType) sup.findType(var,
0452: varPos);
0453: if (lastType != null) { // variable found
0454: staticOnly = false;
0455: } else { // no variable found
0456: lastPkg = finder.getExactPackage(var); // try
0457: // package
0458: if (lastPkg == null) { // not package, let's
0459: // try class name
0460: JCClass cls = sup.getClassFromName(
0461: var, true);
0462: if (cls != null) {
0463: lastType = JavaCompletion
0464: .getType(cls, 0);
0465: } else { // class not found
0466: cont = false;
0467: }
0468: }
0469: }
0470: }
0471: } else { // not the first item
0472: if (lastType != null) { // last was type
0473: if (findType || !last) {
0474: boolean inner = false;
0475: int ad = lastType.getArrayDepth();
0476: if (staticOnly && ad == 0) { // can be inner
0477: // class
0478: JCClass cls = finder
0479: .getExactClass(lastType
0480: .getClazz()
0481: .getFullName()
0482: + "." + var); // NOI18N
0483: if (cls != null) {
0484: lastType = JavaCompletion
0485: .getType(cls, 0);
0486: inner = true;
0487: }
0488: }
0489:
0490: if (!inner) { // not inner class name
0491: if (ad == 0) { // zero array depth
0492: List fldList = finder
0493: .findFields(lastType
0494: .getClazz(),
0495: var, true,
0496: staticOnly,
0497: false);
0498: if (fldList.size() > 0) { // match
0499: // found
0500: JCField fld = (JCField) fldList
0501: .get(0);
0502: lastType = fld.getType();
0503: staticOnly = false;
0504: } else { // no match found
0505: lastType = null;
0506: cont = false;
0507: }
0508: } else { // array depth > 0 but no array
0509: // dereference
0510: cont = false;
0511: }
0512: }
0513: } else { // last and searching for completion output
0514: JCClass cls;
0515: if (lastType.getArrayDepth() == 0) { // Not
0516: // array
0517: cls = lastType.getClazz();
0518: } else { // Array of some depth
0519: cls = JavaCompletion.OBJECT_CLASS_ARRAY; // Use
0520: // Object
0521: // in
0522: // this
0523: // case
0524: }
0525: result = new JavaResult(component,
0526: findFieldsAndMethods(finder,
0527: cls, var, false,
0528: staticOnly, false),
0529: lastType.format(false) + '.'
0530: + var + '*', item, cls
0531: .getName().length() + 1);
0532: }
0533: } else { // currently package
0534: String searchName = lastPkg.getName() + '.'
0535: + var;
0536: if (findType || !last) {
0537: lastPkg = finder
0538: .getExactPackage(searchName);
0539: if (lastPkg == null) { // package doesn't exist
0540: JCClass cls = finder
0541: .getExactClass(searchName);
0542: if (cls != null) {
0543: lastType = JavaCompletion
0544: .getType(cls, 0);
0545: } else {
0546: lastType = null;
0547: cont = false;
0548: }
0549: }
0550: } else { // last and searching for completion output
0551: if (last) { // get all matching
0552: // fields/methods/packages
0553: String searchPkg = lastPkg
0554: .getName()
0555: + '.' + var;
0556: List res = finder.findPackages(
0557: searchPkg, false, false); // find
0558: // matching
0559: // subpackages
0560: res.addAll(finder.findClasses(
0561: lastPkg, var, false)); // matching
0562: // classes
0563: result = new JavaResult(component,
0564: res, searchPkg + '*', item,
0565: 0);
0566: }
0567: }
0568: }
0569: }
0570: break;
0571:
0572: }
0573: break;
0574:
0575: case JCExpression.ARRAY:
0576: cont = resolveItem(item.getParameter(0), first, false);
0577: if (cont) {
0578: cont = false;
0579: if (lastType != null) { // must be type
0580: if (item.getParameterCount() == 2) { // index in
0581: // array follows
0582: JCType arrayType = resolveType(item
0583: .getParameter(1));
0584: if (arrayType != null
0585: && arrayType
0586: .equals(JavaCompletion.INT_TYPE)) {
0587: lastType = JavaCompletion
0588: .getType(
0589: lastType.getClazz(),
0590: Math
0591: .max(
0592: lastType
0593: .getArrayDepth() - 1,
0594: 0));
0595: cont = true;
0596: }
0597: } else { // no index, increase array depth
0598: lastType = JavaCompletion.getType(lastType
0599: .getClazz(), lastType
0600: .getArrayDepth() + 1);
0601: cont = true;
0602: }
0603: }
0604: }
0605: break;
0606:
0607: case JCExpression.INSTANCEOF:
0608: lastType = JavaCompletion.BOOLEAN_TYPE;
0609: break;
0610:
0611: case JCExpression.OPERATOR:
0612: List res = new ArrayList();
0613: JCClass curCls = sup.getClass(item.getTokenOffset(0)); //
0614: if (curCls != null) { // find all methods and fields for
0615: // "this" class
0616: res.addAll(findFieldsAndMethods(finder, curCls, "",
0617: false, sup.isStaticBlock(item
0618: .getTokenOffset(0)), true));
0619: }
0620: res.addAll(finder.findPackages("", false, false)); // find all
0621: // packages
0622: res.addAll(finder.findClasses(null, "", false)); // find all
0623: // classes
0624:
0625: result = new JavaResult(component, res, "*", item,
0626: endOffset, 0, 0);
0627:
0628: switch (item.getTokenID(0).getNumericID()) {
0629: case JavaTokenContext.EQ_ID: // Assignment operators
0630: case JavaTokenContext.PLUS_EQ_ID:
0631: case JavaTokenContext.MINUS_EQ_ID:
0632: case JavaTokenContext.MUL_EQ_ID:
0633: case JavaTokenContext.DIV_EQ_ID:
0634: case JavaTokenContext.AND_EQ_ID:
0635: case JavaTokenContext.OR_EQ_ID:
0636: case JavaTokenContext.XOR_EQ_ID:
0637: case JavaTokenContext.MOD_EQ_ID:
0638: case JavaTokenContext.LSHIFT_EQ_ID:
0639: case JavaTokenContext.RSSHIFT_EQ_ID:
0640: case JavaTokenContext.RUSHIFT_EQ_ID:
0641: if (item.getParameterCount() > 0) {
0642: lastType = resolveType(item.getParameter(0));
0643: staticOnly = false;
0644: }
0645: break;
0646:
0647: case JavaTokenContext.LT_ID: // Binary, result is boolean
0648: case JavaTokenContext.GT_ID:
0649: case JavaTokenContext.LT_EQ_ID:
0650: case JavaTokenContext.GT_EQ_ID:
0651: case JavaTokenContext.EQ_EQ_ID:
0652: case JavaTokenContext.NOT_EQ_ID:
0653: case JavaTokenContext.AND_AND_ID: // Binary, result is boolean
0654: case JavaTokenContext.OR_OR_ID:
0655: lastType = JavaCompletion.BOOLEAN_TYPE;
0656: break;
0657:
0658: case JavaTokenContext.LSHIFT_ID: // Always binary
0659: case JavaTokenContext.RSSHIFT_ID:
0660: case JavaTokenContext.RUSHIFT_ID:
0661: case JavaTokenContext.MUL_ID:
0662: case JavaTokenContext.DIV_ID:
0663: case JavaTokenContext.AND_ID:
0664: case JavaTokenContext.OR_ID:
0665: case JavaTokenContext.XOR_ID:
0666: case JavaTokenContext.MOD_ID:
0667:
0668: case JavaTokenContext.PLUS_ID:
0669: case JavaTokenContext.MINUS_ID:
0670: switch (item.getParameterCount()) {
0671: case 2:
0672: JCType typ1 = resolveType(item.getParameter(0));
0673: JCType typ2 = resolveType(item.getParameter(1));
0674: if (typ1 != null
0675: && typ2 != null
0676: && typ1.getArrayDepth() == 0
0677: && typ2.getArrayDepth() == 0
0678: && JavaCompletion.isPrimitiveClass(typ1
0679: .getClazz())
0680: && JavaCompletion.isPrimitiveClass(typ2
0681: .getClazz())) {
0682: lastType = JCUtilities.getCommonType(typ1,
0683: typ2);
0684: }
0685: break;
0686: case 1: // get the only one parameter
0687: JCType typ = resolveType(item.getParameter(0));
0688: if (typ != null
0689: && JavaCompletion.isPrimitiveClass(typ
0690: .getClazz())) {
0691: lastType = typ;
0692: }
0693: break;
0694: }
0695: break;
0696:
0697: case JavaTokenContext.COLON_ID:
0698: switch (item.getParameterCount()) {
0699: case 2:
0700: JCType typ1 = resolveType(item.getParameter(0));
0701: JCType typ2 = resolveType(item.getParameter(1));
0702: if (typ1 != null && typ2 != null) {
0703: lastType = JCUtilities.getCommonType(typ1,
0704: typ2);
0705: }
0706: break;
0707:
0708: case 1:
0709: lastType = resolveType(item.getParameter(0));
0710: break;
0711: }
0712: break;
0713:
0714: case JavaTokenContext.QUESTION_ID:
0715: if (item.getParameterCount() >= 2) {
0716: lastType = resolveType(item.getParameter(1)); // should
0717: // be
0718: // colon
0719: }
0720: break;
0721: }
0722: break;
0723:
0724: case JCExpression.UNARY_OPERATOR:
0725: if (item.getParameterCount() > 0) {
0726: lastType = resolveType(item.getParameter(0));
0727: }
0728: break;
0729:
0730: case JCExpression.CONVERSION:
0731: lastType = resolveType(item.getParameter(0));
0732: staticOnly = false;
0733: break;
0734:
0735: case JCExpression.TYPE:
0736: lastType = item.getType();
0737: break;
0738:
0739: case JCExpression.PARENTHESIS:
0740: cont = resolveItem(item.getParameter(0), first, last);
0741: break;
0742:
0743: case JCExpression.CONSTRUCTOR: // constructor can be part of a DOT
0744: // expression
0745: isConstructor = true;
0746: cont = resolveExp(item.getParameter(0));
0747: staticOnly = false;
0748: break;
0749:
0750: case JCExpression.METHOD_OPEN: // Unclosed method
0751: methodOpen = true;
0752: // let it flow to method
0753: case JCExpression.METHOD: // Closed method
0754: String mtdName = item.getTokenText(0);
0755:
0756: // this() invoked, offer constructors
0757: if (("this".equals(mtdName))
0758: && (item.getTokenCount() > 0)) { // NOI18N
0759: JCClass cls = sup.getClass(item.getTokenOffset(0));
0760: if (cls != null) {
0761: cls = finder.getExactClass(cls.getFullName());
0762: if (cls != null) {
0763: isConstructor = true;
0764: mtdName = cls.getName();
0765: }
0766: }
0767: }
0768:
0769: // super() invoked, offer constructors for super class
0770: if (("super".equals(mtdName))
0771: && (item.getTokenCount() > 0)) { // NOI18N
0772: JCClass cls = sup.getClass(item.getTokenOffset(0));
0773: if (cls != null) {
0774: cls = finder.getExactClass(cls.getFullName());
0775: if (cls != null) {
0776: cls = cls.getSuperclass();
0777: if (cls != null) {
0778: isConstructor = true;
0779: mtdName = cls.getName();
0780: }
0781: }
0782: }
0783: }
0784:
0785: if (isConstructor) { // Help for the constructor
0786: JCClass cls = null;
0787: if (first) {
0788: cls = sup.getClassFromName(mtdName, true);
0789: } else { // not first
0790: if ((last) && (lastPkg != null)) { // valid package
0791: cls = JCUtilities.getExactClass(finder,
0792: mtdName, lastPkg.getName());
0793: } else if (lastType != null) {
0794: if (last) { // inner class
0795: cls = JCUtilities.getExactClass(finder,
0796: mtdName, lastType.getClazz()
0797: .getFullName());
0798: } else {
0799: if (lastType.getArrayDepth() == 0) { // Not
0800: // array
0801: cls = lastType.getClazz();
0802: } else { // Array of some depth
0803: cls = JavaCompletion.OBJECT_CLASS_ARRAY; // Use
0804: // Object
0805: // in
0806: // this
0807: // case
0808: }
0809: }
0810: }
0811: }
0812: if (cls != null) {
0813: lastType = JavaCompletion.getType(cls, 0);
0814: List ctrList = JCUtilities.getConstructors(cls);
0815: String parmStr = "*"; // NOI18N
0816: List typeList = getTypeList(item);
0817: List filtered = JCUtilities.filterMethods(
0818: ctrList, typeList, methodOpen);
0819: if (filtered.size() > 0) {
0820: ctrList = filtered;
0821: parmStr = formatTypeList(typeList,
0822: methodOpen);
0823: }
0824: List mtdList = finder.findMethods(cls, mtdName,
0825: true, false, first);
0826: if (mtdList.size() > 0) {
0827: if (last && !findType) {
0828: result = new JavaResult(component,
0829: mtdList, lastType.getClazz()
0830: .getFullName()
0831: + '.'
0832: + mtdName
0833: + '('
0834: + parmStr + ')', item,
0835: endOffset, 0, 0);
0836: } else {
0837: lastType = ((JCMethod) mtdList.get(0))
0838: .getReturnType();
0839: staticOnly = false;
0840: }
0841: } else {
0842: result = new JavaResult(component, ctrList,
0843: mtdName + '(' + parmStr + ')',
0844: item, endOffset, 0, 0);
0845: }
0846: } else {
0847: isConstructor = false;
0848: }
0849: }
0850: if (isConstructor == false) {
0851: // Help for the method
0852: if (first) {
0853: JCClass cls = sup.getClass(item
0854: .getTokenOffset(0));
0855: if (cls != null) {
0856: lastType = JavaCompletion.getType(cls, 0);
0857: }
0858: }
0859:
0860: if (lastType != null) {
0861: JCClass cls;
0862: if (lastType.getArrayDepth() == 0) { // Not array
0863: cls = lastType.getClazz();
0864: } else { // Array of some depth
0865: cls = JavaCompletion.OBJECT_CLASS_ARRAY; // Use
0866: // Object
0867: // in
0868: // this
0869: // case
0870: }
0871:
0872: List mtdList = finder.findMethods(cls, mtdName,
0873: true, false, first);
0874: String parmStr = "*"; // NOI18N
0875: List typeList = getTypeList(item);
0876: List filtered = JCUtilities.filterMethods(
0877: mtdList, typeList, methodOpen);
0878: if (filtered.size() > 0) {
0879: mtdList = filtered;
0880: parmStr = formatTypeList(typeList,
0881: methodOpen);
0882: }
0883: if (mtdList.size() > 0) {
0884: if (last && !findType) {
0885: result = new JavaResult(component,
0886: mtdList, lastType.getClazz()
0887: .getFullName()
0888: + '.'
0889: + mtdName
0890: + '('
0891: + parmStr + ')', item,
0892: endOffset, 0, 0);
0893: } else {
0894: if (mtdList.size() > 0) {
0895: lastType = ((JCMethod) mtdList
0896: .get(0)).getReturnType();
0897: staticOnly = false;
0898: }
0899: }
0900: } else {
0901: lastType = null; // no method found
0902: cont = false;
0903: }
0904: } else { // package.method() is invalid
0905: lastPkg = null;
0906: cont = false;
0907: }
0908: }
0909: break;
0910: }
0911:
0912: if (lastType == null && lastPkg == null) { // !!! shouldn't be
0913: // necessary
0914: cont = false;
0915: }
0916:
0917: return cont;
0918: }
0919:
0920: private List getTypeList(JCExpression item) {
0921: int parmCnt = item.getParameterCount();
0922: ArrayList typeList = new ArrayList();
0923: if (parmCnt > 0) { // will try to filter by parameters
0924: boolean methodOpen = (item.getExpID() == JCExpression.METHOD_OPEN);
0925: for (int i = 0; i < parmCnt; i++) {
0926: JCExpression parm = item.getParameter(i);
0927: JCType typ = resolveType(parm);
0928: typeList.add(typ);
0929: }
0930: }
0931: return typeList;
0932: }
0933:
0934: }
0935:
0936: private static String formatTypeList(List typeList,
0937: boolean methodOpen) {
0938: StringBuffer sb = new StringBuffer();
0939: if (typeList.size() > 0) {
0940: int cntM1 = typeList.size() - 1;
0941: for (int i = 0; i <= cntM1; i++) {
0942: JCType t = (JCType) typeList.get(i);
0943: if (t != null) {
0944: sb.append(t.format(false));
0945: } else {
0946: sb.append('?');
0947: }
0948: if (i < cntM1) {
0949: sb.append(", "); // NOI18N
0950: }
0951: }
0952: if (methodOpen) {
0953: sb.append(", *"); // NOI18N
0954: }
0955: } else { // no parameters
0956: if (methodOpen) {
0957: sb.append("*"); // NOI18N
0958: }
0959: }
0960: return sb.toString();
0961: }
0962:
0963: public static class JavaResult extends
0964: CompletionQuery.AbstractResult {
0965:
0966: /**
0967: * First offset in the name of the (inner) class to be displayed. It's
0968: * used to display the inner classes of the main class to exclude the
0969: * initial part of the name.
0970: */
0971: private int classDisplayOffset;
0972:
0973: /** Expression to substitute */
0974: private JCExpression substituteExp;
0975:
0976: /** Starting position of the text to substitute */
0977: private int substituteOffset;
0978:
0979: /** Length of the text to substitute */
0980: private int substituteLength;
0981:
0982: /** Component to update */
0983: private JTextComponent component;
0984:
0985: public JavaResult(JTextComponent component, List data,
0986: String title, JCExpression substituteExp,
0987: int classDisplayOffset) {
0988: this (component, data, title, substituteExp, substituteExp
0989: .getTokenOffset(0),
0990: substituteExp.getTokenLength(0), classDisplayOffset);
0991: }
0992:
0993: public JavaResult(JTextComponent component, List data,
0994: String title, JCExpression substituteExp,
0995: int substituteOffset, int substituteLength,
0996: int classDisplayOffset) {
0997: super (data, title);
0998:
0999: this .component = component;
1000: this .substituteExp = substituteExp;
1001: this .substituteOffset = substituteOffset;
1002: this .substituteLength = substituteLength;
1003: this .classDisplayOffset = classDisplayOffset;
1004: }
1005:
1006: /**
1007: * Get the text that is normally filled into the text if enter is
1008: * pressed.
1009: */
1010: protected String getMainText(Object dataItem) {
1011: String text = null;
1012: if (dataItem instanceof JCPackage) {
1013: text = ((JCPackage) dataItem).getLastName();
1014: } else if (dataItem instanceof JCClass) {
1015: text = ((JCClass) dataItem).getName();
1016: if (classDisplayOffset > 0
1017: && classDisplayOffset < text.length()) { // Only
1018: // the
1019: // last
1020: // name
1021: // for
1022: // inner
1023: // classes
1024: text = text.substring(classDisplayOffset);
1025: }
1026: } else if (dataItem instanceof JCField) {
1027: text = ((JCField) dataItem).getName();
1028: } else if (dataItem instanceof JCMethod) {
1029: JCMethod mtd = (JCMethod) dataItem;
1030: text = mtd.getName();
1031: } else if (dataItem instanceof JCConstructor) {
1032: text = ((JCConstructor) dataItem).getClazz().getName();
1033: }
1034: return text;
1035: }
1036:
1037: /** Get the text that is common to all the entries in the query-result */
1038: protected String getCommonText(String prefix) {
1039: List data = getData();
1040: int cnt = data.size();
1041: int prefixLen = prefix.length();
1042: String commonText = null;
1043: for (int i = 0; i < cnt; i++) {
1044: String mainText = getMainText(data.get(i));
1045: if (mainText != null && mainText.startsWith(prefix)) {
1046: mainText = mainText.substring(prefixLen);
1047: if (commonText == null) {
1048: commonText = mainText;
1049: }
1050: // Get largest common part
1051: int minLen = Math.min(mainText.length(), commonText
1052: .length());
1053: int commonInd;
1054: for (commonInd = 0; commonInd < minLen; commonInd++) {
1055: if (mainText.charAt(commonInd) != commonText
1056: .charAt(commonInd)) {
1057: break;
1058: }
1059: }
1060: if (commonInd != 0) {
1061: commonText = commonText.substring(0, commonInd);
1062: } else {
1063: return null; // no common text
1064: }
1065: }
1066: }
1067: return prefix + ((commonText != null) ? commonText : ""); // NOI18N
1068: }
1069:
1070: /**
1071: * Update the text in response to pressing TAB key.
1072: *
1073: * @return whether the text was successfully updated
1074: */
1075: public boolean substituteCommonText(int dataIndex) {
1076: BaseDocument doc = (BaseDocument) component.getDocument();
1077: try {
1078: String prefix = doc.getText(substituteOffset,
1079: substituteLength);
1080: String commonText = getCommonText(prefix);
1081: if (commonText != null) {
1082: if (substituteExp != null) {
1083: if ((substituteExp.getExpID() == JCExpression.METHOD_OPEN)
1084: || (substituteExp.getExpID() == JCExpression.METHOD))
1085: return true;
1086: }
1087: doc.atomicLock();
1088: try {
1089: doc.remove(substituteOffset, substituteLength);
1090: doc.insertString(substituteOffset, commonText,
1091: null);
1092: } finally {
1093: doc.atomicUnlock();
1094: }
1095: }
1096: } catch (BadLocationException e) {
1097: // no updating
1098: }
1099: return true;
1100: }
1101:
1102: /**
1103: * Update the text in response to pressing ENTER.
1104: *
1105: * @return whether the text was successfully updated
1106: */
1107: public boolean substituteText(int dataIndex, boolean shift) {
1108: BaseDocument doc = (BaseDocument) component.getDocument();
1109: String text = null;
1110: int selectionStartOffset = -1;
1111: int selectionEndOffset = -1;
1112: Object replacement = getData().get(dataIndex);
1113:
1114: if (replacement instanceof JCPackage) {
1115: text = ((JCPackage) replacement).getLastName();
1116:
1117: } else if (replacement instanceof JCClass) {
1118: text = ((JCClass) replacement).getName();
1119: if (classDisplayOffset > 0
1120: && classDisplayOffset < text.length()) { // Only
1121: // the
1122: // last
1123: // name
1124: // for
1125: // inner
1126: // classes
1127: text = text.substring(classDisplayOffset);
1128: }
1129:
1130: } else if (replacement instanceof JCField) {
1131: text = ((JCField) replacement).getName();
1132:
1133: } else if (replacement instanceof JCConstructor) {
1134: JCConstructor mtd = (JCConstructor) replacement;
1135: switch ((substituteExp != null) ? substituteExp
1136: .getExpID() : -1) {
1137: case JCExpression.METHOD:
1138: // no substitution
1139: break;
1140:
1141: case JCExpression.METHOD_OPEN:
1142: JCParameter[] parms = mtd.getParameters();
1143: if (parms.length == 0) {
1144: text = ")"; // NOI18N
1145: } else { // one or more parameters
1146: int ind = substituteExp.getParameterCount();
1147: boolean addSpace = false;
1148: Formatter f = doc.getFormatter();
1149: if (f instanceof ExtFormatter) {
1150: Object o = ((ExtFormatter) f)
1151: .getSettingValue(JavaSettingsNames.JAVA_FORMAT_SPACE_AFTER_COMMA);
1152: if ((o instanceof Boolean)
1153: && ((Boolean) o).booleanValue()) {
1154: addSpace = true;
1155: }
1156: }
1157:
1158: try {
1159: if (addSpace
1160: && (ind == 0 || (substituteOffset > 0 && Character
1161: .isWhitespace(doc
1162: .getText(
1163: substituteOffset - 1,
1164: 1)
1165: .charAt(0))))) {
1166: addSpace = false;
1167: }
1168: } catch (BadLocationException e) {
1169: }
1170:
1171: if (ind < parms.length) {
1172: text = addSpace ? " " : ""; // NOI18N
1173: selectionStartOffset = text.length();
1174: text += parms[ind].getName();
1175: selectionEndOffset = text.length();
1176: }
1177: }
1178: break;
1179:
1180: default:
1181: text = getMainText(replacement);
1182: boolean addSpace = false;
1183: Formatter f = doc.getFormatter();
1184: if (f instanceof ExtFormatter) {
1185: Object o = ((ExtFormatter) f)
1186: .getSettingValue(JavaSettingsNames.JAVA_FORMAT_SPACE_BEFORE_PARENTHESIS);
1187: if ((o instanceof Boolean)
1188: && ((Boolean) o).booleanValue()) {
1189: addSpace = true;
1190: }
1191: }
1192:
1193: if (addSpace) {
1194: text += ' ';
1195: }
1196: text += '(';
1197:
1198: parms = mtd.getParameters();
1199: if (parms.length > 0) {
1200: selectionStartOffset = text.length();
1201: text += parms[0].getName();
1202: selectionEndOffset = text.length();
1203: } else {
1204: text += ")"; // NOI18N
1205: }
1206: break;
1207: }
1208:
1209: } else if (replacement instanceof JCConstructor) {
1210: text = ((JCConstructor) replacement).getClazz()
1211: .getName();
1212: }
1213:
1214: if (text != null) {
1215: // Update the text
1216: doc.atomicLock();
1217: try {
1218: doc.remove(substituteOffset, substituteLength);
1219: doc.insertString(substituteOffset, text, null);
1220: if (selectionStartOffset >= 0) {
1221: component.select(substituteOffset
1222: + selectionStartOffset,
1223: substituteOffset + selectionEndOffset);
1224: }
1225: } catch (BadLocationException e) {
1226: // Can't update
1227: } finally {
1228: doc.atomicUnlock();
1229: }
1230: }
1231:
1232: return true;
1233: }
1234:
1235: }
1236:
1237: }
|