0001: package Schmortopf.JavaSourceEditor.CodeCompletion;
0002:
0003: /**
0004: * This is the worker class of the CodeCompletion.
0005: *
0006: * An instance of this class is created by the
0007: * CodeCompletion object for each search.
0008: *
0009: */
0010:
0011: import java.util.*;
0012: import java.awt.*;
0013: import java.awt.event.*;
0014: import javax.swing.*;
0015: import javax.swing.event.*;
0016: import javax.swing.text.*;
0017: import java.lang.reflect.Modifier;
0018:
0019: import Schmortopf.FileStructure.*;
0020: import Schmortopf.FileStructure.Descriptions.*;
0021: import Schmortopf.JavaSourceEditor.*;
0022: import Schmortopf.JavaSourceEditor.DocumentStyle.*;
0023: import Schmortopf.Utility.BooleanInteger;
0024: import Schmortopf.Utility.StringUtilities;
0025: import Language.Language;
0026: import Shared.Logging.Log;
0027:
0028: public class CodeCompletionSearch {
0029:
0030: private final String basisIdentifier;
0031: private int insertPosition;
0032: private EditorPanel editorPanel;
0033: private FileStructureDescription toplevelBasisFSD;
0034: private FileStructureDescriptionManager fileStructureDescriptionManager;
0035:
0036: private String foundIdentifierName = "";
0037: private FSDSearchResult fsdSearchResult = null;
0038: // This is set and can be obtained, after performSearch() has found a result.
0039: // Depending on the search type, this can be an objectname or a classname
0040:
0041: // All fsd's for which we updated their transient fields are added to
0042: // this Vector, so we can release all transient fields before this object
0043: // is disposed. Prevents memory explosions.
0044: // See freeAllTransientFields().
0045: private Vector fsdsWithUpdatedTransientFields = new Vector();
0046:
0047: /**
0048: * Constructor.
0049: *
0050: * NOTE: You MUST call freeAllTransientFields(), when the object isn't used anymore,
0051: * so that any updated transient fields are released.
0052: * Otherwise the memory explodes.
0053: *
0054: */
0055: public CodeCompletionSearch(
0056: final String basisIdentifier,
0057: final int insertPosition,
0058: final EditorPanel editorPanel,
0059: final FileStructureDescription currentlyEditedFSD,
0060: final FileStructureDescriptionManager fileStructureDescriptionManager) {
0061: this .basisIdentifier = basisIdentifier;
0062: this .insertPosition = insertPosition;
0063: this .editorPanel = editorPanel;
0064: this .toplevelBasisFSD = currentlyEditedFSD;
0065: this .fileStructureDescriptionManager = fileStructureDescriptionManager;
0066: } // Constructor
0067:
0068: /**
0069: * The method, which performs the search using the
0070: * parameters passed to the constructor.
0071: *
0072: * Note: This is called from OUTSIDE the eventdispatch thread.
0073: */
0074: public CodeCompletionListEntry[] performSearch(
0075: final int caretLineNumber) {
0076:
0077: //Log.Info("started for basisIdentifier = " + this.basisIdentifier );
0078: //long startTime = System.currentTimeMillis();
0079: //new Throwable().printStackTrace();
0080:
0081: CodeCompletionListEntry[] codeCompletionListEntryArray = null; // the return type
0082:
0083: // This is used for storing the search result of this search.
0084: // It can be fetched later using this.getFoundFSDSearchResult().
0085: this .fsdSearchResult = null;
0086:
0087: // The caret can lie in one or multiple anonymous classes.
0088: // Therefore we create the chain of anonymous classes containing the caret position,
0089: // which then is used for tracking additional visible objects in the
0090: // deepest anonymous class the caret is in, and for limiting the scope of all
0091: // pther classes including the toplevel class, which still is the one containing
0092: // the outer scope given by the package scope and the import statements.
0093: // Chain order: The deepest anonymous class is the last entry.
0094: FileStructureDescription[] anonymousClassChain = this
0095: .createTheAnonymousClassChainFor(this .toplevelBasisFSD,
0096: caretLineNumber);
0097:
0098: // One exception: If the basisIdentifier starts with a " sign, it is a String literal
0099: // and we don't have to search :
0100: if (this .basisIdentifier.length() > 0) {
0101: if (this .basisIdentifier.charAt(0) == '"') {
0102: // It is a String literal, so we don't have to search :
0103: FileStructureDescription fsd = this .fileStructureDescriptionManager
0104: .searchFSD("java.lang.String");
0105: // Like in the (usual) case below, where we actually search the fsd, we also have
0106: // to update the transient fields here :
0107: if (fsd != null) {
0108: // Set it :
0109: this .fsdSearchResult = new FSDSearchResult(fsd,
0110: false);
0111: // Caution: Always use this member method for updating the transient fields,
0112: // so they are released again when freeAllTransientFields() is called.
0113: this .updateTransientFieldsFor(fsd);
0114: //ystem.out.println("CC> String literal detected");
0115: }
0116: }
0117: }
0118: // Search ( if it hasn't been found above already ):
0119: if (this .fsdSearchResult == null) {
0120: this .fsdSearchResult = this
0121: .search_FileStructureDescription(caretLineNumber,
0122: anonymousClassChain);
0123: }
0124:
0125: // Create the codeCompletionListEntryArray, if the FSDSearchResult contains an fsd.
0126: // If it only contains a simpletype we are finished here. The result still is used
0127: // in this case by calls to this.getFoundFSDSearchResult() at a later time maybe.
0128:
0129: // Continue, if we have found a result with an fsd :
0130:
0131: //boolean debuuug = (this.fsdSearchResult != null);
0132: //ystem.out.println(" this.fsdSearchResult != null is " + debuuug );
0133:
0134: if (this .fsdSearchResult != null) {
0135: if ((this .fsdSearchResult.getFileStructureDescription() != null)
0136: && (!this .fsdSearchResult.getIsJavaArrayObject())) {
0137:
0138: //ystem.out.println("CCSearch.performSearch(): fsd result has been found.");
0139:
0140: // Set the name of the found class, so that it can be retrieved later by the caller:
0141: this .foundIdentifierName = this .fsdSearchResult
0142: .getFileStructureDescription().className.content;
0143: // If the basisIdentifier is a point-delimited identifier, where
0144: // the last token has the name of the class, or if it's a simple string
0145: // which is the same as the simple classname, it's a
0146: // class reference and in this case, we ONLY must show its
0147: // static class attributes and methods :
0148: boolean onlyShowStaticAttributesAndMethods = false;
0149: String[] tokens = this .decompose(this .basisIdentifier);
0150: String lastToken = tokens[tokens.length - 1];
0151: //ystem.out.println("CCSearch LastToken = " + lastToken );
0152: if (lastToken
0153: .equals(this .fsdSearchResult
0154: .getFileStructureDescription().className.content)) {
0155: onlyShowStaticAttributesAndMethods = true;
0156: // For better user feedback, we tell the user in the title, that only
0157: // static members will be displayed :
0158: this .foundIdentifierName += " ["
0159: + Language.Translate("class contents")
0160: + "]";
0161: }
0162: // Collect the entries :
0163: codeCompletionListEntryArray = this
0164: .selectVisibleEntriesFrom(this .fsdSearchResult
0165: .getFileStructureDescription(),
0166: onlyShowStaticAttributesAndMethods,
0167: anonymousClassChain);
0168: } else {
0169: if (this .fsdSearchResult.getIsJavaArrayObject()) {
0170: // A Java array object just has one single member attribute, which is length :
0171: // Set the name of the found class, so that it can be retrieved later by the caller:
0172: this .foundIdentifierName = Language
0173: .Translate("Java Array Object");
0174:
0175: FileStructureDescriptionForField arrayAttribute = new FileStructureDescriptionForField(
0176: 0, true, new BooleanInteger(false, 0), "",
0177: "int", "length");
0178: codeCompletionListEntryArray = new CodeCompletionListEntry[1];
0179: codeCompletionListEntryArray[0] = new CodeCompletionListEntry(
0180: this .fsdSearchResult
0181: .getFileStructureDescription(),
0182: this .fileStructureDescriptionManager,
0183: arrayAttribute, true) {
0184: public void run() {
0185: try {
0186: final int offset = insertPosition;
0187: final String string = "length";
0188: final Style style = StylesFactory
0189: .GetEditorTextStyle();
0190: editorPanel.getEditor().getDocument()
0191: .insertString(offset, string,
0192: style);
0193: } catch (Exception ouiwermnb) {
0194: ouiwermnb.printStackTrace();
0195: }
0196: }
0197: };
0198: }
0199: }
0200: } // if fsdSearchResult != null
0201:
0202: /* Debug Output Start -----------------
0203: Log.Info("CC>> CCSearch.performSearch() Result:");
0204: if( codeCompletionListEntryArray == null )
0205: {
0206: Log.Info("CC>> codeCompletionListEntryArray is null.");
0207: }
0208: else
0209: {
0210: for( int i=0; i < codeCompletionListEntryArray.length; i++ )
0211: {
0212: Log.Info("CC>> Entry " + i + " : ");
0213: CodeCompletionListEntry entry = (CodeCompletionListEntry)codeCompletionListEntryArray[i];
0214: if( entry.parentFSD == null )
0215: {
0216: Log.Info("CC>> parentFSD: null");
0217: }
0218: else
0219: {
0220: Log.Info("CC>> parentFSD: " + entry.parentFSD.fullyQualifiedClassNameBuffer.toString() );
0221: }
0222:
0223: if( entry.methodFSD == null )
0224: {
0225: Log.Info("CC>> methodFSD: null");
0226: }
0227: else
0228: {
0229: Log.Info("CC>> methodFSD: " + entry.methodFSD.getShortTextDescription( false,true ) );
0230: }
0231:
0232: if( entry.fieldFSD == null )
0233: {
0234: Log.Info("CC>> fieldFSD: null");
0235: }
0236: else
0237: {
0238: Log.Info("CC>> fieldFSD: " + entry.fieldFSD.getFullyQualifiedClassName() );
0239: }
0240:
0241: if( entry.parameters == null )
0242: {
0243: Log.Info("CC>> parameters: null");
0244: }
0245: else
0246: {
0247: Log.Info("CC>> parameters: #elements= " + entry.parameters.length );
0248: }
0249: Log.Info("CC>> isTopLevel: " + entry.getIsTopLevel() );
0250: }
0251: } // else
0252: // Debug Output End ----------------- */
0253:
0254: //long elapsedTime = System.currentTimeMillis() - startTime;
0255: //Log.Info("ends. elapsed time [ms] " + elapsedTime );
0256: return codeCompletionListEntryArray;
0257: } // performSearch
0258:
0259: /**
0260: * The caret can lie in one or multiple anonymous classes.
0261: * Therefore we create the chain of anonymous classes containing the caret position,
0262: * which then is used for tracking additional visible objects in the
0263: * deepest anonymous class the caret is in, and for limiting the scope of all
0264: * other classes including the toplevel class, which still is the one containing
0265: * the outer scope given by the package scope and the import statements.
0266: * Chain order: The deepest anonymous class is the last entry.
0267: *
0268: * toplevelBasisFSD must be an FSD associated with a file displayed int the editor,
0269: * with associated caretLineNumber.
0270: */
0271: public FileStructureDescription[] createTheAnonymousClassChainFor(
0272: FileStructureDescription toplevelBasisFSD,
0273: int caretLineNumber) {
0274: Vector anonymousClassChainVectorForCaretPosition = new Vector();
0275: // First just add all, which contain the caretposition :
0276: for (int i = 0; i < this .toplevelBasisFSD.localBlockAnonymousClasses
0277: .size(); i++) {
0278: FileStructureDescription anonymousFSD = (FileStructureDescription) this .toplevelBasisFSD.localBlockAnonymousClasses
0279: .elementAt(i);
0280: if ((caretLineNumber >= anonymousFSD.scopeStartLine)
0281: && (caretLineNumber <= anonymousFSD.scopeEndLine)) {
0282: anonymousClassChainVectorForCaretPosition
0283: .addElement(anonymousFSD);
0284: }
0285: } // for
0286: // Then sort them : [on the fly - normally its only one or two anonymous classes..]
0287: FileStructureDescription iFSD = null;
0288: int iSpan = 0;
0289: FileStructureDescription kFSD = null;
0290: int kSpan = 0;
0291: for (int i = 0; i < anonymousClassChainVectorForCaretPosition
0292: .size(); i++) {
0293: iFSD = (FileStructureDescription) anonymousClassChainVectorForCaretPosition
0294: .elementAt(i);
0295: iSpan = iFSD.scopeEndLine - iFSD.scopeStartLine;
0296: for (int k = anonymousClassChainVectorForCaretPosition
0297: .size() - 1; k > i; k--) {
0298: kFSD = (FileStructureDescription) anonymousClassChainVectorForCaretPosition
0299: .elementAt(k);
0300: kSpan = kFSD.scopeEndLine - kFSD.scopeStartLine;
0301: if (kSpan > iSpan) {
0302: anonymousClassChainVectorForCaretPosition
0303: .setElementAt(kFSD, i);
0304: anonymousClassChainVectorForCaretPosition
0305: .setElementAt(iFSD, k);
0306: iFSD = kFSD;
0307: iSpan = kSpan;
0308: }
0309: }
0310: }
0311: // Put it into an array :
0312: FileStructureDescription[] anonymousClassChain = new FileStructureDescription[anonymousClassChainVectorForCaretPosition
0313: .size()];
0314: anonymousClassChainVectorForCaretPosition
0315: .copyInto(anonymousClassChain);
0316: anonymousClassChainVectorForCaretPosition.setSize(0);
0317: return anonymousClassChain;
0318: } // createTheAnonymousClassChainFor
0319:
0320: /**
0321: * Once performSearch has been called, this will return the
0322: * name of the class or object associated with the found fsd.
0323: * It is used for the CC popup title.
0324: */
0325: public String getFoundIdentifierName() {
0326: return this .foundIdentifierName;
0327: }
0328:
0329: /**
0330: * Once performSearch() has been called, this will return the
0331: * found fsd search result.
0332: */
0333: public FSDSearchResult getFoundFSDSearchResult() {
0334: return this .fsdSearchResult;
0335: }
0336:
0337: /**
0338: * Searches the FSD given by the basisIdentifier taking into account
0339: * the scopes given by the caretLineNumber in the currentlyEditedFSD.
0340: *
0341: * Note: The returned fsd will have transient fields set, and the
0342: * caller MUST release them, otherwise the memory will
0343: * increase on every call unnecessarily.
0344: */
0345: public FSDSearchResult search_FileStructureDescription(
0346: final int caretLineNumber,
0347: FileStructureDescription[] anonymousClassChain) {
0348: // Caution: Always use this member method for updating the transient fields,
0349: // so they are released again when freeAllTransientFields() is called.
0350: this .updateTransientFieldsFor(this .toplevelBasisFSD);
0351:
0352: FileStructureDescription currentResultDescription = null;
0353: String simpletype = null;
0354:
0355: boolean isJavaArrayObject = false;
0356: // The raw basisIdentifier can have the form a.b.c
0357: // where all tokens can require translation.
0358: // Easy Example : System.
0359: // Complex but still easy : System.out.
0360: // Example which needs translation : MyClass.GetMemberAttribute().
0361: // where getMemberAttribute() returns the class util.TestClass
0362: // is translated into class util.TestClass.
0363: // Same goes for just a nonstatic method getMemberAttribute().
0364: // -> Methods have to be replaced by their returntypes.
0365: //
0366: // Additionally the first few tokens can form a package name.
0367: //
0368: // Note, that typecasts at the beginning will be removed :
0369: // If the identifier has the form (type)a.b.c, the typecast (type)
0370: // just will be removed, because it is of no interest for the CodeCompletion
0371: // and of course would deny proper operation.
0372: //
0373: // Additionally the identifier can start with "new XXX", which means that
0374: // XXX is created.
0375:
0376: // decompose() decomposes after point delimiters, but is able to distinguish
0377: // the parser tree levels created by openening/closing brackets. It only
0378: // decomposes the toplevel.
0379: final String[] basisIdentifierTokens = this
0380: .decompose(basisIdentifier);
0381:
0382: /*
0383: for( int i=0; i < basisIdentifierTokens.length; i++ )
0384: {
0385: Log.Info("basisIdentifierTokens["+i+"]= " + basisIdentifierTokens[i] );
0386: }
0387: */
0388:
0389: // Create an array which informs for each identifier, if a cast has priority,
0390: // or if just the last token gives the result for the CC:
0391: boolean[] castHasPriority = new boolean[basisIdentifierTokens.length];
0392: String id;
0393: for (int i = 0; i < basisIdentifierTokens.length; i++) {
0394: // If one of the elements starts with an opening bracket, which
0395: // only is closed at the end of the string, remove both brackets:
0396: id = basisIdentifierTokens[i];
0397: castHasPriority[i] = false;
0398: while (id.startsWith("(")
0399: && this .getPositionOfClosingBracket(id, 0) == id
0400: .length() - 1) {
0401: id = id.substring(1, id.length() - 1);
0402: castHasPriority[i] = true;
0403: }
0404: basisIdentifierTokens[i] = id;
0405: // If castHasPriority is false and there is a typecast, remove it,
0406: // so the detection works:
0407: if (!castHasPriority[i]) {
0408: // In this case, possible typecasts at the beginning of the line have no
0409: // effect for the CC, because the FSD is determined by the type of the last
0410: // identifier, so remove the typecast, otherwise it would make the detections fail:
0411: basisIdentifierTokens[i] = this
0412: .checkRemoveCastIn(basisIdentifierTokens[i]);
0413: }
0414: } // for
0415:
0416: FSDSearchResult fsdSearchResult = null;
0417: if (basisIdentifierTokens.length > 0) {
0418: final StringBuffer currentIdentifier = new StringBuffer(
0419: this .basisIdentifier.length());
0420: currentIdentifier.append(basisIdentifierTokens[0]);
0421: FileStructureDescription currentScopeDescription = this .toplevelBasisFSD; // scope init
0422: // Flag: Once an FSD has been found (after we went through a complete
0423: // qualifier part, like java.lang.System, all subsequent loop
0424: // runs must find an FSD as well - if one subsequent FSD search
0425: // fails, the basisIdentifier does not point to an existent
0426: // FSD and we must break.
0427: boolean initialFSDHasBeenFound = false;
0428:
0429: //ystem.out.println("CCSearch: nr of tokens = " + basisIdentifierTokens.length );
0430:
0431: // Accumulate: Pass the tokens one after each other while following
0432: // with the FileStructureDescriptions. In each run, the algorithm tries
0433: // to localize the current single token (without points) in the
0434: // FileStructureDescription found on the previous runs.
0435:
0436: // previousIdentifier is used for detecting scope extensions of the form
0437: // <Class>this.
0438: // which is used in inner classes or anonymous classes for
0439: // accessing member objects of the enclosing toplevel class.
0440: String previousIdentifier = ""; // empty string initially
0441:
0442: for (int tokenIndex = 0; tokenIndex < basisIdentifierTokens.length; tokenIndex++) {
0443:
0444: if (tokenIndex > 0) // see comments above for the meaning of previousIdentifier
0445: {
0446: previousIdentifier = basisIdentifierTokens[tokenIndex - 1];
0447: }
0448:
0449: //ystem.out.println("++++++++++++++++++++++++++++++ search_FileStructureDescription run ++++++++++++++ ");
0450: //ystem.out.println("+> basisIdentifierTokens[tokenIndex] = " + basisIdentifierTokens[tokenIndex] );
0451: //ystem.out.println("+> castHasPriority[ tokenIndex ] = " + castHasPriority[ tokenIndex ] );
0452:
0453: fsdSearchResult = null; // reset for each loop run
0454:
0455: // Special processing for anonymous classes:
0456: // The passed anonymousClassChain is related to (caretpositions of) the
0457: // toplevelBasisFSD, therefore we MUST clear it, when the current scope
0458: // leaves that FSD inside this loop :
0459: if (currentScopeDescription != this .toplevelBasisFSD) {
0460: // Setting zero length signalizes no anynomous chain :
0461: anonymousClassChain = new FileStructureDescription[0];
0462: }
0463:
0464: // Special case for a term enclosed by brackets, which contains a typecast
0465: // at its beginning: In this case, the FSD is given by the typecast,
0466: // and additionally:
0467: // The enclosing brackets have already been removed below and castHasPriority[i] is true:
0468: if (castHasPriority[tokenIndex]) {
0469:
0470: //ystem.out.println(".....................................castHasPriority TRUE");
0471:
0472: // 0) Test, if the identifier statement starts with a typecast.
0473: // If it does, try to resolve the FSD of the typecast and take it as result:
0474: // The number of the typeCast attribute is the array dimension. 1 for single types.
0475: NumberedString typeCast = this
0476: .checkReturnCastIn(basisIdentifierTokens[tokenIndex]);
0477: if (typeCast.content != null) {
0478:
0479: //ystem.out.println("CCSearch: search_FileStructureDescription: CAST DETECTED.");
0480:
0481: // Although we don't have a field here, getFSDForField() does right what one wants:
0482: FileStructureDescription castFSD = this .fileStructureDescriptionManager
0483: .getFSDForField(typeCast.content,
0484: currentScopeDescription);
0485: if (castFSD != null) {
0486:
0487: //ystem.out.println("CCSearch: search_FileStructureDescription: CAST RESOLVED: class= " +
0488: // castFSD.fullyQualifiedClassNameBuffer.toString() );
0489:
0490: // typeCast.number == array dimension [ 0 for single types ]
0491: boolean is_Array = (typeCast.number > 0);
0492: fsdSearchResult = new FSDSearchResult(
0493: castFSD, is_Array);
0494: }
0495: } // if
0496: else {
0497: //ystem.out.println("CCSearch: search_FileStructureDescription: checkReturnCastIn returned FALSE");
0498: }
0499: } else {
0500: //ystem.out.println(".....................................castHasPriority FALSE");
0501: }
0502:
0503: // 1) Test, if the identifier statement is an object creation and starts
0504: // with the keyword "new" followed by a non java identifier character.
0505: // If this is the case, we get the classname directly :
0506: if (fsdSearchResult == null) {
0507: fsdSearchResult = this
0508: .searchForClassCreatorStatement(
0509: caretLineNumber,
0510: currentScopeDescription,
0511: currentIdentifier.toString());
0512: }
0513:
0514: // 2) not found ? -> Search for an FSD in the current scope (includes importstatements) :
0515: // The scope is given by currentDescription ( = currentlyEditedFSD initially, see above)
0516: // If the cursor is inside anonymous classes, the scope is updated accordingly.
0517: if ((fsdSearchResult == null)
0518: && (previousIdentifier != null)) {
0519: fsdSearchResult = this .searchForClassFSD(
0520: caretLineNumber, currentScopeDescription,
0521: currentIdentifier.toString(),
0522: anonymousClassChain, previousIdentifier);
0523: /* Debug:
0524: if( foundClassFSD != null )
0525: {
0526: ystem.out.println("XX searchForClassFSD->fsd= " + foundClassFSD.className.content );
0527: ystem.out.println("XX searchForClassFSD->inheritedmethods= " + foundClassFSD.inheritedMethodDescriptions.size() );
0528: ystem.out.println("XX searchForClassFSD->inheritedfields= " + foundClassFSD.inheritedFieldDescriptions.size() );
0529: }
0530: else
0531: {
0532: ystem.out.println("XX searchForClassFSD->No fsd found");
0533: }
0534: */
0535:
0536: } // if
0537:
0538: // 3) not found ? -> Search for a local or a member attribute class in the current scope :
0539: if (fsdSearchResult == null) {
0540: //ystem.out.println("CC: Search for member attribute class");
0541: fsdSearchResult = this .searchForAttributeClassFSD(
0542: currentScopeDescription,
0543: currentScopeDescription, currentIdentifier
0544: .toString(), caretLineNumber,
0545: anonymousClassChain);
0546: }
0547:
0548: // 4) not found ? -> Search for a method in the current scope :
0549: if (fsdSearchResult == null) {
0550: //ystem.out.println("CC: Search for method returntype class");
0551: fsdSearchResult = this
0552: .searchForMethodReturnTypeFSD(
0553: currentScopeDescription,
0554: currentIdentifier.toString(),
0555: caretLineNumber,
0556: anonymousClassChain);
0557: }
0558:
0559: // Search completed - adjust parameters for the next run,
0560: // if we have found a non-simple result :
0561: if ((fsdSearchResult != null)
0562: && (fsdSearchResult
0563: .getFileStructureDescription() != null)) {
0564:
0565: //ystem.out.println("CCSearch: search_FileStructureDescription: fsdSearchResult FOUND");
0566:
0567: // Update the transient fields, as stated in the method comment.
0568: // This is needed for tasks of the caller, as well as for tasks
0569: // in one of the above methods, when this fsd comes in as argument
0570: // in the next for loop run :
0571: // Caution: Always use this member method for updating the transient fields,
0572: // so they are released again when freeAllTransientFields() is called.
0573: this .updateTransientFieldsFor(fsdSearchResult
0574: .getFileStructureDescription());
0575: // Set flag:
0576: initialFSDHasBeenFound = true;
0577: // Reset the current scope FSD :
0578: currentScopeDescription = fsdSearchResult
0579: .getFileStructureDescription();
0580: currentResultDescription = fsdSearchResult
0581: .getFileStructureDescription();
0582: // and reset the current identifier :
0583: currentIdentifier.setLength(0);
0584: // and initialize it for the next run :
0585: if (tokenIndex < basisIdentifierTokens.length - 1) {
0586: currentIdentifier
0587: .append(basisIdentifierTokens[tokenIndex + 1]);
0588: }
0589: } else {
0590: if (!initialFSDHasBeenFound) {
0591: // We are still accumulating the tokens (of a package statement)
0592: // until a complete qualifier will be found in one of the next loop runs possibly.
0593: if (tokenIndex < basisIdentifierTokens.length - 1) {
0594: currentIdentifier
0595: .append("."
0596: + basisIdentifierTokens[tokenIndex + 1]);
0597: }
0598: } else {
0599: // Means, that an invalid qualifier follows after some valid qualifiers,
0600: // so we have to break without success :
0601: break; // with a null result - The search has failed.
0602: }
0603: }
0604: } // for tokenIndex
0605: } // if
0606:
0607: /* Debug:
0608: if( fsdSearchResult != null )
0609: {
0610: if( fsdSearchResult.getSimpleTypeName() != null )
0611: {
0612: Log.Info("Returns result with simpletype= " +
0613: fsdSearchResult.getSimpleTypeName() );
0614: }
0615: else
0616: if( fsdSearchResult.getFileStructureDescription() != null )
0617: {
0618: Log.Info("Returns result with fsd= " +
0619: fsdSearchResult.getFileStructureDescription().className.content );
0620: }
0621: else
0622: {
0623: Log.Info("Returns an INVALID result");
0624: }
0625: }
0626: else
0627: {
0628: Log.Info("returns a NULL result");
0629: }
0630: ----------- end debug */
0631:
0632: return fsdSearchResult;
0633: } // search_FileStructureDescription
0634:
0635: /**
0636: * Test, if the identifier statement is an object creation and starts
0637: * with the keyword "new" followed by a non java identifier character.
0638: * If this is the case, we get the classname directly and return the associated FSD.
0639: */
0640: private FSDSearchResult searchForClassCreatorStatement(
0641: final int caretLineNumber,
0642: final FileStructureDescription currentFSD,
0643: final String currentIdentifier) {
0644:
0645: //ystem.out.println("searchForClassCreatorStatement in with " + currentIdentifier );
0646:
0647: FileStructureDescription resultFSD = null; // returnvalue
0648: if (currentIdentifier.startsWith("new")) {
0649: if (currentIdentifier.length() > 4) {
0650: int position = 3;
0651: if (!Character.isJavaIdentifierPart(currentIdentifier
0652: .charAt(position))) {
0653: // "new XX()" statement detected
0654: while ((!Character
0655: .isJavaIdentifierPart(currentIdentifier
0656: .charAt(position)))
0657: && (position < currentIdentifier.length() - 1)) {
0658: position++;
0659: }
0660: if (position < currentIdentifier.length() - 1) {
0661: int classNameStartPosition = position;
0662: while ((Character
0663: .isJavaIdentifierPart(currentIdentifier
0664: .charAt(position)))
0665: && (position < currentIdentifier
0666: .length())) {
0667: position++;
0668: // Break from the loop, if we have reached the last character,
0669: // otherwise the while test above would produce an exception :
0670: if (position == currentIdentifier.length()) {
0671: break;
0672: }
0673: }
0674: String className = currentIdentifier.substring(
0675: classNameStartPosition, position);
0676:
0677: //ystem.out.println("searchForClassCreatorStatement className= " + className );
0678:
0679: // First check, if it's an inner class creation (has priority):
0680: // If it was an inner class, its fullyQualified class name had to be:
0681: String targetCandidateName = currentFSD.fullyQualifiedClassNameBuffer
0682: .toString()
0683: + "." + className;
0684: boolean isNestedClass = false;
0685: for (int iNested = 0; iNested < currentFSD.innerClasses
0686: .size(); iNested++) {
0687: FileStructureDescription nestedFSD = (FileStructureDescription) currentFSD.innerClasses
0688: .elementAt(iNested);
0689: if (nestedFSD.fullyQualifiedClassNameBuffer
0690: .toString().equals(
0691: targetCandidateName)) {
0692: resultFSD = nestedFSD;
0693: isNestedClass = true;
0694: break;
0695: }
0696: }
0697: if (!isNestedClass) // else search globally
0698: {
0699: // Search the FSD associated with that classname in the import scope
0700: // of the currentFSD (trying all combinations):
0701: final String[] inScopePossibleIdentifiers = CodeCompletionUtilities
0702: .CreatePossibleQualifiedClassIdentifiers(
0703: currentFSD, className);
0704: for (int p = 0; p < inScopePossibleIdentifiers.length; p++) {
0705: resultFSD = this .fileStructureDescriptionManager
0706: .searchFSD(inScopePossibleIdentifiers[p]);
0707: if (resultFSD != null) {
0708: break;
0709: }
0710: }
0711: }
0712: }
0713: }
0714: }
0715: }
0716: FSDSearchResult fsdSearchResult = null;
0717: if (resultFSD != null) {
0718: fsdSearchResult = new FSDSearchResult(resultFSD, false);
0719: }
0720: return fsdSearchResult;
0721: } // searchForClassCreatorStatement
0722:
0723: /**
0724: * Private helper method. Called from performSearch().
0725: * This one assumes, that the passed currentIdentifier is a class name.
0726: */
0727: private FSDSearchResult searchForClassFSD(
0728: final int caretLineNumber,
0729: final FileStructureDescription currentFSD,
0730: final String currentIdentifier,
0731: final FileStructureDescription[] anonymousClassChain,
0732: final String previousIdentifier) {
0733:
0734: /*
0735: ystem.out.println("XXXXXXXXXXX searchForClassFSD: for current= " +
0736: currentFSD.className.content );
0737: ystem.out.println("XXXXXXXXXXX searchForClassFSD: for id= " +
0738: currentIdentifier );
0739: boolean isAnonymous = ( anonymousClassChain.length > 0 );
0740: ystem.out.println("XXXXXXXXXXX searchForClassFSD: isAnonymous=" + isAnonymous );
0741: */
0742: FileStructureDescription resultFSD = null;
0743:
0744: // Some special cases for simple not segmented currentIdentifiers:
0745: if (currentIdentifier.equals("this")) {
0746:
0747: //ystem.out.println("CCS> +++++++ this detected.");
0748: //ystem.out.println("CCS> +++++++ currentFSD = " + currentFSD.className.content );
0749: //ystem.out.println("CCS> +++++++ previousIdentifier= " + previousIdentifier );
0750:
0751: // Special case for this. : Return the currentFSD itself, or if the
0752: // caret is inside a nested class, return the fsd of that nested class :
0753: // Default :
0754: FileStructureDescription this FSD = currentFSD; // default, if not in a nested class
0755: // If the previous identifier has been the classname of the current FSD,
0756: // The scope is the current FSD. This is the case, if one writes
0757: // <classname>.this inside a nested or anonymous class for having access
0758: // to member objects of the toplevel class.
0759: // In all other cases, the nested class determines the scope:
0760: if (previousIdentifier.equals(currentFSD.className.content)) {
0761: // Scope extension by a <class>.this term -> return the toplevel FSD:
0762: resultFSD = currentFSD;
0763: //ystem.out.println("CCS> +++++++ scope extended ");
0764: } else // The scope is restricted by nested or anonymous classes:
0765: {
0766: //ystem.out.println("CCS> +++++++ scope conserved -> testing nested classes ");
0767: // Nested class: Overwites default :
0768: for (int i = 0; i < currentFSD.innerClasses.size(); i++) {
0769: FileStructureDescription nestedFSD = (FileStructureDescription) currentFSD.innerClasses
0770: .elementAt(i);
0771: if ((caretLineNumber >= nestedFSD.scopeStartLine)
0772: && (caretLineNumber <= nestedFSD.scopeEndLine)) {
0773: this FSD = nestedFSD;
0774: break;
0775: }
0776: }
0777: // If the caret is inside an anonymous class chain, take the deepest (=last) anonymous class :
0778: // Anonymous class: Overwrites all [highest priority] :
0779: if (anonymousClassChain.length > 0) {
0780: this FSD = anonymousClassChain[anonymousClassChain.length - 1];
0781: }
0782: resultFSD = this FSD;
0783: }
0784: } else if (currentIdentifier.equals("super")) {
0785: // Special case for super.
0786: FileStructureDescription super ClassFSD = null;
0787: FileStructureDescription basisFSD = currentFSD;
0788: // Like in the "this" case: If the caret is inside a nested class, that
0789: // nested class defines the result of the super operator :
0790: for (int i = 0; i < currentFSD.innerClasses.size(); i++) {
0791: FileStructureDescription nestedFSD = (FileStructureDescription) currentFSD.innerClasses
0792: .elementAt(i);
0793: if ((caretLineNumber >= nestedFSD.scopeStartLine)
0794: && (caretLineNumber <= nestedFSD.scopeEndLine)) {
0795: basisFSD = nestedFSD;
0796: break;
0797: }
0798: }
0799: // If the caret is inside an anonymous class chain, take the deepest (=last) anonymous class
0800: // Anonymous class: Overwrites all [highest priority] :
0801: if (anonymousClassChain.length > 0) {
0802: basisFSD = anonymousClassChain[anonymousClassChain.length - 1];
0803: }
0804: if (basisFSD.super Class_fullyQualifiedClassName.content
0805: .length() > 0) {
0806: final String super ClassQualifier = basisFSD.super Class_fullyQualifiedClassName.content;
0807: // Note: superClass_fullyQualifiedClassName only is qualified in the sense, that
0808: // it's the qualifier as entered in the source. That means, that we still
0809: // must combine it with all import statements :
0810: // Note: For the combination of import statements, it's always the toplevel currentFSD,
0811: // which must be used, also if basisFSD points to a nested fsd.
0812: final String[] inScopePossibleIdentifiers = CodeCompletionUtilities
0813: .CreatePossibleQualifiedClassIdentifiers(
0814: currentFSD, super ClassQualifier);
0815: for (int p = 0; p < inScopePossibleIdentifiers.length; p++) {
0816: super ClassFSD = this .fileStructureDescriptionManager
0817: .searchFSD(inScopePossibleIdentifiers[p]);
0818: if (super ClassFSD != null) {
0819: break;
0820: }
0821: }
0822: } else {
0823: // The considered class does not explicitly extend another class, but
0824: // in Java, all classes always extend the basisclass object, so return
0825: // this one, if the basisFSD isn't already the class Object :
0826: if (!basisFSD.className.content.equals("Object")) {
0827: final String super ClassQualifier = "java.lang.Object";
0828: super ClassFSD = this .fileStructureDescriptionManager
0829: .searchFSD(super ClassQualifier);
0830: }
0831: }
0832: resultFSD = super ClassFSD;
0833: } else {
0834: if (currentFSD.innerClasses != null) {
0835: // Local Search in the currentFSD: Search for an inner class first, because this has priority:
0836: if (currentFSD.innerClasses.size() > 0) {
0837: // The fullyQualifiedClassName is preceeded with the fullyQualifiedClassName
0838: // of the parentclass.
0839: // If the name of the parentclass is MyPackage.MyApplication and the classname
0840: // of the innerclass is MyFrame, then the fullyQualifiedClassName of
0841: // the innerclass is: MyPackage.MyApplication.MyFrame
0842: // In this case, MyFrame would be the currentIdentifier.
0843: // Create the target search identifier as explained above:
0844: final String targetQualifier = currentFSD.fullyQualifiedClassNameBuffer
0845: .toString()
0846: + "." + currentIdentifier;
0847: for (int i = 0; i < currentFSD.innerClasses.size(); i++) {
0848: final FileStructureDescription this InnerClassFSD = (FileStructureDescription) currentFSD.innerClasses
0849: .elementAt(i);
0850: if (this InnerClassFSD.fullyQualifiedClassNameBuffer
0851: .toString().equals(targetQualifier)) {
0852: resultFSD = this InnerClassFSD;
0853: break;
0854: }
0855: }
0856: } // if
0857: } // if
0858: if (resultFSD == null) {
0859: // Local Search in the currentFSD: Check inner interfaces, if still not found:
0860: if (currentFSD.innerInterfaces.size() > 0) {
0861: // The fullyQualifiedClassName is preceeded with the fullyQualifiedClassName
0862: // of the parentclass.
0863: // If the name of the parentclass is MyPackage.MyApplication and the classname
0864: // of the inner interface is MyInterface, then the fullyQualifiedClassName of
0865: // the inner interface is: MyPackage.MyApplication.MyInterface
0866: // In this case, MyInterface would be the currentIdentifier.
0867: // Create the target search identifier as explained above:
0868: final String targetQualifier = currentFSD.fullyQualifiedClassNameBuffer
0869: .toString()
0870: + "." + currentIdentifier;
0871: for (int i = 0; i < currentFSD.innerInterfaces
0872: .size(); i++) {
0873: final FileStructureDescription this InnerInterfaceFSD = (FileStructureDescription) currentFSD.innerInterfaces
0874: .elementAt(i);
0875: if (this InnerInterfaceFSD.fullyQualifiedClassNameBuffer
0876: .toString().equals(targetQualifier)) {
0877: resultFSD = this InnerInterfaceFSD;
0878: break;
0879: }
0880: }
0881: } // if
0882: } // if
0883: // General case, if still not found: We have to search on gobal scope:
0884: if (resultFSD == null) {
0885: FileStructureDescription foundFSD = null;
0886: // We must combine the currentIdentifier with all wildcard-importstatements
0887: // of the current FSD, and also take into account, that one importstatement
0888: // could be the exact statement for the searched class, this
0889: // is done in the following inner loop :
0890: // Search a class description, which matches the currentIdentifier :
0891: final String[] possibleQualifiedClassIdentifiers = CodeCompletionUtilities
0892: .CreatePossibleQualifiedClassIdentifiers(
0893: currentFSD, currentIdentifier);
0894: for (int searchIndex = 0; searchIndex < possibleQualifiedClassIdentifiers.length; searchIndex++) {
0895: foundFSD = this .fileStructureDescriptionManager
0896: .searchFSD(possibleQualifiedClassIdentifiers[searchIndex]);
0897: // Break, if we have found the FSD, otherwise continue :
0898: if (foundFSD != null) {
0899: break;
0900: }
0901: } // for possibleQualifierIndex
0902: resultFSD = foundFSD;
0903: } // if
0904: } // else
0905: FSDSearchResult fsdSearchResult = null;
0906: if (resultFSD != null) {
0907: fsdSearchResult = new FSDSearchResult(resultFSD, false);
0908: }
0909: return fsdSearchResult;
0910: } // searchForClassFSD
0911:
0912: /**
0913: * Private helper method. Called from performSearch().
0914: *
0915: * This one assumes, that the passed currentIdentfier is the name of an attribute.
0916: *
0917: * The method goes recursively through nested class fsds. But the required import statements
0918: * always have to be fetched from the topLevel fsd, which mustn't change when diving into
0919: * nested classes.
0920: *
0921: * For a first call of this method with a toplevel fsd, pass topLevelFSD = currentFSD.
0922: */
0923: private FSDSearchResult searchForAttributeClassFSD(
0924: final FileStructureDescription topLevelFSD,
0925: final FileStructureDescription currentFSD,
0926: String currentIdentifier, final int caretLineNumber,
0927: final FileStructureDescription[] anonymousClassChain) {
0928: FSDSearchResult fsdSearchResult = null;
0929:
0930: //ystem.out.println("CCSearch: searchForAttributeClassFSD starts with currentFSD= " +
0931: // currentFSD.className.content +
0932: // " and currentIdentifier= " + currentIdentifier );
0933:
0934: // If the caret is inside a nested class, attributes of that nested class
0935: // have highest priority, so add these first.
0936: // We use recursion for this:
0937: for (int i = 0; i < currentFSD.innerClasses.size(); i++) {
0938: FileStructureDescription nestedFSD = (FileStructureDescription) currentFSD.innerClasses
0939: .elementAt(i);
0940:
0941: //ystem.out.println("searchForAttributeClassFSD: check nested class " +
0942: // nestedFSD.fullyQualifiedClassName );
0943:
0944: if ((caretLineNumber >= nestedFSD.scopeStartLine)
0945: && (caretLineNumber <= nestedFSD.scopeEndLine)) {
0946: fsdSearchResult = this .searchForAttributeClassFSD(
0947: topLevelFSD, nestedFSD, currentIdentifier,
0948: caretLineNumber, anonymousClassChain);
0949:
0950: //ystem.out.println("searchForAttributeClassFSD: caret inside -> search RECURSIVELY nested class " +
0951: // nestedFSD.fullyQualifiedClassName );
0952:
0953: if (fsdSearchResult != null) {
0954: break;
0955: }
0956: }
0957: }
0958: // Local attributes have higher priority than [inherited]member attributes,
0959: // therefore search these first :
0960: // Search locals (can be in a nested fsd, cause we use recursion above),
0961: // if no nested fsd result has been found already :
0962: if (fsdSearchResult == null) {
0963: // 2nd) search for an inscope local attribute FSD
0964: final Vector localFieldFSDVector = currentFSD.localBlockFieldDescriptions;
0965:
0966: // If the caret lies in a chain of anonymous classes,
0967: // add all toplevel-member and inherited-member attributes of them :
0968: if (anonymousClassChain.length > 0) {
0969: for (int i = 0; i < anonymousClassChain.length; i++) {
0970: FileStructureDescription aFSD = (FileStructureDescription) anonymousClassChain[i];
0971: /* Debug output:
0972: ystem.out.println("XXX searchForAttributeClassFSD() anonymous fsd: " +
0973: aFSD.className.content );
0974: ystem.out.println("XXX searchForAttributeClassFSD() scopestart= " +
0975: aFSD.scopeStartLine + " scopeend= " + aFSD.scopeEndLine );
0976: ystem.out.println("XXX searchForAttributeClassFSD() number of member attributes= " +
0977: aFSD.fieldDescriptions.size() );
0978: ystem.out.println("XXX searchForAttributeClassFSD() number of localblock attributes= " +
0979: aFSD.localBlockFieldDescriptions.size() );
0980: */
0981: // anonymous local attributes :
0982: for (int k = 0; k < anonymousClassChain[i].localBlockFieldDescriptions
0983: .size(); k++) {
0984: localFieldFSDVector
0985: .addElement(anonymousClassChain[i].localBlockFieldDescriptions
0986: .elementAt(k));
0987: /*
0988: FileStructureDescriptionForField af = (FileStructureDescriptionForField)anonymousClassChain[i].localBlockFieldDescriptions.elementAt(k);
0989: ystem.out.println("XXX searchForAttributeClassFSD() local field: " +
0990: af.objectName );
0991: */
0992: }
0993: } // for i
0994: } // if anonymousClassChain.length > 0
0995:
0996: // Get the field FSD's in array form:
0997: final FileStructureDescriptionForField[] localFieldFSDs = new FileStructureDescriptionForField[localFieldFSDVector
0998: .size()];
0999: localFieldFSDVector.copyInto(localFieldFSDs);
1000:
1001: //ystem.out.println("XXX searchForAttributeClassFSD(): SEARCHING LOCAL ATTRIBUTE... There are " +
1002: // localFieldFSDs.length + " local fields in current fsd= " +
1003: // currentFSD.className.content );
1004:
1005: fsdSearchResult = this .searchForSpecialAttributeClassFSD(
1006: topLevelFSD, currentFSD, localFieldFSDs,
1007: currentIdentifier, caretLineNumber);
1008: } // if
1009:
1010: // Search members (can be in a nested fsd, cause we use recursion above),
1011: // if no nested fsd result has been found already :
1012: if (fsdSearchResult == null) {
1013: // CAUTION: DO not change the content of the fieldDescriptions or
1014: // localBlockFieldDescriptions Vectors.
1015:
1016: final Vector allFieldFSDVector = new Vector(); // Collect all here
1017: // Get the member field FSD's:
1018: final Vector memberFieldFSDVector = currentFSD.fieldDescriptions;
1019: // Add them to the allFieldFSDVector Vector :
1020: for (int i = 0; i < memberFieldFSDVector.size(); i++) {
1021: allFieldFSDVector.addElement(memberFieldFSDVector
1022: .elementAt(i));
1023: }
1024: // Get all inherited field FSD's:
1025: final Vector inheritedMemberFieldFSDVector = currentFSD.inheritedFieldDescriptions;
1026: // Add them to the allFieldFSDVector Vector :
1027: for (int i = 0; i < inheritedMemberFieldFSDVector.size(); i++) {
1028: allFieldFSDVector
1029: .addElement(inheritedMemberFieldFSDVector
1030: .elementAt(i));
1031: }
1032:
1033: // Additionally attributes defined in a declared interface also are
1034: // visible in this class [ an asymmetry, cause methods have to be "implemented",
1035: // but attributes in an interface don't make sense basically, because
1036: // it makes no sense to "implement an attribute" in the target class like
1037: // one would have to do with methods, but..]
1038: Vector interfaceFieldsVector = CodeCompletionUtilities
1039: .GetAllFieldsFromAllInterfacesFor(currentFSD,
1040: this .fileStructureDescriptionManager);
1041: for (int i = 0; i < interfaceFieldsVector.size(); i++) {
1042: FileStructureDescriptionForField f = (FileStructureDescriptionForField) interfaceFieldsVector
1043: .elementAt(i);
1044: allFieldFSDVector.addElement(f);
1045: }
1046:
1047: // If the caret lies in a chain of anonymous classes,
1048: // add all toplevel-member and inherited-member attributes of them :
1049: if (anonymousClassChain.length > 0) {
1050:
1051: for (int i = 0; i < anonymousClassChain.length; i++) {
1052: FileStructureDescription aFSD = (FileStructureDescription) anonymousClassChain[i];
1053: /* Debug output:
1054: ystem.out.println("XXX searchForAttributeClassFSD() anonymous fsd: " +
1055: aFSD.className.content );
1056: ystem.out.println("XXX searchForAttributeClassFSD() scopestart= " +
1057: aFSD.scopeStartLine + " scopeend= " + aFSD.scopeEndLine );
1058: ystem.out.println("XXX searchForAttributeClassFSD() number of member attributes= " +
1059: aFSD.fieldDescriptions.size() );
1060: ystem.out.println("XXX searchForAttributeClassFSD() number of localblock attributes= " +
1061: aFSD.localBlockFieldDescriptions.size() );
1062: for( int qq=0; qq < aFSD.localBlockFieldDescriptions.size(); qq++ )
1063: {
1064: FileStructureDescriptionForField fsdf =
1065: (FileStructureDescriptionForField)aFSD.localBlockFieldDescriptions.elementAt(qq);
1066: ystem.out.println("XXX searchForAttributeClassFSD() localblock attribute: " +
1067: fsdf.objectName +
1068: " scopestart= " + fsdf.scopeStartLine +
1069: " scopeend= " + fsdf.scopeEndLine );
1070: }
1071: */
1072:
1073: // anonymous members :
1074: for (int k = 0; k < anonymousClassChain[i].fieldDescriptions
1075: .size(); k++) {
1076: allFieldFSDVector
1077: .addElement(anonymousClassChain[i].fieldDescriptions
1078: .elementAt(k));
1079: /*
1080: FileStructureDescriptionForField af = (FileStructureDescriptionForField)anonymousClassChain[i].fieldDescriptions.elementAt(k);
1081: ystem.out.println("XXX searchForAttributeClassFSD() anonymous field: " +
1082: af.objectName );
1083: */
1084: }
1085: // anonymous inherited members :
1086: for (int k = 0; k < anonymousClassChain[i].inheritedFieldDescriptions
1087: .size(); k++) {
1088: allFieldFSDVector
1089: .addElement(anonymousClassChain[i].inheritedFieldDescriptions
1090: .elementAt(k));
1091: /*
1092: FileStructureDescriptionForField af = (FileStructureDescriptionForField)anonymousClassChain[i].inheritedFieldDescriptions.elementAt(k);
1093: ystem.out.println("XXX searchForAttributeClassFSD() inherited anonymous field: " +
1094: af.objectName );
1095: */
1096: }
1097: }
1098: }
1099:
1100: // Copy all into one array :
1101: final FileStructureDescriptionForField[] allFieldFSDs = new FileStructureDescriptionForField[allFieldFSDVector
1102: .size()];
1103: allFieldFSDVector.copyInto(allFieldFSDs);
1104:
1105: //ystem.out.println("XXX searchForAttributeClassFSD(): SEARCHING MEMBER ATTRIBUTES...");
1106: fsdSearchResult = this .searchForSpecialAttributeClassFSD(
1107: topLevelFSD, currentFSD, allFieldFSDs,
1108: currentIdentifier, caretLineNumber);
1109:
1110: } // if
1111:
1112: /* Debug:
1113: if( fsdSearchResult != null )
1114: {
1115: if( fsdSearchResult.getSimpleTypeName() != null )
1116: {
1117: ystem.out.println("searchForAttributeClassFSD: Returns fsdSearchResult with simpletype= " +
1118: fsdSearchResult.getSimpleTypeName() +
1119: " array= " + fsdSearchResult.getIsJavaArrayObject() );
1120: }
1121: else
1122: if( fsdSearchResult.getFileStructureDescription() != null )
1123: {
1124: ystem.out.println("searchForAttributeClassFSD: Returns fsdSearchResult with fsd= " +
1125: fsdSearchResult.getFileStructureDescription().className.content +
1126: " array= " + fsdSearchResult.getIsJavaArrayObject() );
1127: }
1128: else
1129: {
1130: ystem.out.println("searchForAttributeClassFSD: Returns an INVALID fsdSearchResult");
1131: }
1132: }
1133: else
1134: {
1135: ystem.out.println("searchForAttributeClassFSD: returns a NULL fsdSearchResult");
1136: }
1137: */
1138:
1139: return fsdSearchResult;
1140: } // searchForAttributeClassFSD
1141:
1142: /**
1143: * Private helper method. Called from searchForAttributeClassFSD().
1144: *
1145: * It is used for the search for a member attribute FSD as well as
1146: * for a local attribute FSD, depending on the content of fieldFSDs.
1147: *
1148: * It also gets called with nested class fsd's as currentFSD. Therefore
1149: * the possible package statement combinations always have to be fetched
1150: * from the also passed topLevelFSD.
1151: * If the currentFSD is a toplevel fsd, we have topLevelFSD = currentFSD.
1152: *
1153: */
1154: private FSDSearchResult searchForSpecialAttributeClassFSD(
1155: final FileStructureDescription topLevelFSD,
1156: final FileStructureDescription currentFSD,
1157: final FileStructureDescriptionForField[] fieldFSDs,
1158: String currentIdentifier, final int caretLineNumber) {
1159:
1160: //ystem.out.println("x>>> CCSearch.searchForSpecialAttributeClassFSD() called for: ");
1161: //ystem.out.println("x>>> currentIdentifier= " + currentIdentifier );
1162:
1163: // Simplification 1 : If the currentIdentifier ends with
1164: // array brackets, skip the brackets incl. content for getting
1165: // the fsd first.
1166: // If no brackets were detected, but below a java array type is found,
1167: // the flag isJavaArrayObject in the returned object is set for signaling,
1168: // that it's a simple java array (with only one attribute, which is length )
1169: boolean isArrayElement = false;
1170: final int squareBracketIndex = currentIdentifier.indexOf("[");
1171: if (squareBracketIndex >= 0) {
1172: isArrayElement = true;
1173: currentIdentifier = currentIdentifier.substring(0,
1174: squareBracketIndex);
1175: //ystem.out.println("CC: Methodsearch: searchForSpecialAttributeClassFSD: SIMPLIFIED 1 (squarebracket) currentIdentifier = " + currentIdentifier );
1176: }
1177:
1178: FileStructureDescription resultFSD = null;
1179: String simpletype = null;
1180: boolean resultAttributeHasArrayType = false;
1181:
1182: for (int i = 0; i < fieldFSDs.length; i++) {
1183: final FileStructureDescriptionForField this FieldFSD = fieldFSDs[i];
1184: if (this FieldFSD.objectNameWithPosition.content
1185: .equals(currentIdentifier)) {
1186:
1187: //ystem.out.println("x>>> CCSearch.searchForSpecialAttributeClassFSD(): Attribute FOUND");
1188: //ystem.out.println("x>>> caretLineNumber= " + caretLineNumber );
1189: //ystem.out.println("x>>> thisFieldFSD.scopeStartLine= " + thisFieldFSD.scopeStartLine );
1190: //ystem.out.println("x>>> thisFieldFSD.scopeEndLine= " + thisFieldFSD.scopeEndLine );
1191:
1192: boolean inScope = true;
1193: // If it's a local attribute, test, if we are in scope :
1194: if (this FieldFSD.scopeEndLine > 0) // it's a local attribute FSD
1195: {
1196: inScope = ((this FieldFSD.scopeStartLine <= caretLineNumber) && (this FieldFSD.scopeEndLine >= caretLineNumber));
1197: }
1198: // else it's a member attribute FSD.
1199:
1200: //ystem.out.println("x>>> CCSearch.searchForSpecialAttributeClassFSD(): Attribute inScope= " + inScope);
1201:
1202: if (inScope) {
1203: // We have found an inscope attribute description with the requested name,
1204: // so now, we only have to get the FSD for the class type,
1205: // which should exist, unless it's not programmed in the targetsource,
1206: // or unless its a primitive type :
1207: String classQualifier = null;
1208: if (this FieldFSD.packageName.length() > 0) {
1209: classQualifier = this FieldFSD.packageName + "."
1210: + this FieldFSD.simpleClassName.content;
1211: } else {
1212: classQualifier = this FieldFSD.simpleClassName.content;
1213: }
1214: resultAttributeHasArrayType = this FieldFSD.isArrayType.booleanValue;
1215: // If the type is not primitive, we have to search the fsd, otherwise
1216: // we have all we need.
1217: if (this FieldFSD.isPrimitiveType) {
1218: // Note: simpletype will be used for creating the returned FSDResult below, if its set.
1219: simpletype = classQualifier;
1220:
1221: //ystem.out.println("---->searchForSpecialAttributeClassFSD: Simple type detected and returned in FSDResult: " +
1222: // simpletype );
1223:
1224: // all done - break the loop
1225: break;
1226: } else {
1227: // Seen by tests: Nested classes have higher priority, than toplevel classes.
1228: // Means: If you have a nested class named MyClass, and a toplevel class of
1229: // the same name MyClass (but with other methods and attributes), the compiler
1230: // will associate an attribute of class MyClass with the *nested class* and not
1231: // with the toplevel class. This gives the order here :
1232: // First search the nested classes :
1233: for (int iNested = 0; iNested < currentFSD.innerClasses
1234: .size(); iNested++) {
1235: FileStructureDescription nestedFSD = (FileStructureDescription) currentFSD.innerClasses
1236: .elementAt(iNested);
1237: if (nestedFSD.className.content
1238: .equals(classQualifier)) {
1239: resultFSD = nestedFSD;
1240: break;
1241: }
1242: }
1243: // Go search this-level fsd's (can be nested too), if no nested class inside currentFSD
1244: // has been found:
1245: if (resultFSD == null) {
1246: // Note: classQualifier only is qualified in the sense, that
1247: // its the qualifier as entered in the source. That means, that we still
1248: // must combine it with all import statements :
1249: // Always use the topLevelFSD for getting these combinations, which is required,
1250: // when currentFSD itseld is a nested fsd :
1251: resultFSD = this
1252: .searchFSDForClassQualifierIncludingParentChainOf(
1253: topLevelFSD, classQualifier);
1254: } // if
1255: if (resultFSD != null) // keep it for returning and break this outer loop too
1256: {
1257: break;
1258: }
1259: } // else
1260: } // if inScope
1261: } // if
1262: } // for i
1263:
1264: // If neither an fsd (of a complex type) nor a simpletype has been found,
1265: // test the currentIdentifier, wether it's a constant value and identify it :
1266: // 234 -> int
1267: // 1.2 -> double
1268: // 1.3f -> float
1269: // 'd' -> char
1270: // true, false -> boolean
1271: //
1272: // Exception: No action for null :
1273: // null -> null ( identity - this must be handled later, its like an object wildcard )
1274: if ((resultFSD == null) && (simpletype == null)) {
1275:
1276: //ystem.out.println("x>>> CodeCompletionSearch: Neither field fsd nor simple type found -> Checking for constant value.");
1277:
1278: simpletype = identifySimpleType(currentIdentifier);
1279:
1280: /*
1281: if( simpletype != null )
1282: {
1283: ystem.out.println("x>>> CodeCompletionSearch: Constant value identified: " + simpletype );
1284: }
1285: else
1286: {
1287: ystem.out.println("x>>> CodeCompletionSearch: NO Constant value identified.");
1288: }
1289: */
1290:
1291: } // if (constant identification)
1292:
1293: // It's an abstract java array type (with only a length attribute), if no square
1294: // brackets were detected above, but the attribute was marked as array.
1295: boolean isJavaArrayObject = (resultAttributeHasArrayType && !isArrayElement);
1296: // Switch depending on simple or object type, return null if nothing has been found :
1297: FSDSearchResult result = null; // default, if nothing found
1298: if (simpletype != null) {
1299: result = new FSDSearchResult(simpletype, isJavaArrayObject);
1300: } else if (resultFSD != null) {
1301: result = new FSDSearchResult(resultFSD, isJavaArrayObject);
1302: }
1303: return result;
1304: } // searchForSpecialAttributeClassFSD
1305:
1306: /**
1307: * Tries to identify the passed identifier as constant value and
1308: * returns its simple type, or null if it couldn't identify it.
1309: *
1310: * Called by searchForSpecialAttributeClassFSD() above.
1311: *
1312: * Examples:
1313: * 234 -> int
1314: * 1.2 -> double
1315: * 1.3f -> float
1316: * 'd' -> char
1317: * true, false -> boolean
1318: */
1319: private String identifySimpleType(String identifier) {
1320: String simpletype = null; // Return value. It's null, if nothing has been found.
1321: if (identifier.length() > 0) {
1322: char lastCharacter = identifier
1323: .charAt(identifier.length() - 1);
1324: if (lastCharacter == 'd') {
1325: try {
1326: Double.parseDouble(identifier); // throws an exception, if it doesn't work
1327: simpletype = "double";
1328: } catch (Exception e1) {
1329: }
1330: } else if (lastCharacter == 'f') {
1331: try {
1332: Float.parseFloat(identifier); // throws an exception, if it doesn't work
1333: simpletype = "float";
1334: } catch (Exception e2) {
1335: }
1336: } else if (identifier.equals("true")
1337: || identifier.equals("false")) {
1338: simpletype = "boolean";
1339: } else if (identifier.startsWith("'")) {
1340: simpletype = "char";
1341: } else {
1342: try {
1343: Integer.parseInt(identifier); // throws an exception, if it doesn't work
1344: simpletype = "int";
1345: } catch (Exception e3) {
1346: }
1347: }
1348: } // if length > 0
1349: return simpletype;
1350: } // identifySimpleType
1351:
1352: /**
1353: * Private helper method. Called from performSearch().
1354: */
1355: private FSDSearchResult searchForMethodReturnTypeFSD(
1356: final FileStructureDescription currentFSD,
1357: String currentIdentifier, final int caretLineNumber,
1358: final FileStructureDescription[] anonymousClassChain) {
1359: // Parameter signature : Initialized as an empty String, which is correct for
1360: // methods with an empty parameter list.
1361: final int roundBracketStartIndex = currentIdentifier
1362: .indexOf("(");
1363: if (roundBracketStartIndex >= 0) {
1364: // The method parameter list ends at the LAST closing bracket. Of course
1365: // there can be many brackets inside (which should be balanced, if the syntax
1366: // happens to be correct )
1367: final int roundBracketEndIndex = currentIdentifier
1368: .lastIndexOf(")");
1369: // Try to calculate the parameter signature, if we have at last one character between
1370: // the brackets. The parameter signature consists of all parameter classnames, separated
1371: // by commas.
1372: if ((roundBracketEndIndex >= 0)
1373: && (roundBracketEndIndex > roundBracketStartIndex + 1)) {
1374: String plainMethodName = currentIdentifier.substring(0,
1375: roundBracketStartIndex);
1376: String parameterListText = currentIdentifier.substring(
1377: roundBracketStartIndex + 1,
1378: roundBracketEndIndex);
1379: // Call the recursive method, which parses the parameterlist and tries to
1380: // resolve the signature for it :
1381: String parameterSignature = this
1382: .calculateParameterSignatureForParameterList(
1383: parameterListText, currentFSD,
1384: caretLineNumber);
1385: // Add it, if one has been found at all :
1386: if (parameterSignature.length() > 0) {
1387: currentIdentifier = plainMethodName + "("
1388: + parameterSignature + ")";
1389: } else {
1390: currentIdentifier = plainMethodName + "()";
1391: }
1392:
1393: //ystem.out.println("CC> searchForMethodReturnTypeFSD: identifier with signature = " +
1394: // currentIdentifier );
1395:
1396: } else {
1397: // Only for debug purpose :
1398: //ystem.out.println("CC> searchForMethodReturnTypeFSD: [plain] identifier = " + currentIdentifier );
1399: }
1400: } // if
1401:
1402: // CAUTION: Do NOT change the contents of the methodDescriptions
1403: // or inheritedMethodDescriptions.
1404:
1405: FileStructureDescription resultFSD = null;
1406: final Vector allMethodsVector = new Vector();
1407: // Get the toplevel methods :
1408: final Vector methodFSDVector = currentFSD.methodDescriptions;
1409: // Add all toplevel methods to the allMethodsVector Vector :
1410: for (int i = 0; i < methodFSDVector.size(); i++) {
1411: allMethodsVector.addElement(methodFSDVector.elementAt(i));
1412: }
1413: // Get all inherited methods :
1414: final Vector inheritedMethodFSDVector = currentFSD.inheritedMethodDescriptions;
1415: // Add all inherited methods to the allMethodsVector Vector :
1416: for (int i = 0; i < inheritedMethodFSDVector.size(); i++) {
1417: allMethodsVector.addElement(inheritedMethodFSDVector
1418: .elementAt(i));
1419: }
1420: // If the caret lies in a chain of anonymous classes,
1421: // add all toplevel-methods and inherited-methods of them :
1422: if (anonymousClassChain.length > 0) {
1423: for (int i = 0; i < anonymousClassChain.length; i++) {
1424: for (int k = 0; k < anonymousClassChain[i].methodDescriptions
1425: .size(); k++) {
1426: allMethodsVector
1427: .addElement(anonymousClassChain[i].methodDescriptions
1428: .elementAt(k));
1429: }
1430: for (int k = 0; k < anonymousClassChain[i].inheritedMethodDescriptions
1431: .size(); k++) {
1432: allMethodsVector
1433: .addElement(anonymousClassChain[i].inheritedMethodDescriptions
1434: .elementAt(k));
1435: }
1436:
1437: /* Debug:
1438: if( anonymousClassChain[i].inheritedMethodDescriptions.size() == 0 )
1439: {
1440: ystem.out.println("*** No inherited methods for anonymous class " +
1441: anonymousClassChain[i].className.content +
1442: " with superclass= " +
1443: anonymousClassChain[i].superClass_fullyQualifiedClassName.content );
1444: }
1445: else
1446: {
1447: ystem.out.println("*** Inherited methods for anonymous class " +
1448: anonymousClassChain[i].className.content +
1449: " : " +
1450: anonymousClassChain[i].inheritedMethodDescriptions.size() );
1451: }
1452: */
1453:
1454: }
1455: }
1456: // Copy all into one array :
1457: final FileStructureDescriptionForMethod[] allMethodFSDs = new FileStructureDescriptionForMethod[allMethodsVector
1458: .size()];
1459: allMethodsVector.copyInto(allMethodFSDs);
1460: // and search:
1461: boolean resultReturnTypeHasArrayType = false;
1462: boolean isPrimitiveType = false;
1463: String simpletype = "";
1464: for (int i = 0; i < allMethodFSDs.length; i++) {
1465: final FileStructureDescriptionForMethod this MethodFSD = allMethodFSDs[i];
1466: final ParameterTypeDescription returnType = this MethodFSD.resultParameter;
1467:
1468: //ystem.out.println("CC> methodsearch: comparing " + currentIdentifier +
1469: // " with signature= " + thisMethodFSD.signature );
1470:
1471: String returnedClassIdentifier = null;
1472: if (this MethodFSD.signature.equals(currentIdentifier)) {
1473: // We have found the method signature,
1474: // so now, we only have to get the FSD for the returned class type,
1475: // which should exist, unless the associated file (class or java),
1476: // doesnt exist, or unless its a primitive type :
1477:
1478: // Skip primitive return types :
1479: isPrimitiveType = returnType.isPrimitiveType;
1480: if (!returnType.isPrimitiveType) {
1481: String classQualifier = null;
1482: if (returnType.packageName.length() > 0) {
1483: classQualifier = returnType.packageName + "."
1484: + returnType.simpleClassName;
1485: } else {
1486: classQualifier = returnType.simpleClassName;
1487: }
1488: resultReturnTypeHasArrayType = returnType.isArrayType.booleanValue;
1489: // Note: classQualifier only is qualified in the sense, that
1490: // its the qualifier as entered in the source. That means, that we still
1491: // must combine it with all import statements :
1492: resultFSD = this
1493: .searchFSDForClassQualifierIncludingParentChainOf(
1494: currentFSD, classQualifier);
1495: if (resultFSD != null) // keep it for returning and break this outer loop too
1496: {
1497: break; // break the outer for loop
1498: }
1499: } else {
1500: simpletype = returnType.simpleClassName;
1501: // check array type also for primitive types :
1502: resultReturnTypeHasArrayType = returnType.isArrayType.booleanValue;
1503: break;
1504: }
1505: } // if
1506: } // for i
1507:
1508: boolean isJavaArrayObject = resultReturnTypeHasArrayType;
1509: // Switch depending on simple or object type, return null if nothing has been found :
1510: FSDSearchResult result = null; // default, if nothing found
1511: if (isPrimitiveType) {
1512: result = new FSDSearchResult(simpletype, isJavaArrayObject);
1513:
1514: //ystem.out.println("searchForMethodReturnTypeFSD() returns simpletype " + simpletype );
1515:
1516: } else if (resultFSD != null) {
1517: result = new FSDSearchResult(resultFSD, isJavaArrayObject);
1518:
1519: //ystem.out.println("searchForMethodReturnTypeFSD() returns fsd " + resultFSD.className.content );
1520:
1521: } else {
1522: //ystem.out.println("searchForMethodReturnTypeFSD() returns NULL ");
1523: // this.
1524: }
1525: return result;
1526: } // searchForMethodReturnTypeFSD
1527:
1528: private FileStructureDescription searchFSDForClassQualifierIncludingParentChainOf(
1529: final FileStructureDescription basisFSD,
1530: final String classQualifier) {
1531: // Note: The classQualifier only is qualified in the sense, that
1532: // its the qualifier as entered in the source. That means, that we still
1533: // must combine it with all import statements :
1534: FileStructureDescription resultFSD = null; // The return value
1535: // Examine the current FSD, and while not found go back the parent chain :
1536: FileStructureDescription searchFSD = basisFSD;
1537: while ((searchFSD != null) && (resultFSD == null)) {
1538: final String[] inScopePossibleIdentifiers = CodeCompletionUtilities
1539: .CreatePossibleQualifiedClassIdentifiers(searchFSD,
1540: classQualifier);
1541: for (int p = 0; p < inScopePossibleIdentifiers.length; p++) {
1542: resultFSD = this .fileStructureDescriptionManager
1543: .searchFSD(inScopePossibleIdentifiers[p]);
1544: if (resultFSD != null) // keep it for returning and break this inner loop
1545: {
1546: break; // break the for while loop
1547: }
1548: }
1549: // If it nothing has been found, step back the parent chain :
1550: if (resultFSD == null) {
1551: searchFSD = this .fileStructureDescriptionManager
1552: .getSuperClassFSDOf(searchFSD);
1553: // If no superclass exists, searchFSD will be null now, which
1554: // will leave the while loop.
1555: }
1556: } // while
1557: return resultFSD;
1558: } // searchFSDForIdentifierIncludingParentChainOf
1559:
1560: /**
1561: * Recursive method, which tries to replace the parameterList, which is the
1562: * text between method parameter brackets, by the signature made of the
1563: * parametertypes separated by commas.
1564: *
1565: * The returned signature isn't enclosed by parnethesis. It just contains the
1566: * classnames [or simpletype names] of all parameters, separated by commas.
1567: *
1568: */
1569: private String calculateParameterSignatureForParameterList(
1570: final String parameterListText,
1571: final FileStructureDescription currentFSD,
1572: final int caretLineNumber) {
1573: // Round brackets go one recursion step deeper.
1574: // example: parameterListText= "myInt,this.getComponent(a1,a2),i)"
1575: // where the comma between a1 and a2 is a level deeper in the recursion.
1576: // Tokenize the first level only, but be prepared, that the brackets don't
1577: // have to be balanced (because of possible syntax errors of the programmer).
1578: Vector parameterElementsTextVector = new Vector();
1579: String workText = ""; // Empty String
1580: char workCharacter;
1581: int childLevel = 0;
1582: for (int i = 0; i < parameterListText.length(); i++) {
1583: workCharacter = parameterListText.charAt(i);
1584: if ((workCharacter == ',') && (childLevel == 0)) {
1585: // Add the existing text and make a new one ready :
1586: if (workText.length() > 0) {
1587: // add, and remove spaces at the beginning and the end :
1588: parameterElementsTextVector.addElement(workText
1589: .trim());
1590: workText = new String("");
1591: }
1592: } else if (workCharacter == '(') {
1593: childLevel++;
1594: workText += "" + workCharacter;
1595: } else if (workCharacter == ')') {
1596: childLevel--;
1597: workText += "" + workCharacter;
1598: } else {
1599: // Append. Tabs, newlines or return characters also are valid
1600: // delimiters, so we replace them by a space here. (easier)
1601: // Note: We cannot skip spaces (delimiters) anywhere, cause we would merge
1602: // statements of the type "new Component()". We trim the identifiers later.
1603: if ((workCharacter == '\t') || (workCharacter == '\r')
1604: || (workCharacter == '\n')) {
1605: workText += " ";
1606: } else {
1607: workText += "" + workCharacter;
1608: }
1609: }
1610: } // for loop
1611: // Ending : Add the workText, if it still contains characters :
1612: if (workText.length() > 0) {
1613: // add, and remove spaces at the beginning and the end :
1614: parameterElementsTextVector.addElement(workText.trim());
1615: }
1616: // Now get the level one parameter texts into an array :
1617: String[] parameterElements = new String[parameterElementsTextVector
1618: .size()];
1619: parameterElementsTextVector.copyInto(parameterElements);
1620:
1621: // The parameterElementClassNames has to be filled to get the signature :
1622: String[] parameterElementClassNames = new String[parameterElementsTextVector
1623: .size()];
1624: for (int i = 0; i < parameterElementClassNames.length; i++) {
1625: parameterElementClassNames[i] = ""; // initialize all to an empty String
1626: }
1627:
1628: //ystem.out.println("CC> calculateParameterSignatureForParameterList> Loop for " +
1629: // parameterElements.length + " elements." );
1630:
1631: // Now try to resolve the parameter class of each parameterElements element,
1632: // which is a kind of [more complicated] recursion, as we create a new search,
1633: // which again can go through this method (multiple times) :
1634: for (int i = 0; i < parameterElements.length; i++) {
1635:
1636: //ystem.out.println("CC> calculateParameterSignatureForParameterList> " + parameterElements[i] );
1637:
1638: // Herewego recursively :
1639: // Note, that the scope FSD always is the same like the search has been
1640: // started for, and not the one of the parser chain : It is
1641: // this.toplevelBasisFSD and not currentFSD.
1642: CodeCompletionSearch ccSearch = new CodeCompletionSearch(
1643: parameterElements[i], 0, this .editorPanel,
1644: this .toplevelBasisFSD,
1645: this .fileStructureDescriptionManager);
1646: ccSearch.performSearch(caretLineNumber);
1647: FSDSearchResult searchResult = ccSearch
1648: .getFoundFSDSearchResult();
1649:
1650: /* Debug:
1651: String fsdName = "<NULL>";
1652: if( searchResult.getFileStructureDescription() != null )
1653: {
1654: fsdName = searchResult.getFileStructureDescription().className.content;
1655: }
1656: String simpleName = "<NULL>";
1657: if( searchResult.getSimpleTypeName() != null )
1658: {
1659: simpleName = searchResult.getSimpleTypeName();
1660: }
1661: ystem.out.println("CC> calculateParameterSignatureForParameterList> Loop run " + i +
1662: " SearchResult has fsd= " + fsdName +
1663: " and simpletypename= " + simpleName );
1664: */
1665:
1666: if (searchResult != null) {
1667: if (searchResult.getFileStructureDescription() != null) {
1668: parameterElementClassNames[i] = searchResult
1669: .getFileStructureDescription().className.content;
1670: } else if (searchResult.getSimpleTypeName() != null) {
1671: parameterElementClassNames[i] = searchResult
1672: .getSimpleTypeName();
1673: }
1674: }
1675: } // for
1676: // Now build the parameter signature :
1677: String signature = "";
1678: for (int i = 0; i < parameterElementClassNames.length; i++) {
1679: if (i == 0) {
1680: signature += parameterElementClassNames[i];
1681: } else {
1682: signature += "," + parameterElementClassNames[i];
1683: }
1684: }
1685: parameterElementsTextVector.setSize(0); // Help the GC by lowering associations
1686:
1687: //System.
1688:
1689: return signature;
1690: } // calculateParameterSignatureForParameterList
1691:
1692: /**
1693: * This method examines the resultDescription and returns methods/attributes,
1694: * which are visible from the current position of the currentlyEditedFSD.
1695: * Methods/Attributes which are not accessible from the current position are excluded,
1696: * and additional accessible methods/attributes from parent classes are added.
1697: */
1698: private CodeCompletionListEntry[] selectVisibleEntriesFrom(
1699: final FileStructureDescription resultDescription,
1700: final boolean onlySelectStaticAttributesAndMethods,
1701: final FileStructureDescription[] anonymousClassChain) {
1702: /* Debug:
1703: ystem.out.println("selectVisibleEntriesFrom [on start] for fsd " +
1704: resultDescription.className.content );
1705: ystem.out.println("selectVisibleEntriesFrom [on start] with member methods: " +
1706: resultDescription.methodDescriptions.size() );
1707: ystem.out.println("selectVisibleEntriesFrom [on start] with inherited methods: " +
1708: resultDescription.inheritedMethodDescriptions.size() );
1709: ystem.out.println("selectVisibleEntriesFrom [on start] with member attributes: " +
1710: resultDescription.fieldDescriptions.size() );
1711: ystem.out.println("selectVisibleEntriesFrom [on start] with inherited attributes: " +
1712: resultDescription.inheritedFieldDescriptions.size() );
1713: */
1714:
1715: final Vector codeCompletionListEntries = new Vector(); // of CodeCompletionListEntry objects with functionality
1716:
1717: // Filter out methods and objects, which are not accessible from
1718: // the source FSD. Condition flags :
1719:
1720: // Include private objects, if the edited FSD is the same
1721: // as the resultDescription ...
1722: boolean sameFSDs = (this .toplevelBasisFSD.fullyQualifiedClassNameBuffer
1723: .toString()
1724: .equals(resultDescription.fullyQualifiedClassNameBuffer
1725: .toString()));
1726: // ... or if the resultDescription is a toplevel anonymous class fsd :
1727: boolean isToplevelAnonymousClassFSD = false;
1728: if (anonymousClassChain.length > 0) {
1729: FileStructureDescription toplevelAnonymousClassFSD = anonymousClassChain[anonymousClassChain.length - 1];
1730: isToplevelAnonymousClassFSD = (toplevelAnonymousClassFSD.fullyQualifiedClassNameBuffer
1731: .toString()
1732: .equals(resultDescription.fullyQualifiedClassNameBuffer
1733: .toString()));
1734: }
1735:
1736: // ... or if the resultDescription is a nested class of the toplevel fsd :
1737: boolean isNestedClass = this .fileStructureDescriptionManager
1738: .getIsInnerClassOf(resultDescription,
1739: this .toplevelBasisFSD);
1740:
1741: //ystem.out.println("selectVisibleEntriesFrom isNestedClass = " + isNestedClass );
1742:
1743: boolean includePrivateObjects = sameFSDs
1744: || isToplevelAnonymousClassFSD || isNestedClass;
1745:
1746: // Include parent package-scope objects, if the package of
1747: // the edited and the result FSD are the same.
1748: // ***Important Exception*** : If the resultdescription is an interface,
1749: // also package-scope objects ALWAYS have public scope.
1750: boolean includePackageScopeObjects = (this .toplevelBasisFSD.packageName.content
1751: .equals(resultDescription.packageName.content) || (resultDescription.isInterface ));
1752:
1753: // Include protected parent objects, if the result FSD is
1754: // in the parentchain of the edited FSD or if the two FSD's are the same:
1755: boolean includeProtectedObjects = ((this .isParentOf(
1756: resultDescription, this .toplevelBasisFSD))
1757: || (sameFSDs) || (isNestedClass));
1758:
1759: /*
1760: ystem.out.println("selectVisibleEntriesFrom [before call] with member methods: " +
1761: resultDescription.methodDescriptions.size() );
1762: ystem.out.println("selectVisibleEntriesFrom [before call] with inherited methods: " +
1763: resultDescription.inheritedMethodDescriptions.size() );
1764: */
1765:
1766: // Retrieve the short textrepresentations of all inscope methods
1767: this .addMethodEntriesOf(resultDescription,
1768: resultDescription.methodDescriptions,
1769: codeCompletionListEntries, true, includePrivateObjects,
1770: includePackageScopeObjects, includeProtectedObjects,
1771: onlySelectStaticAttributesAndMethods);
1772: // inherited methods :
1773: this .addMethodEntriesOf(resultDescription,
1774: resultDescription.inheritedMethodDescriptions,
1775: codeCompletionListEntries, false,
1776: false, // never include private objects of parent classes
1777: includePackageScopeObjects, includeProtectedObjects,
1778: onlySelectStaticAttributesAndMethods);
1779: // inherited attributes :
1780: this .addFieldEntriesOf(resultDescription,
1781: resultDescription.inheritedFieldDescriptions,
1782: codeCompletionListEntries, false,
1783: false, // never include private objects of parent classes
1784: includePackageScopeObjects, includeProtectedObjects,
1785: onlySelectStaticAttributesAndMethods);
1786: // Same with all inscope member attributes :
1787: this .addFieldEntriesOf(resultDescription,
1788: resultDescription.fieldDescriptions,
1789: codeCompletionListEntries, true, includePrivateObjects,
1790: includePackageScopeObjects, includeProtectedObjects,
1791: onlySelectStaticAttributesAndMethods);
1792:
1793: // A special case for visible class attributes when the cursor is inside
1794: // a nested or anonymous class: In this case, one can extend the scope
1795: // by writing <toplevel classname> this
1796: // Therefore "this" is added like a class attribute for these cases:
1797: if (onlySelectStaticAttributesAndMethods && sameFSDs) {
1798: FileStructureDescriptionForField this VirtualFieldFSD = new FileStructureDescriptionForField(
1799: Modifier.PUBLIC + Modifier.STATIC, false,
1800: new BooleanInteger(false, 0), Language
1801: .Translate("toplevel scope extension to"),
1802: resultDescription.className.content, "this");
1803: final String objectNameString = this VirtualFieldFSD.objectNameWithPosition.content;
1804: final CodeCompletionListEntry entry = new CodeCompletionListEntry(
1805: resultDescription,
1806: this .fileStructureDescriptionManager,
1807: this VirtualFieldFSD, true) {
1808: public void run() {
1809: try {
1810: final int offset = insertPosition;
1811: final String string = objectNameString;
1812: final Style style = StylesFactory
1813: .GetEditorTextStyle();
1814: editorPanel.getEditor().getDocument()
1815: .insertString(offset, string, style);
1816: } catch (Exception ouiwermnb) {
1817: ouiwermnb.printStackTrace();
1818: }
1819: }
1820: };
1821: codeCompletionListEntries.addElement(entry);
1822: } // if
1823:
1824: // Plus attributes defined in an interface, if one or multiple interfaces
1825: // were implemented, including the optionally following extension chains,
1826: // which are created, when each interface again extends other
1827: // interfaces [Note that an interface neither can implement anything nor
1828: // can it extend a class - only option is <interface extends another interface> ].
1829: // The complete extension chains are included by getAllFieldsFromAllInterfacesFor().
1830: this .addFieldEntriesOf(resultDescription,
1831: CodeCompletionUtilities
1832: .GetAllFieldsFromAllInterfacesFor(
1833: resultDescription,
1834: this .fileStructureDescriptionManager),
1835: codeCompletionListEntries, true, includePrivateObjects,
1836: includePackageScopeObjects, includeProtectedObjects,
1837: onlySelectStaticAttributesAndMethods);
1838:
1839: // static inner classes:
1840: this .addStaticInnerClassEntriesOf(resultDescription,
1841: resultDescription.innerClasses,
1842: codeCompletionListEntries, true,
1843: includePackageScopeObjects, includeProtectedObjects);
1844:
1845: // Fast copy all into the returned array :
1846: final CodeCompletionListEntry[] codeCompletionListEntryArray = new CodeCompletionListEntry[codeCompletionListEntries
1847: .size()];
1848: codeCompletionListEntries
1849: .copyInto(codeCompletionListEntryArray);
1850:
1851: //ystem.out.println("CCSearch selectVisibleEntriesFrom " + resultDescription.className.content +
1852: // " has found " + codeCompletionListEntryArray.length + " entries." );
1853:
1854: return codeCompletionListEntryArray;
1855: } // selectVisibleEntriesFrom
1856:
1857: private boolean isParentOf(
1858: final FileStructureDescription parentCandidate,
1859: final FileStructureDescription child) {
1860: boolean isParent = false;
1861: for (int i = 0; i < child.parentDescriptions.length; i++) {
1862: final FileStructureDescription pFSD = child.parentDescriptions[i];
1863: if (pFSD.fullyQualifiedClassNameBuffer.toString().equals(
1864: parentCandidate.fullyQualifiedClassNameBuffer
1865: .toString())) {
1866: isParent = true;
1867: break;
1868: }
1869: }
1870: return isParent;
1871: } // isParentOf
1872:
1873: /**
1874: * Called from selectVisibleEntriesFrom().
1875: */
1876: private void addStaticInnerClassEntriesOf(
1877: final FileStructureDescription parentFSD,
1878: final Vector innerClasses,
1879: final Vector codeCompletionListEntries,
1880: final boolean isTopLevel,
1881: final boolean includePackageScopeObjects,
1882: final boolean includeProtectedObjects) {
1883: for (int i = 0; i < innerClasses.size(); i++) {
1884: final FileStructureDescription this InnerClassFSD = (FileStructureDescription) innerClasses
1885: .elementAt(i);
1886: final String innerClassName = this InnerClassFSD.className.content;
1887: final CodeCompletionListEntry entry = new CodeCompletionListEntry(
1888: parentFSD, this .fileStructureDescriptionManager,
1889: this InnerClassFSD, isTopLevel) {
1890: public void run() {
1891: try {
1892: final int offset = insertPosition;
1893: String stringForInsertion = innerClassName;
1894: final Style style = StylesFactory
1895: .GetEditorTextStyle();
1896: editorPanel.getEditor().getDocument()
1897: .insertString(offset,
1898: stringForInsertion, style);
1899: } catch (Exception kjhsdf) {
1900: kjhsdf.printStackTrace();
1901: }
1902: }
1903: };
1904: // Now add this, if the visibility conditions are ok :
1905: if (!this InnerClassFSD.isPrivate()) // private inner classes never are visible globally
1906: {
1907: if (includePackageScopeObjects
1908: || !this InnerClassFSD.isPackageScope()) {
1909: if (includeProtectedObjects
1910: || !this InnerClassFSD.isProtected()) {
1911: if (this InnerClassFSD.isStatic()) // static is required always here
1912: {
1913: codeCompletionListEntries.addElement(entry);
1914: }
1915: }
1916: }
1917: }
1918: }
1919: } // addStaticInnerClassEntriesOf
1920:
1921: /**
1922: * Called from selectVisibleEntriesFrom().
1923: */
1924: private void addMethodEntriesOf(
1925: final FileStructureDescription parentFSD,
1926: final Vector methodDescriptions,
1927: final Vector codeCompletionListEntries,
1928: final boolean isTopLevel,
1929: final boolean includePrivateObjects,
1930: final boolean includePackageScopeObjects,
1931: final boolean includeProtectedObjects,
1932: final boolean onlyIncludeStaticMethods) {
1933: for (int i = 0; i < methodDescriptions.size(); i++) {
1934: final FileStructureDescriptionForMethod this MethodFSD = (FileStructureDescriptionForMethod) methodDescriptions
1935: .elementAt(i);
1936: final String methodName = this MethodFSD.name.content;
1937: final CodeCompletionListEntry entry = new CodeCompletionListEntry(
1938: parentFSD, this .fileStructureDescriptionManager,
1939: this MethodFSD, isTopLevel) {
1940: public void run() {
1941: try {
1942: final int offset = insertPosition;
1943: String stringForInsertion = null;
1944: // If that method has no parameters, we can add both brackets :
1945:
1946: //ystem.out.println("np = " + thisMethodFSD.parameters.length);
1947:
1948: if (this MethodFSD.parameters.length == 0) {
1949: stringForInsertion = methodName + "()";
1950: } else {
1951: stringForInsertion = methodName + "(";
1952: // NOTE: The additional "(" character will automatically
1953: // launch the ParameterCompletion in the editor on insertion.
1954: }
1955:
1956: //ystem.out.println("Inserting: " + stringForInsertion );
1957:
1958: final Style style = StylesFactory
1959: .GetEditorTextStyle();
1960: editorPanel.getEditor().getDocument()
1961: .insertString(offset,
1962: stringForInsertion, style);
1963: } catch (Exception kjhsdf) {
1964: kjhsdf.printStackTrace();
1965: }
1966: }
1967: };
1968: // Now add this, if the visibility conditions are ok :
1969: if (includePrivateObjects || !this MethodFSD.isPrivate()) {
1970: if (includePackageScopeObjects
1971: || !this MethodFSD.isPackageScope()) {
1972: if (includeProtectedObjects
1973: || !this MethodFSD.isProtected()) {
1974: if (!onlyIncludeStaticMethods
1975: || this MethodFSD.isStatic()) {
1976: codeCompletionListEntries.addElement(entry);
1977: }
1978: }
1979: }
1980: }
1981: }
1982: } // addMethodEntriesOf
1983:
1984: /**
1985: * Called from selectVisibleEntriesFrom().
1986: */
1987: private void addFieldEntriesOf(
1988: final FileStructureDescription parentFSD,
1989: final Vector fieldDescriptions,
1990: final Vector codeCompletionListEntries,
1991: final boolean isTopLevel,
1992: final boolean includePrivateObjects,
1993: final boolean includePackageScopeObjects,
1994: final boolean includeProtectedObjects,
1995: final boolean onlyIncludeStaticFields) {
1996: final Vector fieldMenuItems = new Vector(); // of JMenuItem objects with functionality
1997: for (int i = 0; i < fieldDescriptions.size(); i++) {
1998: final FileStructureDescriptionForField fieldFSD = (FileStructureDescriptionForField) fieldDescriptions
1999: .elementAt(i);
2000: final String objectNameString = fieldFSD.objectNameWithPosition.content;
2001: final CodeCompletionListEntry entry = new CodeCompletionListEntry(
2002: parentFSD, this .fileStructureDescriptionManager,
2003: fieldFSD, isTopLevel) {
2004: public void run() {
2005: try {
2006: final int offset = insertPosition;
2007: final String string = objectNameString;
2008: final Style style = StylesFactory
2009: .GetEditorTextStyle();
2010: editorPanel.getEditor().getDocument()
2011: .insertString(offset, string, style);
2012: } catch (Exception ouiwermnb) {
2013: ouiwermnb.printStackTrace();
2014: }
2015: }
2016: };
2017: // Now add this, if the visibility conditions are ok :
2018: if (includePrivateObjects || !fieldFSD.isPrivate()) {
2019: if (includePackageScopeObjects
2020: || !fieldFSD.isPackageScope()) {
2021: if (includeProtectedObjects
2022: || !fieldFSD.isProtected()) {
2023: if (!onlyIncludeStaticFields
2024: || fieldFSD.isStatic()) {
2025: codeCompletionListEntries.addElement(entry);
2026: }
2027: }
2028: }
2029: }
2030: }
2031: } // addFieldEntriesOf
2032:
2033: /**
2034: * Decompose a.b.c into the array {a,b,c}
2035: *
2036: * Also, parameter arguments are skipped in the decomposition, because they
2037: * lie one or more recursion levels deeper from the parser point of view :
2038: * Example: a.get( this.getX() ).b.c is decomposed into
2039: * { a, get( this.getX() ), b, c } that is: The point after "this" lies
2040: * deeper.
2041: */
2042: private String[] decompose(final String identifier) {
2043:
2044: //ystem.out.println(">decompose starts. arg= " + identifier );
2045:
2046: // Round brackets go one recursion step deeper.
2047: // Tokenize the first level only, but be prepared, that the brackets don't
2048: // have to be balanced (because of possible syntax errors of the programmer).
2049: Vector elementsVector = new Vector();
2050: StringBuffer workText = new StringBuffer(""); // Empty
2051: char workCharacter;
2052: int childLevel = 0;
2053: for (int i = 0; i < identifier.length(); i++) {
2054: workCharacter = identifier.charAt(i);
2055: if ((workCharacter == '.') && (childLevel == 0)) {
2056: // Add the existing text and make a new one ready :
2057: if (workText.length() > 0) {
2058: // Make a new uncoupled instance and add it:
2059: elementsVector.addElement(new String(workText
2060: .toString()));
2061: workText.setLength(0);
2062: }
2063: } else if (workCharacter == '(') {
2064: childLevel++;
2065: workText.append(workCharacter);
2066: } else if (workCharacter == ')') {
2067: childLevel--;
2068: workText.append(workCharacter);
2069: } else {
2070: workText.append(workCharacter);
2071: }
2072: } // for loop
2073: // Ending : Add the workText, if it still contains characters :
2074: if (workText.length() > 0) {
2075: elementsVector.addElement(new String(workText.toString()));
2076: }
2077: // Now get the level one parameters into an array :
2078: String[] tokens = new String[elementsVector.size()];
2079: elementsVector.copyInto(tokens);
2080: elementsVector.removeAllElements();
2081: elementsVector = null;
2082: // Trim all:
2083: for (int i = 0; i < tokens.length; i++) {
2084: tokens[i] = tokens[i].trim();
2085: }
2086: workText.setLength(0);
2087: workText = null;
2088: return tokens;
2089: }
2090:
2091: /**
2092: * Returns the position, where the bracket at the passed position
2093: * it closed.
2094: * openingBracketPosition must point to a opening bracket.
2095: */
2096: private int getPositionOfClosingBracket(final String s,
2097: int openingBracketPosition) {
2098: int closingPosition = -1;
2099: // Make sure, there is an opening bracket at openingBracketPosition:
2100: if (s.charAt(openingBracketPosition) == '(') {
2101: if (openingBracketPosition < s.length()) {
2102: int bracketLevel = 0;
2103: int index = openingBracketPosition;
2104: char ch;
2105: while (index < s.length()) {
2106: ch = s.charAt(index);
2107: if (ch == '(') // Note: This will be true for the first run,
2108: { // increasing bracketLevel to 1
2109: bracketLevel++;
2110: } else if (ch == ')') {
2111: bracketLevel--;
2112: }
2113: if (bracketLevel == 0) {
2114: closingPosition = index;
2115: break;
2116: }
2117: index++;
2118: } // while
2119: } // if
2120: } else {
2121: //ystem.out.println("*** CCSearch.getPositionOfClosingBracket: Invalid opening bracket position.");
2122: }
2123: return closingPosition;
2124: } // getPositionOfClosingBracket
2125:
2126: /**
2127: * Removes a possible cast.
2128: */
2129: private String checkRemoveCastIn(final String id) {
2130: // Assume there is an opening bracket at the start.
2131: int closingPosition = this .getPositionOfClosingBracket(id, 0);
2132: if (closingPosition > 0) {
2133: return id.substring(closingPosition + 1);
2134: } else {
2135: return id;
2136: }
2137: } // removeCastIn
2138:
2139: /**
2140: * If the passed identifier starts with a typecast, this typecast
2141: * is returned as NumberedString, where the number is the
2142: * optional array dimension of the cast, or 1 for single types.
2143: * Otherwise null is returned.
2144: */
2145: private NumberedString checkReturnCastIn(String identifier) {
2146: //ystem.out.println("checkReturnCastIn() called for identifier= " + identifier );
2147:
2148: // Initialize the content as null, because this is signals, that
2149: // no cast has been detected. The number is set to zero == no array as default.
2150: NumberedString typeCast = new NumberedString(null, 0);
2151: identifier = identifier.trim(); // get rid of leading spaces
2152: if (identifier.startsWith("(")) {
2153: // If it's a valid typecast, the opening bracket must be followed
2154: // ONLY by java identifiers, white-spaces and finally by a closing bracket.
2155: // After that closing bracket, at least one java identifier must follow.
2156: int closingBracketPosition = -1;
2157: boolean isValidTypeCast = false;
2158: int stringIndex = 1;
2159: while (stringIndex < identifier.length()) {
2160: final char ch = identifier.charAt(stringIndex);
2161: if (ch == ')') {
2162: // The cast is valid, if there are some more characters left as identifier.
2163: isValidTypeCast = (stringIndex < identifier
2164: .length() - 1);
2165: break;
2166: }
2167: if ((!Character.isJavaIdentifierPart(ch))
2168: && (!Character.isWhitespace(ch))) {
2169: isValidTypeCast = false;
2170: break;
2171: }
2172: stringIndex++;
2173: } // while
2174: if (isValidTypeCast) // return it
2175: {
2176: // The raw typecast string can contain a series of "[]" array symbols.
2177: String rawTypeCast = identifier.substring(1,
2178: stringIndex).trim();
2179: // Check for arrays and count them :
2180: int arrayDimension = 0; // 0 == no array but a single value
2181: // The array symbols can have spaces or tabs or... between the brackets,
2182: // so take an easy solution: Just count the [ and ] characters. If the
2183: // syntax is wrong, the search anyway won't find anything:
2184: int leftBracketNumber = 0;
2185: int rightBracketNumber = 0;
2186: char ch;
2187: for (int i = 0; i < rawTypeCast.length(); i++) {
2188: ch = rawTypeCast.charAt(i);
2189: if (ch == '[')
2190: leftBracketNumber++;
2191: if (ch == ']')
2192: rightBracketNumber++;
2193: }
2194: if ((leftBracketNumber == rightBracketNumber)
2195: && (leftBracketNumber > 0)) {
2196: int firstLeftBracketPosition = rawTypeCast
2197: .indexOf('[');
2198: typeCast.content = rawTypeCast.substring(0,
2199: firstLeftBracketPosition);
2200: typeCast.number = leftBracketNumber; // array dimension
2201: } else {
2202: typeCast.content = rawTypeCast;
2203: typeCast.number = 0; // array dimension == single type ==> 1
2204: }
2205: }
2206: } // if
2207: return typeCast;
2208: } // checkReturnCastIn
2209:
2210: /**
2211: * Updates the transient fields of the passed fsd and adds the fsd to the
2212: * fsdsWithUpdatedTransientFields vector, so that it will be released
2213: * when freeAllTransientFields() is called.
2214: *
2215: * ALWAYS use this method for updating transient fields, otherwise the
2216: * memory explodes.
2217: */
2218: private void updateTransientFieldsFor(
2219: final FileStructureDescription fsd) {
2220: this .fileStructureDescriptionManager
2221: .updateTransientFieldsFor(fsd);
2222: this .fsdsWithUpdatedTransientFields.addElement(fsd);
2223: }
2224:
2225: /**
2226: * This method must be called before the CodeCompletionSearch object is
2227: * left to the GC, so that all updated transient fields are released.
2228: * Otherwise the memory would explode.
2229: */
2230: public void freeAllTransientFields() {
2231: FileStructureDescription fsd = null;
2232: for (int i = 0; i < this .fsdsWithUpdatedTransientFields.size(); i++) {
2233: fsd = (FileStructureDescription) this .fsdsWithUpdatedTransientFields
2234: .elementAt(i);
2235: this .fileStructureDescriptionManager
2236: .releaseTransientFieldsFor(fsd);
2237: }
2238: this .fsdsWithUpdatedTransientFields.setSize(0);
2239:
2240: } // freeAllTransientFields
2241:
2242: } // CodeCompletionSearch
|