0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.cnd.completion.cplusplus.ext;
0043:
0044: import org.netbeans.modules.cnd.api.model.CsmClass;
0045: import org.netbeans.modules.cnd.api.model.CsmClassifier;
0046: import org.netbeans.modules.cnd.api.model.CsmParameter;
0047: import org.netbeans.modules.cnd.api.model.CsmType;
0048: import org.netbeans.modules.cnd.api.model.CsmVariable;
0049: import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities;
0050: import java.util.ArrayList;
0051: import java.util.List;
0052: import java.util.Map;
0053: import java.util.HashMap;
0054: import java.util.Iterator;
0055: import javax.swing.text.BadLocationException;
0056: import javax.swing.event.DocumentEvent;
0057: import javax.swing.text.JTextComponent;
0058: import org.netbeans.editor.BaseDocument;
0059: import org.netbeans.editor.TokenItem;
0060: import org.netbeans.editor.Utilities;
0061: import org.netbeans.editor.TextBatchProcessor;
0062: import org.netbeans.editor.FinderFactory;
0063: import org.netbeans.editor.Analyzer;
0064: import org.netbeans.editor.StringMap;
0065: import org.netbeans.editor.TokenID;
0066: import org.netbeans.editor.TokenContextPath;
0067: import org.netbeans.editor.ext.ExtSyntaxSupport.DeclarationTokenProcessor;
0068: import org.netbeans.editor.ext.ExtSyntaxSupport.VariableMapTokenProcessor;
0069: import org.netbeans.modules.cnd.api.model.CsmFunction;
0070: import org.netbeans.modules.cnd.api.model.CsmOffsetableDeclaration;
0071: import org.netbeans.modules.cnd.completion.csm.CompletionUtilities;
0072: import org.netbeans.modules.cnd.editor.cplusplus.CCTokenContext;
0073: import org.netbeans.modules.cnd.editor.spi.cplusplus.CCSyntaxSupport;
0074:
0075: /**
0076: * Support methods for csm based syntax analyzes
0077: *
0078: * @author Vladimir Voskresensky
0079: * @version 1.00
0080: * implemented after JavaSyntaxSupport
0081: */
0082:
0083: abstract public class CsmSyntaxSupport extends CCSyntaxSupport {
0084:
0085: // Internal C++ declaration token processor states
0086: static final int INIT = 0;
0087: static final int AFTER_TYPE = 1;
0088: static final int AFTER_VARIABLE = 2;
0089: static final int AFTER_COMMA = 3;
0090: static final int AFTER_DOT = 4;
0091: static final int AFTER_TYPE_LSB = 5;
0092: static final int AFTER_MATCHING_VARIABLE_LSB = 6;
0093: static final int AFTER_MATCHING_VARIABLE = 7;
0094: static final int AFTER_EQUAL = 8; // in decl after "var ="
0095: static final int AFTER_ARROW = 9;
0096: static final int AFTER_SCOPE = 10;
0097:
0098: private static final TokenID[] COMMENT_TOKENS = new TokenID[] {
0099: CCTokenContext.LINE_COMMENT, CCTokenContext.BLOCK_COMMENT };
0100:
0101: private static final TokenID[] BRACKET_SKIP_TOKENS = new TokenID[] {
0102: CCTokenContext.LINE_COMMENT, CCTokenContext.BLOCK_COMMENT,
0103: CCTokenContext.CHAR_LITERAL, CCTokenContext.STRING_LITERAL };
0104:
0105: // tokens valid for include-completion provider
0106: private static final TokenID[] INCLUDE_COMPLETION_TOKENS = new TokenID[] {
0107: CCTokenContext.USR_INCLUDE, CCTokenContext.SYS_INCLUDE,
0108: CCTokenContext.INCOMPLETE_SYS_INCLUDE,
0109: CCTokenContext.INCOMPLETE_USR_INCLUDE };
0110: // tokens invalid for general completion provider: skip tokens + include tokens
0111: private static final TokenID[] COMPLETION_SKIP_TOKENS;
0112: static {
0113: int brLen = BRACKET_SKIP_TOKENS.length;
0114: int incLen = INCLUDE_COMPLETION_TOKENS.length;
0115: COMPLETION_SKIP_TOKENS = new TokenID[brLen + incLen];
0116: System.arraycopy(BRACKET_SKIP_TOKENS, 0,
0117: COMPLETION_SKIP_TOKENS, 0, brLen);
0118: System.arraycopy(INCLUDE_COMPLETION_TOKENS, 0,
0119: COMPLETION_SKIP_TOKENS, brLen, incLen);
0120: }
0121:
0122: private static final char[] COMMAND_SEPARATOR_CHARS = new char[] {
0123: ';', '{', '}' };
0124:
0125: private CsmIncludeProcessor javaImport;
0126:
0127: /** Whether java 1.5 constructs are recognized. */
0128: private boolean java15;
0129:
0130: public CsmSyntaxSupport(BaseDocument doc) {
0131: super (doc);
0132:
0133: tokenNumericIDsValid = true;
0134: }
0135:
0136: abstract protected CsmFinder getFinder();
0137:
0138: protected CsmIncludeProcessor createIncludeProc() {
0139: return new CsmIncludeProcessor(this );
0140: }
0141:
0142: @Override
0143: protected void documentModified(DocumentEvent evt) {
0144: super .documentModified(evt);
0145: classFieldMaps.clear();
0146: fileVariableMaps.clear();
0147: if (javaImport != null) {
0148: javaImport.documentModifiedAtPosition(evt.getOffset(),
0149: getDocument());
0150: }
0151: }
0152:
0153: protected void setJava15(boolean java15) {
0154: this .java15 = java15;
0155: }
0156:
0157: @Override
0158: public TokenID[] getCommentTokens() {
0159: return COMMENT_TOKENS;
0160: }
0161:
0162: @Override
0163: public TokenID[] getBracketSkipTokens() {
0164: return BRACKET_SKIP_TOKENS;
0165: }
0166:
0167: /** Return the position of the last command separator before
0168: * the given position.
0169: */
0170: @Override
0171: public int getLastCommandSeparator(final int pos)
0172: throws BadLocationException {
0173: if (pos == 0)
0174: return 0;
0175: final int posLine = Utilities.getLineOffset(getDocument(), pos);
0176: TextBatchProcessor tbp = new TextBatchProcessor() {
0177: public int processTextBatch(BaseDocument doc, int startPos,
0178: int endPos, boolean lastBatch) {
0179: try {
0180: int[] blks = getCommentBlocks(endPos, startPos);
0181: FinderFactory.CharArrayBwdFinder cmdFinder = new FinderFactory.CharArrayBwdFinder(
0182: COMMAND_SEPARATOR_CHARS);
0183: int lastSeparatorOffset = findOutsideBlocks(
0184: cmdFinder, startPos, endPos, blks);
0185: if (lastSeparatorOffset < 1)
0186: return lastSeparatorOffset;
0187: TokenID separatorID = getTokenID(lastSeparatorOffset);
0188: if (separatorID.getNumericID() == CCTokenContext.RBRACE_ID) {
0189: int matchingBrkPos[] = findMatchingBlock(
0190: lastSeparatorOffset, true);
0191: if (matchingBrkPos != null) {
0192: int prev = Utilities.getFirstNonWhiteBwd(
0193: getDocument(), matchingBrkPos[0]);
0194: if (prev > -1
0195: && getTokenID(prev).getNumericID() == CCTokenContext.RBRACKET_ID) {
0196: return getLastCommandSeparator(prev);
0197: }
0198: }
0199: } else if (separatorID.getCategory() == CCTokenContext.CPP) {
0200: // found preprocessor directive, skip till the end of it
0201: int separatorLine = Utilities.getLineOffset(
0202: getDocument(), lastSeparatorOffset);
0203: assert (separatorLine <= posLine);
0204: if (separatorLine != posLine) {
0205: lastSeparatorOffset = Utilities.getRowEnd(
0206: getDocument(), lastSeparatorOffset);
0207: }
0208: }
0209: if (separatorID.getNumericID() != CCTokenContext.LBRACE_ID
0210: && separatorID.getNumericID() != CCTokenContext.RBRACE_ID
0211: && separatorID.getNumericID() != CCTokenContext.SEMICOLON_ID
0212: && separatorID.getCategory() != CCTokenContext.CPP) {
0213: lastSeparatorOffset = processTextBatch(doc,
0214: lastSeparatorOffset, 0, lastBatch);
0215: }
0216: return lastSeparatorOffset;
0217: } catch (BadLocationException e) {
0218: e.printStackTrace();
0219: return -1;
0220: }
0221: }
0222: };
0223: int lastPos = getDocument().processText(tbp, pos, 0);
0224:
0225: //ensure we return last command separator from last
0226: //block of C++ tokens from <startPos;endPos> offset interval
0227: //AFAIK this is currently needed only for JSP code completion
0228: TokenItem item = getTokenChain(pos - 1, pos);
0229: //go back throught the token chain and try to find last C++ token
0230: while (item != null) {
0231: int tokenOffset = item.getOffset();
0232: if (lastPos != -1 && tokenOffset < lastPos)
0233: break; //stop backtracking if we met the lastPos
0234: //test token type
0235: if (!item.getTokenContextPath().contains(
0236: CCTokenContext.contextPath)) {
0237: //return offset of last C++ token - this token isn't already a C++ token so return offset of next token
0238: lastPos = item.getNext() != null ? item.getNext()
0239: .getOffset() : item.getOffset()
0240: + item.getImage().length();
0241: break;
0242: }
0243: item = item.getPrevious();
0244: }
0245:
0246: return lastPos;
0247: }
0248:
0249: /** Get the class from name. The import sections are consulted to find
0250: * the proper package for the name. If the search in import sections fails
0251: * the method can ask the finder to search just by the given name.
0252: * @param className name to resolve. It can be either the full name
0253: * or just the name without the package.
0254: * @param searchByName if true and the resolving through the import sections fails
0255: * the finder is asked to find the class just by the given name
0256: */
0257: public CsmClass getClassFromName(String className,
0258: boolean searchByName) {
0259: refreshJavaImport();
0260: // XXX handle primitive type
0261: CsmClass ret = null;
0262: // CsmClass ret = JavaCompletion.getPrimitiveClass(className);
0263: // if (ret == null) {
0264: //
0265: // ret = getIncludeProc().getClassifier(className);
0266: // }
0267: if (ret == null && searchByName) {
0268: if (isUnknownInclude(className))
0269: return null;
0270: List clsList = getFinder().findClasses(null, className,
0271: true, false);
0272: if (clsList != null && clsList.size() > 0) {
0273: if (clsList.size() > 0
0274: && (clsList.get(0) instanceof CsmClass)) { // more matching classes
0275: ret = (CsmClass) clsList.get(0); // get the first one
0276: }
0277: }
0278:
0279: }
0280: return ret;
0281: }
0282:
0283: public synchronized CsmIncludeProcessor getIncludeProc() {
0284: if (javaImport == null) {
0285: javaImport = createIncludeProc();
0286: }
0287: javaImport.update(getDocument());
0288: return javaImport;
0289: }
0290:
0291: protected boolean isUnknownInclude(String className) {
0292: return getIncludeProc().isUnknownImport(className);
0293: }
0294:
0295: /** Returns all imports that aren't in Code Completion DB yet */
0296: protected List getUnknownIncludes() {
0297: return getIncludeProc().getUnknownImports();
0298: }
0299:
0300: // VK: never used - commented this out
0301: // /** Returns true if the given class is in the import statement directly or
0302: // * indirectly (package.name.*) */
0303: // public boolean isIcluded(CsmClass cls){
0304: // return getIncludeProc().isIncluded(cls);
0305: // }
0306:
0307: public void refreshJavaImport() {
0308: if (javaImport != null) {
0309: javaImport.update(getDocument());
0310: }
0311: }
0312:
0313: protected void refreshClassInfo() {
0314: }
0315:
0316: protected List getImportedInnerClasses() {
0317: refreshJavaImport();
0318: return getIncludeProc().getInnerClasses();
0319: }
0320:
0321: /** Get the class that belongs to the given position */
0322: public CsmClass getClass(int pos) {
0323: return CompletionUtilities.findClassOnPosition(getDocument(),
0324: pos);
0325: }
0326:
0327: /** Get the class or function definition that belongs to the given position */
0328: public CsmOffsetableDeclaration getDefinition(int pos) {
0329: return CompletionUtilities.findFunDefinitionOrClassOnPosition(
0330: getDocument(), pos);
0331: }
0332:
0333: public boolean isStaticBlock(int pos) {
0334: return false;
0335: }
0336:
0337: public boolean isAnnotation(int pos) {
0338: try {
0339: BaseDocument document = getDocument();
0340: int off = Utilities.getFirstNonWhiteBwd(document, pos);
0341: char ch = '*'; // NOI18N
0342: while (off > -1
0343: && (ch = document.getChars(off, 1)[0]) == '.') { // NOI18N
0344: off = Utilities.getFirstNonWhiteBwd(document, off);
0345: if (off > -1)
0346: off = Utilities.getPreviousWord(document, off);
0347: if (off > -1)
0348: off = Utilities.getFirstNonWhiteBwd(document, off);
0349: }
0350: if (off > -1 && ch == '@') // NOI18N
0351: return true;
0352: } catch (BadLocationException e) {
0353: }
0354: return false;
0355: }
0356:
0357: @Override
0358: public int[] getFunctionBlock(int[] identifierBlock)
0359: throws BadLocationException {
0360: int[] retValue = super .getFunctionBlock(identifierBlock);
0361: if (!isAnnotation(identifierBlock[0]))
0362: return retValue;
0363: return null;
0364: }
0365:
0366: @Override
0367: protected DeclarationTokenProcessor createDeclarationTokenProcessor(
0368: String varName, int startPos, int endPos) {
0369: return java15 ? (DeclarationTokenProcessor) new CsmDeclarationProcessor(
0370: this , varName)
0371: : (DeclarationTokenProcessor) new CsmDeclarationTokenProcessor(
0372: this , varName);
0373: }
0374:
0375: @Override
0376: protected VariableMapTokenProcessor createVariableMapTokenProcessor(
0377: int startPos, int endPos) {
0378: return java15 ? (VariableMapTokenProcessor) new CsmDeclarationProcessor(
0379: this , null)
0380: : (VariableMapTokenProcessor) new CsmDeclarationTokenProcessor(
0381: this , null);
0382: }
0383:
0384: /** Checks, whether caret is inside method */
0385: // private boolean insideMethod(JTextComponent textComp, int startPos){
0386: // try{
0387: // int level = 0;
0388: // BaseDocument doc = (BaseDocument)textComp.getDocument();
0389: // for(int i = startPos-1; i>0; i--){
0390: // char ch = doc.getChars(i, 1)[0];
0391: // if (ch == ';') return false;
0392: // if (ch == ')') level++;
0393: // if (ch == '('){
0394: // if (level == 0){
0395: // return true;
0396: // }else{
0397: // level--;
0398: // }
0399: // }
0400: // }
0401: // return false;
0402: // } catch (BadLocationException e) {
0403: // return false;
0404: // }
0405: // }
0406: /** Check and possibly popup, hide or refresh the completion */
0407: // public int checkCompletion(JTextComponent target, String typedText, boolean visible ) {
0408: // if (!visible) { // pane not visible yet
0409: // int dotPos = target.getCaret().getDot();
0410: // switch (typedText.charAt(0)) {
0411: // case ' ':
0412: // BaseDocument doc = (BaseDocument)target.getDocument();
0413: //
0414: // if (dotPos >= 2) { // last char before inserted space
0415: // int pos = Math.max(dotPos - 8, 0);
0416: // try {
0417: // String txtBeforeSpace = doc.getText(pos, dotPos - pos);
0418: //
0419: // if ( txtBeforeSpace.endsWith("import ") // NOI18N
0420: // && !Character.isJavaIdentifierPart(txtBeforeSpace.charAt(0))) {
0421: // return ExtSyntaxSupport.COMPLETION_POPUP;
0422: // }
0423: //
0424: // if (txtBeforeSpace.endsWith(", ")) { // NOI18N
0425: // // autoPopup completion only if caret is inside method
0426: // if (insideMethod(target, dotPos)) return ExtSyntaxSupport.COMPLETION_POPUP;
0427: // }
0428: // } catch (BadLocationException e) {
0429: // }
0430: // }
0431: // break;
0432: //
0433: // case '.':
0434: // return ExtSyntaxSupport.COMPLETION_POPUP;
0435: // case ',':
0436: // // autoPopup completion only if caret is inside method
0437: // if (insideMethod(target, dotPos)) return ExtSyntaxSupport.COMPLETION_POPUP;
0438: // default:
0439: // if (Character.isJavaIdentifierStart(typedText.charAt(0))) {
0440: // if (dotPos >= 5) { // last char before inserted space
0441: // try {
0442: // String maybeNew = target.getDocument().getText(dotPos - 5, 4);
0443: // if (maybeNew.equals("new ")){ // NOI18N
0444: // return ExtSyntaxSupport.COMPLETION_POPUP;
0445: // }
0446: // } catch (BadLocationException e) {
0447: // }
0448: // }
0449: // }
0450: // }
0451: // return ExtSyntaxSupport.COMPLETION_CANCEL;
0452: //
0453: // } else { // the pane is already visible
0454: // switch (typedText.charAt(0)) {
0455: // case '=':
0456: // case '{':
0457: // case ';':
0458: // return ExtSyntaxSupport.COMPLETION_HIDE;
0459: // default:
0460: // return ExtSyntaxSupport.COMPLETION_POST_REFRESH;
0461: // }
0462: // }
0463: // }
0464: public boolean isAssignable(CsmType from, CsmType to) {
0465: CsmClassifier fromCls = from.getClassifier();
0466: CsmClassifier toCls = to.getClassifier();
0467:
0468: // XXX review!
0469: if (fromCls.equals(CsmCompletion.NULL_CLASS)) {
0470: return to.getArrayDepth() > 0
0471: || !CsmCompletion.isPrimitiveClass(toCls);
0472: }
0473:
0474: if (toCls.equals(CsmCompletion.OBJECT_CLASS)) { // everything is object
0475: return (from.getArrayDepth() > to.getArrayDepth())
0476: || (from.getArrayDepth() == to.getArrayDepth() && !CsmCompletion
0477: .isPrimitiveClass(fromCls));
0478: }
0479:
0480: if (from.getArrayDepth() != to.getArrayDepth()
0481: || from.getPointerDepth() != to.getPointerDepth()) {
0482: return false;
0483: }
0484:
0485: if (fromCls.equals(toCls)) {
0486: return true; // equal classes
0487: }
0488: String tfrom = from.getCanonicalText().toString().replaceAll(
0489: "const", "").trim(); // NOI18N
0490: String tto = to.getCanonicalText().toString().replaceAll(
0491: "const", "").trim(); // NOI18N
0492:
0493: if (tfrom.equals(tto)) {
0494: return true;
0495: }
0496: // if (CsmInheritanceUtilities.isAssignableFrom(toCls, fromCls)) {
0497: // return true;
0498: // }
0499: // XXX
0500: // if (fromCls.isInterface()) {
0501: // return toCls.isInterface()
0502: // && (JCUtilities.getAllInterfaces(getFinder(), fromCls).indexOf(toCls) >= 0);
0503: // } else { // fromCls is a class
0504: // TokenID fromClsKwd = CCTokenContext.getKeyword(fromCls.getName());
0505: // if (fromClsKwd != null) { // primitive class
0506: // TokenID toClsKwd = CCTokenContext.getKeyword(toCls.getName());
0507: // return toClsKwd != null
0508: // && JCUtilities.getPrimitivesAssignable(fromClsKwd.getNumericID(), toClsKwd.getNumericID());
0509: // } else {
0510: // if (toCls.isInterface()) {
0511: // return (JCUtilities.getAllInterfaces(getFinder(), fromCls).indexOf(toCls) >= 0);
0512: // } else { // toCls is a class
0513: // return (JCUtilities.getSuperclasses(getFinder(), fromCls).indexOf(toCls) >= 0);
0514: // }
0515: // }
0516: // }
0517: return false;
0518: }
0519:
0520: public CsmType getCommonType(CsmType typ1, CsmType typ2) {
0521: if (typ1.equals(typ2)) {
0522: return typ1;
0523: }
0524:
0525: // The following part
0526: TokenID cls1Kwd = CCTokenContext.getKeyword(typ1
0527: .getClassifier().getName().toString());
0528: TokenID cls2Kwd = CCTokenContext.getKeyword(typ2
0529: .getClassifier().getName().toString());
0530: if (cls1Kwd == null && cls2Kwd == null) { // non-primitive classes
0531: if (isAssignable(typ1, typ2)) {
0532: return typ1;
0533: } else if (isAssignable(typ2, typ1)) {
0534: return typ2;
0535: } else {
0536: return null;
0537: }
0538: } else { // at least one primitive class
0539: if (typ1.getArrayDepth() != typ2.getArrayDepth()) {
0540: return null;
0541: }
0542: // XXX review
0543: // if (cls1Kwd != null && cls2Kwd != null) {
0544: // return JavaCompletion.getType(
0545: // JCUtilities.getPrimitivesCommonClass(cls1Kwd.getNumericID(), cls2Kwd.getNumericID()),
0546: // typ1.getArrayDepth());
0547: // } else { // one primitive but other not
0548: // return null;
0549: // }
0550: return null;
0551: }
0552: }
0553:
0554: /** Filter the list of the methods (usually returned from
0555: * Finder.findMethods()) or the list of the constructors
0556: * by the given parameter specification.
0557: * @param methodList list of the methods. They should have the same
0558: * name but in fact they don't have to.
0559: * @param parmTypes parameter types specification. If set to null, no filtering
0560: * is performed and the same list is returned. If a particular
0561: * @param acceptMoreParameters useful for code completion to get
0562: * even the methods with more parameters.
0563: */
0564: public List filterMethods(List methodList, List parmTypeList,
0565: boolean acceptMoreParameters) {
0566: assert (methodList != null);
0567: if (parmTypeList == null) {
0568: return methodList;
0569: }
0570:
0571: List ret = new ArrayList();
0572: int parmTypeCnt = parmTypeList.size();
0573: int cnt = methodList.size();
0574: int maxMatched = -1;
0575: for (int i = 0; i < cnt; i++) {
0576: // Use constructor conversion to allow to use it too for the constructors
0577: CsmFunction m = (CsmFunction) methodList.get(i);
0578: CsmParameter[] methodParms = (CsmParameter[]) m
0579: .getParameters().toArray(new CsmParameter[0]);
0580: if (methodParms.length == parmTypeCnt
0581: || (acceptMoreParameters && methodParms.length >= parmTypeCnt)) {
0582: boolean accept = true;
0583: boolean bestMatch = !acceptMoreParameters;
0584: int matched = 0;
0585: for (int j = 0; accept && j < parmTypeCnt; j++) {
0586: CsmType mpt = methodParms[j].getType();
0587: CsmType t = (CsmType) parmTypeList.get(j);
0588: if (t != null) {
0589: if (!methodParms[j].isVarArgs()
0590: && !equalTypes(t, mpt)) {
0591: bestMatch = false;
0592: if (!isAssignable(t, mpt)) {
0593: accept = false;
0594: // TODO: do not break now, count matches
0595: // break;
0596: } else {
0597: matched++;
0598: }
0599: } else {
0600: matched++;
0601: }
0602: } else { // type in list is null
0603: bestMatch = false;
0604: }
0605: }
0606:
0607: if (accept) {
0608: if (bestMatch) {
0609: ret.clear();
0610: } else if (matched > maxMatched) {
0611: maxMatched = matched;
0612: ret.clear();
0613: }
0614: ret.add(m);
0615: if (bestMatch) {
0616: break;
0617: }
0618: } else {
0619: if (matched > maxMatched) {
0620: maxMatched = matched;
0621: ret.clear();
0622: ret.add(m);
0623: }
0624: }
0625:
0626: } else if (methodParms.length == 0 && parmTypeCnt == 1) { // for cases like f(void)
0627: CsmType t = (CsmType) parmTypeList.get(0);
0628: if (t != null && "void".equals(t.getText())) { // best match // NOI18N
0629: ret.clear();
0630: ret.add(m);
0631: }
0632: }
0633: }
0634: return ret;
0635: }
0636:
0637: private boolean isOffsetInToken(TokenItem token,
0638: TokenID[] tokenIDs, int offset) {
0639: boolean exists = false;
0640: for (int i = tokenIDs.length - 1; i >= 0; i--) {
0641: if (token.getTokenID() == tokenIDs[i]) {
0642: exists = true;
0643: break;
0644: }
0645: }
0646: if (exists) {
0647: // check offset
0648: int st = token.getOffset();
0649: int len = token.getImage().length();
0650: if (st >= offset) {
0651: exists = false;
0652: } else if (len == 1) {
0653: exists = ((st + len) == offset);
0654: } else if (token.getTokenID().getCategory() == CCTokenContext.ERRORS) {
0655: exists = ((st + len) >= offset);
0656: } else {
0657: exists = ((st + len) > offset);
0658: }
0659: }
0660: return exists;
0661: }
0662:
0663: /**
0664: * Interface that can be implemented by the values (in the key-value Map terminology)
0665: * of the variableMap provided by VariableMapTokenProcessor implementations.
0666: */
0667: public interface JavaVariable {
0668:
0669: /**
0670: * Get type expression of the variable declaration.
0671: * <br>
0672: * For example for "List<String> l;" it would be an expression formed
0673: * from "List<String>".
0674: *
0675: * @return type expression for this variable.
0676: */
0677: public CsmCompletionExpression getTypeExpression();
0678:
0679: /**
0680: * Get variable expression of the variable declaration.
0681: * <br>
0682: * Typically it is just the declaration variable but for arrays
0683: * it can include array depths - for example
0684: * for "int i[];" it would be an expression formed
0685: * from "i[]".
0686: *
0687: * @return type expression for this variable.
0688: */
0689: public CsmCompletionExpression getVariableExpression();
0690:
0691: }
0692:
0693: public static class CsmDeclarationTokenProcessor implements
0694: DeclarationTokenProcessor, VariableMapTokenProcessor {
0695:
0696: protected CsmSyntaxSupport sup;
0697:
0698: /** Position of the begining of the declaration to be returned */
0699: protected int decStartPos = -1;
0700:
0701: protected int decArrayDepth;
0702:
0703: /** Starting position of the declaration type */
0704: protected int typeStartPos;
0705:
0706: /** Position of the end of the type */
0707: protected int typeEndPos;
0708:
0709: /** Offset of the name of the variable */
0710: protected int decVarNameOffset;
0711:
0712: /** Length of the name of the variable */
0713: protected int decVarNameLen;
0714:
0715: /** Currently inside parenthesis, i.e. comma delimits declarations */
0716: protected int parenthesisCounter;
0717:
0718: /** Depth of the array when there is an array declaration */
0719: protected int arrayDepth;
0720:
0721: protected char[] buffer;
0722:
0723: protected int bufferStartPos;
0724:
0725: protected String varName;
0726:
0727: protected int state;
0728:
0729: /** Map filled with the [varName, type/classifier] pairs */
0730: protected HashMap varMap;
0731:
0732: /** Construct new token processor
0733: * @param varName it contains valid varName name or null to search
0734: * for all variables and construct the variable map.
0735: */
0736: public CsmDeclarationTokenProcessor(CsmSyntaxSupport sup,
0737: String varName) {
0738: this .sup = sup;
0739: this .varName = varName;
0740: if (varName == null) {
0741: varMap = new HashMap();
0742: }
0743: }
0744:
0745: public int getDeclarationPosition() {
0746: return decStartPos;
0747: }
0748:
0749: public Map getVariableMap() {
0750: return varMap;
0751: }
0752:
0753: protected void processDeclaration() {
0754: // XXX review!
0755: if (varName == null) { // collect all variables
0756: String decType = new String(buffer, typeStartPos
0757: - bufferStartPos, typeEndPos - typeStartPos);
0758: if (decType.indexOf(' ') >= 0) {
0759: decType = Analyzer.removeSpaces(decType);
0760: }
0761: String decVarName = new String(buffer,
0762: decVarNameOffset, decVarNameLen);
0763:
0764: // Maybe it's inner class. Stick an outerClass before it ...
0765: CsmClass innerClass = null;
0766: CsmClass outerCls = sup.getClass(decVarNameOffset);
0767: if (outerCls != null) {
0768: String outerClassName = outerCls.getQualifiedName()
0769: .toString();
0770: CsmClassifier innerClassifier = sup.getFinder()
0771: .getExactClassifier(
0772: outerClassName
0773: + CsmCompletion.SCOPE
0774: + decType);
0775: innerClass = CsmKindUtilities
0776: .isClass(innerClassifier) ? (CsmClass) innerClassifier
0777: : null;
0778: if (innerClass != null) {
0779: // varMap.put(decVarName, JavaCompletion.getType(innerClass, decArrayDepth));
0780: varMap.put(decVarName, innerClass);
0781: }
0782: }
0783:
0784: if (innerClass == null) {
0785: CsmClass cls = sup.getClassFromName(decType, true);
0786: if (cls != null) {
0787: // varMap.put(decVarName, JavaCompletion.getType(cls, decArrayDepth));
0788: varMap.put(decVarName, cls);
0789: }
0790: }
0791:
0792: } else {
0793: decStartPos = typeStartPos;
0794: }
0795: }
0796:
0797: public boolean token(TokenID tokenID,
0798: TokenContextPath tokenContextPath, int tokenOffset,
0799: int tokenLen) {
0800: int pos = bufferStartPos + tokenOffset;
0801:
0802: // Check whether we are really recognizing the C++ tokens
0803: if (!tokenContextPath.contains(CCTokenContext.contextPath)) {
0804: state = INIT;
0805: return true;
0806: }
0807:
0808: switch (tokenID.getNumericID()) {
0809: case CCTokenContext.BOOLEAN_ID:
0810: case CCTokenContext.CHAR_ID:
0811: case CCTokenContext.DOUBLE_ID:
0812: case CCTokenContext.FLOAT_ID:
0813: case CCTokenContext.INT_ID:
0814: case CCTokenContext.LONG_ID:
0815: case CCTokenContext.SHORT_ID:
0816: case CCTokenContext.VOID_ID:
0817: typeStartPos = pos;
0818: arrayDepth = 0;
0819: typeEndPos = pos + tokenLen;
0820: state = AFTER_TYPE;
0821: break;
0822:
0823: case CCTokenContext.DOT_ID:
0824: case CCTokenContext.DOTMBR_ID:
0825: switch (state) {
0826: case AFTER_TYPE: // allowed only inside type
0827: state = AFTER_DOT;
0828: typeEndPos = pos + tokenLen;
0829: break;
0830:
0831: case AFTER_EQUAL:
0832: case AFTER_VARIABLE:
0833: break;
0834:
0835: default:
0836: state = INIT;
0837: break;
0838: }
0839: break;
0840:
0841: case CCTokenContext.ARROW_ID:
0842: case CCTokenContext.ARROWMBR_ID:
0843: switch (state) {
0844: case AFTER_TYPE: // allowed only inside type
0845: state = AFTER_ARROW;
0846: typeEndPos = pos + tokenLen;
0847: break;
0848:
0849: case AFTER_EQUAL:
0850: case AFTER_VARIABLE:
0851: break;
0852:
0853: default:
0854: state = INIT;
0855: break;
0856: }
0857: break;
0858:
0859: case CCTokenContext.SCOPE_ID:
0860: switch (state) {
0861: case AFTER_TYPE: // allowed only inside type
0862: state = AFTER_SCOPE;
0863: typeEndPos = pos + tokenLen;
0864: break;
0865:
0866: case AFTER_EQUAL:
0867: case AFTER_VARIABLE:
0868: break;
0869:
0870: default:
0871: state = INIT;
0872: break;
0873: }
0874: break;
0875: //XXX
0876: // case CCTokenContext.ELLIPSIS_ID:
0877: // switch (state) {
0878: // case AFTER_TYPE:
0879: // arrayDepth++;
0880: // break;
0881: //
0882: // default:
0883: // state = INIT;
0884: // break;
0885: // }
0886: // break;
0887:
0888: case CCTokenContext.LBRACKET_ID:
0889: switch (state) {
0890: case AFTER_TYPE:
0891: state = AFTER_TYPE_LSB;
0892: arrayDepth++;
0893: break;
0894:
0895: case AFTER_MATCHING_VARIABLE:
0896: state = AFTER_MATCHING_VARIABLE_LSB;
0897: decArrayDepth++;
0898: break;
0899:
0900: case AFTER_EQUAL:
0901: break;
0902:
0903: default:
0904: state = INIT;
0905: break;
0906: }
0907: break;
0908:
0909: case CCTokenContext.RBRACKET_ID:
0910: switch (state) {
0911: case AFTER_TYPE_LSB:
0912: state = AFTER_TYPE;
0913: break;
0914:
0915: case AFTER_MATCHING_VARIABLE_LSB:
0916: state = AFTER_MATCHING_VARIABLE;
0917: break;
0918:
0919: case AFTER_EQUAL:
0920: break;
0921:
0922: default:
0923: state = INIT;
0924: break;
0925: }
0926: break; // both in type and varName
0927:
0928: case CCTokenContext.LPAREN_ID:
0929: parenthesisCounter++;
0930: if (state != AFTER_EQUAL) {
0931: state = INIT;
0932: }
0933: break;
0934:
0935: case CCTokenContext.RPAREN_ID:
0936: if (state == AFTER_MATCHING_VARIABLE) {
0937: processDeclaration();
0938: }
0939: if (parenthesisCounter > 0) {
0940: parenthesisCounter--;
0941: }
0942: if (state != AFTER_EQUAL) {
0943: state = INIT;
0944: }
0945: break;
0946:
0947: case CCTokenContext.LBRACE_ID:
0948: case CCTokenContext.RBRACE_ID:
0949: if (parenthesisCounter > 0) {
0950: parenthesisCounter--; // to tolerate opened parenthesis
0951: }
0952: state = INIT;
0953: break;
0954:
0955: case CCTokenContext.COMMA_ID:
0956: if (parenthesisCounter > 0) { // comma is declaration separator in parenthesis
0957: if (parenthesisCounter == 1
0958: && state == AFTER_MATCHING_VARIABLE) {
0959: processDeclaration();
0960: }
0961: if (state != AFTER_EQUAL) {
0962: state = INIT;
0963: }
0964: } else { // not in parenthesis
0965: switch (state) {
0966: case AFTER_MATCHING_VARIABLE:
0967: processDeclaration();
0968: // let it flow to AFTER_VARIABLE
0969: case AFTER_VARIABLE:
0970: case AFTER_EQUAL:
0971: state = AFTER_COMMA;
0972: break;
0973:
0974: default:
0975: state = INIT;
0976: break;
0977: }
0978: }
0979: break;
0980:
0981: case CCTokenContext.NEW_ID:
0982: if (state != AFTER_EQUAL) {
0983: state = INIT;
0984: }
0985: break;
0986:
0987: case CCTokenContext.EQ_ID:
0988: switch (state) {
0989: case AFTER_MATCHING_VARIABLE:
0990: processDeclaration();
0991: // flow to AFTER_VARIABLE
0992:
0993: case AFTER_VARIABLE:
0994: state = AFTER_EQUAL;
0995: break;
0996:
0997: case AFTER_EQUAL:
0998: break;
0999:
1000: default:
1001: state = INIT;
1002: }
1003: break;
1004:
1005: case CCTokenContext.SEMICOLON_ID:
1006: if (state == AFTER_MATCHING_VARIABLE) {
1007: processDeclaration();
1008: }
1009: state = INIT;
1010: break;
1011:
1012: case CCTokenContext.IDENTIFIER_ID:
1013: switch (state) {
1014: case AFTER_TYPE:
1015: case AFTER_COMMA:
1016: if (varName == null
1017: || Analyzer.equals(varName, buffer,
1018: tokenOffset, tokenLen)) {
1019: decArrayDepth = arrayDepth;
1020: decVarNameOffset = tokenOffset;
1021: decVarNameLen = tokenLen;
1022: state = AFTER_MATCHING_VARIABLE;
1023: } else {
1024: state = AFTER_VARIABLE;
1025: }
1026: break;
1027:
1028: case AFTER_VARIABLE: // error
1029: state = INIT;
1030: break;
1031:
1032: case AFTER_EQUAL:
1033: break;
1034:
1035: case AFTER_DOT:
1036: typeEndPos = pos + tokenLen;
1037: state = AFTER_TYPE;
1038: break;
1039:
1040: case AFTER_ARROW:
1041: typeEndPos = pos + tokenLen;
1042: state = AFTER_VARIABLE;
1043: break;
1044:
1045: case AFTER_SCOPE: // only valid after type
1046: typeEndPos = pos + tokenLen;
1047: state = AFTER_TYPE;
1048: break;
1049:
1050: case INIT:
1051: typeStartPos = pos;
1052: arrayDepth = 0;
1053: typeEndPos = pos + tokenLen;
1054: state = AFTER_TYPE;
1055: break;
1056:
1057: default:
1058: state = INIT;
1059: break;
1060: }
1061: break;
1062:
1063: case CCTokenContext.WHITESPACE_ID: // whitespace ignored
1064: break;
1065:
1066: case CCTokenContext.COLON_ID: // 1.5 enhanced for loop sysntax
1067: processDeclaration();
1068:
1069: // case CCTokenContext.INSTANCEOF_ID:
1070: default:
1071: state = INIT;
1072: }
1073:
1074: return true;
1075: }
1076:
1077: public int eot(int offset) {
1078: return 0;
1079: }
1080:
1081: public void nextBuffer(char[] buffer, int offset, int len,
1082: int startPos, int preScan, boolean lastBuffer) {
1083: this .buffer = buffer;
1084: bufferStartPos = startPos - offset;
1085: }
1086:
1087: }
1088:
1089: ////////////////////////////////////////////////
1090: // overriden functions to resolve expressions
1091: /////////////////////////////////////////////////
1092:
1093: /** Map holding the [position, class-fields-map] pairs */
1094: private HashMap classFieldMaps = new HashMap();
1095:
1096: /** Map holding the [position, class-fields-map] pairs */
1097: private HashMap fileVariableMaps = new HashMap();
1098:
1099: /** Find the type of the variable. The default behavior is to first
1100: * search for the local variable declaration and then possibly for
1101: * the global declaration and if the declaration position is found
1102: * to get the first word on that position.
1103: * @return it returns Object to enable the custom implementations
1104: * to return the appropriate instances.
1105: */
1106: @Override
1107: public Object findType(String varName, int varPos) {
1108: CsmType type = null;
1109: Map varMap = getLocalVariableMap(varPos); // first try local vars
1110: if (varMap != null) {
1111: type = (CsmType) varMap.get(varName);
1112: }
1113:
1114: // then try class fields
1115: if (type == null) {
1116: varMap = getClassFieldMap(varPos); // try class fields
1117: if (varMap != null) {
1118: type = (CsmType) varMap.get(varName);
1119: }
1120: }
1121:
1122: // then try file local vars
1123: if (type == null) {
1124: varMap = getFileVariableMap(varPos); // try file local vars
1125: if (varMap != null) {
1126: type = (CsmType) varMap.get(varName);
1127: }
1128: }
1129:
1130: // at the end - globals
1131: if (type == null) {
1132: varMap = getGlobalVariableMap(varPos); // try global vars
1133: if (varMap != null) {
1134: type = (CsmType) varMap.get(varName);
1135: }
1136: }
1137:
1138: return type;
1139: }
1140:
1141: public Map getClassFieldMap(int offset) {
1142: Integer posI = new Integer(offset);
1143: Map varMap = (Map) classFieldMaps.get(posI);
1144: if (varMap == null) {
1145: varMap = buildClassFieldMap(offset);
1146: classFieldMaps.put(posI, varMap);
1147: }
1148: return varMap;
1149: }
1150:
1151: public Map getFileVariableMap(int offset) {
1152: Integer posI = new Integer(offset);
1153: Map varMap = (Map) fileVariableMaps.get(posI);
1154: if (varMap == null) {
1155: varMap = buildFileVariableMap(offset);
1156: fileVariableMaps.put(posI, varMap);
1157: }
1158: return varMap;
1159: }
1160:
1161: ///////////////////////////////////////////////////////////////////////////
1162: // build variable maps
1163: ///////////////////////////////////////////////////////////////////////////
1164:
1165: @Override
1166: protected Map buildLocalVariableMap(int offset) {
1167: int methodStartPos = getMethodStartPosition(offset);
1168: if (methodStartPos >= 0 && methodStartPos < offset) {
1169: List res = CompletionUtilities.findFunctionLocalVariables(
1170: getDocument(), offset);
1171: return list2Map(res);
1172: }
1173: return null;
1174: }
1175:
1176: @Override
1177: protected Map buildGlobalVariableMap(int offset) {
1178: List res = CompletionUtilities.findGlobalVariables(
1179: getDocument(), offset);
1180: return list2Map(res);
1181: }
1182:
1183: protected Map buildClassFieldMap(int offset) {
1184: List res = CompletionUtilities.findClassFields(getDocument(),
1185: offset);
1186: return list2Map(res);
1187: }
1188:
1189: protected Map buildFileVariableMap(int offset) {
1190: List res = CompletionUtilities.findFileVariables(getDocument(),
1191: offset);
1192: return list2Map(res);
1193: }
1194:
1195: // utitlies
1196:
1197: private Map/*<var-name, CsmType>*/list2Map(
1198: List/*<CsmVariable>*/vars) {
1199: if (vars == null || vars.size() == 0) {
1200: return null;
1201: }
1202: Map res = new StringMap();
1203: for (Iterator it = vars.iterator(); it.hasNext();) {
1204: Object elem = it.next();
1205: if (elem instanceof CsmVariable) {
1206: CsmVariable var = (CsmVariable) elem;
1207: res.put(var.getName().toString(), var.getType());
1208: }
1209: }
1210: return res;
1211: }
1212:
1213: @Override
1214: protected boolean isAbbrevDisabled(int offset) {
1215: boolean abbrevDisabled = false;
1216: TokenID[] disableTokenIds = BRACKET_SKIP_TOKENS;
1217: if (disableTokenIds != null) {
1218: TokenItem token;
1219: try {
1220: token = getTokenChain(offset, offset + 1);
1221: } catch (BadLocationException e) {
1222: token = null;
1223: }
1224: if (token != null) {
1225: if (offset > token.getOffset()) { // not right at token's begining
1226: for (int i = disableTokenIds.length - 1; i >= 0; i--) {
1227: if (token.getTokenID() == disableTokenIds[i]) {
1228: abbrevDisabled = true;
1229: break;
1230: }
1231: }
1232: }
1233: if (!abbrevDisabled) { // check whether not right after line comment
1234: if (token.getOffset() == offset) {
1235: TokenItem prevToken = token.getPrevious();
1236: if (prevToken != null
1237: && prevToken.getTokenID() == CCTokenContext.LINE_COMMENT) {
1238: abbrevDisabled = true;
1239: }
1240: }
1241: }
1242: }
1243: }
1244: return abbrevDisabled;
1245: }
1246:
1247: public boolean isIncludeCompletionDisabled(int offset) {
1248: boolean completionDisabled = true;
1249: TokenItem token = getTokenItem(offset);
1250: token = shiftToNonWhiteBwd(token);
1251: if (token != null) {
1252: completionDisabled = !isOffsetInToken(token,
1253: INCLUDE_COMPLETION_TOKENS, offset);
1254: if (completionDisabled) {
1255: // check whether right after #include or #include_next directive
1256: switch (token.getTokenID().getNumericID()) {
1257: case CCTokenContext.CPPINCLUDE_ID:
1258: case CCTokenContext.CPPINCLUDE_NEXT_ID:
1259: return completionDisabled = false;
1260: }
1261: }
1262: }
1263: return completionDisabled;
1264: }
1265:
1266: public TokenItem getTokenItem(int offset) {
1267: TokenItem token;
1268: try {
1269: int checkOffset = offset;
1270: if (offset == getDocument().getLength()) {
1271: if (offset == 0) {
1272: return null;
1273: }
1274: checkOffset--;
1275: }
1276: token = getTokenChain(checkOffset, checkOffset + 1);
1277: } catch (BadLocationException e) {
1278: token = null;
1279: }
1280: return token;
1281: }
1282:
1283: public TokenItem shiftToNonWhiteBwd(TokenItem token) {
1284: if (token == null) {
1285: return null;
1286: }
1287: boolean checkedFirst = false;
1288: do {
1289: switch (token.getTokenID().getNumericID()) {
1290: case CCTokenContext.WHITESPACE_ID:
1291: if (checkedFirst) {
1292: if (token.getImage().contains("\n")) { // NOI18N
1293: return null;
1294: }
1295: }
1296: break;
1297: case CCTokenContext.BLOCK_COMMENT_ID:
1298: // skip
1299: break;
1300: default:
1301: return token;
1302: }
1303: token = token.getPrevious();
1304: checkedFirst = true;
1305: } while (token != null);
1306: return null;
1307: }
1308:
1309: private boolean isAfterInclude(int offset) {
1310: try {
1311: if (offset == getDocument().getLength()) {
1312: offset--;
1313: }
1314: int rowStart = Utilities.getRowStart(getDocument(), offset);
1315: TokenItem item = getTokenChain(rowStart, rowStart + 1);
1316: if (item != null) {
1317: switch (item.getTokenID().getNumericID()) {
1318: case CCTokenContext.CPPINCLUDE_ID:
1319: case CCTokenContext.CPPINCLUDE_NEXT_ID:
1320: while (item != null) {
1321: item = item.getNext();
1322: if (item != null
1323: && (item.getOffset() < offset)
1324: && (item.getTokenID() != CCTokenContext.WHITESPACE)) {
1325: return false;
1326: }
1327: }
1328: return true;
1329: }
1330: }
1331: } catch (BadLocationException ex) {
1332: // skip
1333: }
1334: return false;
1335: }
1336:
1337: public boolean isCompletionDisabled(int offset) {
1338: boolean completionDisabled = false;
1339: TokenID[] disableTokenIds = COMPLETION_SKIP_TOKENS;
1340: if (disableTokenIds != null) {
1341: TokenItem token;
1342: try {
1343: token = getTokenChain(offset, offset + 1);
1344: } catch (BadLocationException e) {
1345: token = null;
1346: }
1347: if (token != null) {
1348: if (offset > token.getOffset()) { // not right at token's begining
1349: for (int i = disableTokenIds.length - 1; i >= 0; i--) {
1350: if (token.getTokenID() == disableTokenIds[i]) {
1351: completionDisabled = true;
1352: break;
1353: }
1354: }
1355: }
1356: if (!completionDisabled) { // check whether not right after line comment or float constant
1357: if (token.getOffset() == offset) {
1358: TokenItem prevToken = token.getPrevious();
1359: if (prevToken != null
1360: && (prevToken.getTokenID() == CCTokenContext.LINE_COMMENT
1361: || prevToken.getTokenID() == CCTokenContext.FLOAT_LITERAL || prevToken
1362: .getTokenID() == CCTokenContext.DOUBLE_LITERAL)) {
1363: completionDisabled = true;
1364: }
1365: }
1366: }
1367: }
1368: }
1369: return completionDisabled;
1370: }
1371:
1372: public boolean needShowCompletionOnText(JTextComponent target,
1373: String typedText) throws BadLocationException {
1374: boolean showCompletion = false;
1375: char typedChar = typedText.charAt(0);
1376: if (typedChar == ' ' || typedChar == '>' || typedChar == ':'
1377: || typedChar == '.' || typedChar == '*') {
1378:
1379: int dotPos = target.getCaret().getDot();
1380: BaseDocument doc = (BaseDocument) target.getDocument();
1381: TokenItem item = getTokenChain(dotPos - 1, dotPos);
1382: TokenItem prev = null;
1383: if (typedChar == ' ' || typedChar == '.') { // init prev for space and dot
1384: try {
1385: prev = item == null ? null : item.getPrevious();
1386: } catch (IllegalStateException ex) {
1387: prev = null;
1388: }
1389: }
1390: switch (typedChar) {
1391: case ' ': // completion after "new" keyword
1392: if (prev != null
1393: && prev.getTokenID() == CCTokenContext.NEW) {
1394: showCompletion = true;
1395: }
1396: break;
1397: case '>': // completion after arrow
1398: if (item != null
1399: && item.getTokenID() == CCTokenContext.ARROW) {
1400: showCompletion = true;
1401: }
1402: break;
1403: case '.': // completion after dot
1404: showCompletion = true;
1405: // hide completion in inlclude strings
1406: if (item != null
1407: && (item.getTokenID().getCategory() == CCTokenContext.ERRORS
1408: || item.getTokenID() == CCTokenContext.USR_INCLUDE || item
1409: .getTokenID() == CCTokenContext.SYS_INCLUDE)) {
1410: showCompletion = false;
1411: } else if (prev != null
1412: && prev.getTokenID() == CCTokenContext.DOT) {
1413: showCompletion = false;
1414: }
1415: break;
1416: case '*': // completion after star
1417: if (item != null
1418: && (item.getTokenID() == CCTokenContext.ARROWMBR || item
1419: .getTokenID() == CCTokenContext.DOTMBR)) {
1420: showCompletion = true;
1421: }
1422: break;
1423: case ':': // completion after scope
1424: if (item != null
1425: && item.getTokenID() == CCTokenContext.SCOPE) {
1426: showCompletion = true;
1427: }
1428: break;
1429: }
1430: }
1431: return showCompletion;
1432: }
1433:
1434: private boolean equalTypes(CsmType t, CsmType mpt) {
1435: assert t != null;
1436: if (t.equals(mpt)) {
1437: return true;
1438: } else if (mpt != null) {
1439: String t1 = t.getCanonicalText().toString();
1440: String t2 = mpt.getCanonicalText().toString();
1441: return t1.equals(t2);
1442: }
1443: return false;
1444: }
1445: }
|