/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.wst.jsdt.internal.codeassist.complete; /* * Parser able to build specific completion parse nodes, given a cursorLocation. * * Cursor location denotes the position of the last character behind which completion * got requested: * -1 means completion at the very beginning of the source * 0 means completion behind the first character * n means completion behind the n-th character */ import org.eclipse.wst.jsdt.core.ast.IExpression; import org.eclipse.wst.jsdt.core.ast.IFieldReference; import org.eclipse.wst.jsdt.core.ast.ISingleNameReference; import org.eclipse.wst.jsdt.core.compiler.CharOperation; import org.eclipse.wst.jsdt.internal.codeassist.impl.AssistParser; import org.eclipse.wst.jsdt.internal.codeassist.impl.Keywords; import org.eclipse.wst.jsdt.internal.compiler.CompilationResult; import org.eclipse.wst.jsdt.internal.compiler.ast.AND_AND_Expression; import org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode; import org.eclipse.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.AbstractVariableDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.AllocationExpression; import org.eclipse.wst.jsdt.internal.compiler.ast.Argument; import org.eclipse.wst.jsdt.internal.compiler.ast.ArrayAllocationExpression; import org.eclipse.wst.jsdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.wst.jsdt.internal.compiler.ast.ArrayReference; import org.eclipse.wst.jsdt.internal.compiler.ast.Assignment; import org.eclipse.wst.jsdt.internal.compiler.ast.BinaryExpression; import org.eclipse.wst.jsdt.internal.compiler.ast.Block; import org.eclipse.wst.jsdt.internal.compiler.ast.CaseStatement; import org.eclipse.wst.jsdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.CompoundAssignment; import org.eclipse.wst.jsdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.EqualExpression; import org.eclipse.wst.jsdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.wst.jsdt.internal.compiler.ast.Expression; import org.eclipse.wst.jsdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.ImportReference; import org.eclipse.wst.jsdt.internal.compiler.ast.Initializer; import org.eclipse.wst.jsdt.internal.compiler.ast.IntLiteral; import org.eclipse.wst.jsdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.MessageSend; import org.eclipse.wst.jsdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.NameReference; import org.eclipse.wst.jsdt.internal.compiler.ast.OR_OR_Expression; import org.eclipse.wst.jsdt.internal.compiler.ast.PrefixExpression; import org.eclipse.wst.jsdt.internal.compiler.ast.QualifiedAllocationExpression; import org.eclipse.wst.jsdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.wst.jsdt.internal.compiler.ast.ReturnStatement; import org.eclipse.wst.jsdt.internal.compiler.ast.SingleNameReference; import org.eclipse.wst.jsdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.wst.jsdt.internal.compiler.ast.Statement; import org.eclipse.wst.jsdt.internal.compiler.ast.StringLiteral; import org.eclipse.wst.jsdt.internal.compiler.ast.SuperReference; import org.eclipse.wst.jsdt.internal.compiler.ast.SwitchStatement; import org.eclipse.wst.jsdt.internal.compiler.ast.ThisReference; import org.eclipse.wst.jsdt.internal.compiler.ast.TryStatement; import org.eclipse.wst.jsdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.TypeReference; import org.eclipse.wst.jsdt.internal.compiler.ast.UnaryExpression; import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.wst.jsdt.internal.compiler.env.ICompilationUnit; import org.eclipse.wst.jsdt.internal.compiler.parser.JavadocParser; import org.eclipse.wst.jsdt.internal.compiler.parser.RecoveredBlock; import org.eclipse.wst.jsdt.internal.compiler.parser.RecoveredElement; import org.eclipse.wst.jsdt.internal.compiler.parser.RecoveredField; import org.eclipse.wst.jsdt.internal.compiler.parser.RecoveredInitializer; import org.eclipse.wst.jsdt.internal.compiler.parser.RecoveredLocalVariable; import org.eclipse.wst.jsdt.internal.compiler.parser.RecoveredMethod; import org.eclipse.wst.jsdt.internal.compiler.parser.RecoveredType; import org.eclipse.wst.jsdt.internal.compiler.parser.RecoveredUnit; import org.eclipse.wst.jsdt.internal.compiler.problem.AbortCompilation; import org.eclipse.wst.jsdt.internal.compiler.problem.ProblemReporter; import org.eclipse.wst.jsdt.internal.compiler.util.Util; public class CompletionParser extends AssistParser { // OWNER protected static final int COMPLETION_PARSER = 1024; protected static final int COMPLETION_OR_ASSIST_PARSER = ASSIST_PARSER + COMPLETION_PARSER; // KIND : all values known by CompletionParser are between 1025 and 1549 protected static final int K_BLOCK_DELIMITER = COMPLETION_PARSER + 1; // whether we are inside a block protected static final int K_SELECTOR_INVOCATION_TYPE = COMPLETION_PARSER + 2; // whether we are inside a message send protected static final int K_SELECTOR_QUALIFIER = COMPLETION_PARSER + 3; // whether we are inside a message send protected static final int K_BETWEEN_CATCH_AND_RIGHT_PAREN = COMPLETION_PARSER + 4; // whether we are between the keyword 'catch' and the following ')' protected static final int K_NEXT_TYPEREF_IS_CLASS = COMPLETION_PARSER + 5; // whether the next type reference is a class protected static final int K_NEXT_TYPEREF_IS_INTERFACE = COMPLETION_PARSER + 6; // whether the next type reference is an interface protected static final int K_NEXT_TYPEREF_IS_EXCEPTION = COMPLETION_PARSER + 7; // whether the next type reference is an exception protected static final int K_BETWEEN_NEW_AND_LEFT_BRACKET = COMPLETION_PARSER + 8; // whether we are between the keyword 'new' and the following left braket, ie. '[', '(' or '{' protected static final int K_INSIDE_THROW_STATEMENT = COMPLETION_PARSER + 9; // whether we are between the keyword 'throw' and the end of a throw statement protected static final int K_INSIDE_RETURN_STATEMENT = COMPLETION_PARSER + 10; // whether we are between the keyword 'return' and the end of a return statement protected static final int K_CAST_STATEMENT = COMPLETION_PARSER + 11; // whether we are between ')' and the end of a cast statement protected static final int K_LOCAL_INITIALIZER_DELIMITER = COMPLETION_PARSER + 12; protected static final int K_ARRAY_INITIALIZER = COMPLETION_PARSER + 13; protected static final int K_ARRAY_CREATION = COMPLETION_PARSER + 14; protected static final int K_UNARY_OPERATOR = COMPLETION_PARSER + 15; protected static final int K_BINARY_OPERATOR = COMPLETION_PARSER + 16; protected static final int K_ASSISGNMENT_OPERATOR = COMPLETION_PARSER + 17; protected static final int K_CONDITIONAL_OPERATOR = COMPLETION_PARSER + 18; protected static final int K_BETWEEN_IF_AND_RIGHT_PAREN = COMPLETION_PARSER + 19; protected static final int K_BETWEEN_WHILE_AND_RIGHT_PAREN = COMPLETION_PARSER + 20; protected static final int K_BETWEEN_FOR_AND_RIGHT_PAREN = COMPLETION_PARSER + 21; protected static final int K_BETWEEN_SWITCH_AND_RIGHT_PAREN = COMPLETION_PARSER + 22; protected static final int K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN = COMPLETION_PARSER + 23; protected static final int K_INSIDE_ASSERT_STATEMENT = COMPLETION_PARSER + 24; protected static final int K_SWITCH_LABEL= COMPLETION_PARSER + 25; protected static final int K_BETWEEN_CASE_AND_COLON = COMPLETION_PARSER + 26; protected static final int K_BETWEEN_DEFAULT_AND_COLON = COMPLETION_PARSER + 27; protected static final int K_BETWEEN_LEFT_AND_RIGHT_BRACKET = COMPLETION_PARSER + 28; protected static final int K_EXTENDS_KEYWORD = COMPLETION_PARSER + 29; protected static final int K_PARAMETERIZED_METHOD_INVOCATION = COMPLETION_PARSER + 30; protected static final int K_PARAMETERIZED_ALLOCATION = COMPLETION_PARSER + 31; protected static final int K_PARAMETERIZED_CAST = COMPLETION_PARSER + 32; protected static final int K_BETWEEN_ANNOTATION_NAME_AND_RPAREN = COMPLETION_PARSER + 33; protected static final int K_INSIDE_BREAK_STATEMENT = COMPLETION_PARSER + 34; protected static final int K_INSIDE_CONTINUE_STATEMENT = COMPLETION_PARSER + 35; protected static final int K_LABEL = COMPLETION_PARSER + 36; protected static final int K_MEMBER_VALUE_ARRAY_INITIALIZER = COMPLETION_PARSER + 37; public final static char[] FAKE_TYPE_NAME = new char[]{' '}; public final static char[] FAKE_METHOD_NAME = new char[]{' '}; public final static char[] FAKE_ARGUMENT_NAME = new char[]{' '}; public final static char[] VALUE = new char[]{'v', 'a', 'l', 'u', 'e'}; /* public fields */ public int cursorLocation; public ASTNode assistNodeParent; // the parent node of assist node /* the following fields are internal flags */ // block kind static final int IF = 1; static final int TRY = 2; static final int CATCH = 3; static final int WHILE = 4; static final int SWITCH = 5; static final int FOR = 6; static final int DO = 7; static final int SYNCHRONIZED = 8; // label kind static final int DEFAULT = 1; // invocation type constants static final int EXPLICIT_RECEIVER = 0; static final int NO_RECEIVER = -1; static final int SUPER_RECEIVER = -2; static final int NAME_RECEIVER = -3; static final int ALLOCATION = -4; static final int QUALIFIED_ALLOCATION = -5; static final int QUESTION = 1; static final int COLON = 2; // K_BETWEEN_ANNOTATION_NAME_AND_RPAREN arguments static final int LPAREN_NOT_CONSUMED = 1; static final int LPAREN_CONSUMED = 2; static final int ANNOTATION_NAME_COMPLETION = 4; // K_PARAMETERIZED_METHOD_INVOCATION arguments static final int INSIDE_NAME = 1; // the type of the current invocation (one of the invocation type constants) int invocationType; // a pointer in the expression stack to the qualifier of a invocation int qualifier; // last modifiers info int lastModifiers = ClassFileConstants.AccDefault; int lastModifiersStart = -1; // depth of '(', '{' and '[]' int bracketDepth; // show if the current token can be an explicit constructor int canBeExplicitConstructor = NO; static final int NO = 0; static final int NEXTTOKEN = 1; static final int YES = 2; protected static final int LabelStackIncrement = 10; char[][] labelStack = new char[LabelStackIncrement][]; int labelPtr = -1; boolean isAlreadyAttached; public boolean record = false; public boolean skipRecord = false; public int recordFrom; public int recordTo; public int potentialVariableNamesPtr; public char[][] potentialVariableNames; public int[] potentialVariableNameStarts; public int[] potentialVariableNameEnds; public CompletionParser(ProblemReporter problemReporter) { super(problemReporter); this.reportSyntaxErrorIsRequired = false; this.javadocParser.checkDocComment = true; } private void addPotentialName(char[] potentialVariableName, int start, int end) { int length = this.potentialVariableNames.length; if (this.potentialVariableNamesPtr >= length - 1) { System.arraycopy( this.potentialVariableNames, 0, this.potentialVariableNames = new char[length * 2][], 0, length); System.arraycopy( this.potentialVariableNameStarts, 0, this.potentialVariableNameStarts = new int[length * 2], 0, length); System.arraycopy( this.potentialVariableNameEnds, 0, this.potentialVariableNameEnds = new int[length * 2], 0, length); } this.potentialVariableNames[++this.potentialVariableNamesPtr] = potentialVariableName; this.potentialVariableNameStarts[this.potentialVariableNamesPtr] = start; this.potentialVariableNameEnds[this.potentialVariableNamesPtr] = end; } public void startRecordingIdentifiers(int from, int to) { this.record = true; this.skipRecord = false; this.recordFrom = from; this.recordTo = to; this.potentialVariableNamesPtr = -1; this.potentialVariableNames = new char[10][]; this.potentialVariableNameStarts = new int[10]; this.potentialVariableNameEnds = new int[10]; } public void stopRecordingIdentifiers() { this.record = true; this.skipRecord = false; } public char[] assistIdentifier(){ return ((CompletionScanner)scanner).completionIdentifier; } protected void attachOrphanCompletionNode(){ if(assistNode == null || this.isAlreadyAttached) return; if (STOP_AT_CURSOR) this.isAlreadyAttached = true; if (this.isOrphanCompletionNode) { if (!STOP_AT_CURSOR) this.isAlreadyAttached = true; ASTNode orphan = this.assistNode; this.isOrphanCompletionNode = false; if (currentElement instanceof RecoveredUnit){ if (orphan instanceof ImportReference){ currentElement.add((ImportReference)orphan, 0); } } /* if in context of a type, then persists the identifier into a fake field return type */ if (currentElement instanceof RecoveredType){ RecoveredType recoveredType = (RecoveredType)currentElement; /* filter out cases where scanner is still inside type header */ if (recoveredType.foundOpeningBrace) { /* generate a pseudo field with a completion on type reference */ if (orphan instanceof TypeReference){ TypeReference fieldType; int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); if(kind == K_BINARY_OPERATOR && info == LESS && this.identifierPtr > -1) { this.pushOnGenericsStack(orphan); fieldType = getTypeReference(0); this.assistNodeParent = fieldType; } else { fieldType = (TypeReference)orphan; } CompletionOnFieldType fieldDeclaration = new CompletionOnFieldType(fieldType, false); // retrieve available modifiers if any if (intPtr >= 2 && intStack[intPtr-1] == this.lastModifiersStart && intStack[intPtr-2] == this.lastModifiers){ fieldDeclaration.modifiersSourceStart = intStack[intPtr-1]; fieldDeclaration.modifiers = intStack[intPtr-2]; } currentElement = currentElement.add(fieldDeclaration, 0); return; } } } /* if in context of a method, persists if inside arguments as a type */ if (currentElement instanceof RecoveredMethod){ RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement; /* only consider if inside method header */ if (!recoveredMethod.foundOpeningBrace) { //if (rParenPos < lParenPos){ // inside arguments if (orphan instanceof TypeReference){ currentElement = currentElement.parent.add( new CompletionOnFieldType((TypeReference)orphan, true), 0); return; } } } if ((topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_CATCH_AND_RIGHT_PAREN)) { if (this.assistNode instanceof CompletionOnSingleTypeReference && ((CompletionOnSingleTypeReference)this.assistNode).isException()) { buildMoreTryStatementCompletionContext((TypeReference)this.assistNode); return; } else if (this.assistNode instanceof CompletionOnQualifiedTypeReference && ((CompletionOnQualifiedTypeReference)this.assistNode).isException()) { buildMoreTryStatementCompletionContext((TypeReference)this.assistNode); return; } } // add the completion node to the method declaration or constructor declaration if (orphan instanceof Statement) { /* check for completion at the beginning of method body behind an invalid signature */ RecoveredMethod method = currentElement.enclosingMethod(); if (method != null){ AbstractMethodDeclaration methodDecl = method.methodDeclaration; if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace && (Util.getLineNumber(orphan.sourceStart, scanner.lineEnds, 0, scanner.linePtr) == Util.getLineNumber(methodDecl.sourceEnd, scanner.lineEnds, 0, scanner.linePtr))){ return; } } // add the completion node as a statement to the list of block statements currentElement = currentElement.add((Statement)orphan, 0); return; } } if (this.isInsideAnnotation()) { // push top expression on ast stack if it contains the completion node Expression expression; if (this.expressionPtr > -1) { expression = this.expressionStack[this.expressionPtr]; if(expression == assistNode) { if (this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_MEMBER_VALUE_ARRAY_INITIALIZER ) { ArrayInitializer arrayInitializer = new ArrayInitializer(); arrayInitializer.expressions = new Expression[]{expression}; } else if(this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN) { if (expression instanceof SingleNameReference) { SingleNameReference nameReference = (SingleNameReference) expression; return; } else if (expression instanceof QualifiedNameReference) { } } else { int index; if((index = lastIndexOfElement(K_ATTRIBUTE_VALUE_DELIMITER)) != -1) { int attributeIndentifierPtr = this.elementInfoStack[index]; int identLengthPtr = this.identifierLengthPtr; int identPtr = this.identifierPtr; while (attributeIndentifierPtr < identPtr) { identPtr -= this.identifierLengthStack[identLengthPtr--]; } if(attributeIndentifierPtr != identPtr) return; this.identifierLengthPtr = identLengthPtr; this.identifierPtr = identPtr; this.identifierLengthPtr--; this.identifierPtr--; return; } } } else { CompletionNodeDetector detector = new CompletionNodeDetector(this.assistNode, expression); if(detector.containsCompletionNode()) { } } } } if(this.currentElement instanceof RecoveredType || this.currentElement instanceof RecoveredMethod) { if(this.currentElement instanceof RecoveredType) { RecoveredType recoveredType = (RecoveredType)this.currentElement; } if ((!isInsideMethod() && !isInsideFieldInitialization())) { if(this.genericsPtr > -1 && this.genericsLengthPtr > -1 && this.genericsIdentifiersLengthPtr > -1) { int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); int numberOfIdentifiers = this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr]; int genPtr = this.genericsPtr; done : for(int i = 0; i <= this.identifierLengthPtr && numberOfIdentifiers > 0; i++){ int identifierLength = this.identifierLengthStack[this.identifierLengthPtr - i]; int length = this.genericsLengthStack[this.genericsLengthPtr - i]; for(int j = 0; j < length; j++) { ASTNode node = this.genericsStack[genPtr - j]; CompletionNodeDetector detector = new CompletionNodeDetector(this.assistNode, node); if(detector.containsCompletionNode()) { if(node == this.assistNode){ if(this.identifierLengthPtr > -1 && this.identifierLengthStack[this.identifierLengthPtr]!= 0) { TypeReference ref = this.getTypeReference(0); this.assistNodeParent = ref; } } else { this.assistNodeParent = detector.getCompletionNodeParent(); } break done; } } genPtr -= length; numberOfIdentifiers -= identifierLength; } if(this.assistNodeParent != null && this.assistNodeParent instanceof TypeReference) { if(this.currentElement instanceof RecoveredType) { this.currentElement = this.currentElement.add(new CompletionOnFieldType((TypeReference)this.assistNodeParent, false), 0); } else { this.currentElement = this.currentElement.add((TypeReference)this.assistNodeParent, 0); } } } } } // the following code applies only in methods, constructors or initializers if ((!isInsideMethod() && !isInsideFieldInitialization() && !isInsideAttributeValue())) { return; } if(this.genericsPtr > -1) { ASTNode node = this.genericsStack[this.genericsPtr]; CompletionNodeDetector detector = new CompletionNodeDetector(this.assistNode, node); if(detector.containsCompletionNode()) { /* check for completion at the beginning of method body behind an invalid signature */ RecoveredMethod method = this.currentElement.enclosingMethod(); if (method != null){ AbstractMethodDeclaration methodDecl = method.methodDeclaration; if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace && (Util.getLineNumber(node.sourceStart, this.scanner.lineEnds, 0, this.scanner.linePtr) == Util.getLineNumber(methodDecl.sourceEnd, this.scanner.lineEnds, 0, this.scanner.linePtr))){ return; } } if(node == this.assistNode){ buildMoreGenericsCompletionContext(node,true); } } } // push top expression on ast stack if it contains the completion node Expression expression; if (this.expressionPtr > -1) { expression = this.expressionStack[this.expressionPtr]; CompletionNodeDetector detector = new CompletionNodeDetector(assistNode, expression); if(detector.containsCompletionNode()) { /* check for completion at the beginning of method body behind an invalid signature */ RecoveredMethod method = currentElement.enclosingMethod(); if (method != null){ AbstractMethodDeclaration methodDecl = method.methodDeclaration; if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace && (Util.getLineNumber(expression.sourceStart, scanner.lineEnds, 0, scanner.linePtr) == Util.getLineNumber(methodDecl.sourceEnd, scanner.lineEnds, 0, scanner.linePtr))){ return; } } if(expression == assistNode || (expression instanceof AllocationExpression && ((AllocationExpression)expression).type == assistNode)){ buildMoreCompletionContext(expression); } else { assistNodeParent = detector.getCompletionNodeParent(); if(assistNodeParent != null) { currentElement = currentElement.add((Statement)assistNodeParent, 0); } else { currentElement = currentElement.add(expression, 0); } } } } } private void buildMoreCompletionContext(Expression expression) { Statement statement = expression; int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if(kind != 0) { int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); nextElement : switch (kind) { case K_SELECTOR_QUALIFIER : int selector = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 2); if(false){// Not Possible selector == THIS_CONSTRUCTOR || selector == SUPER_CONSTRUCTOR) { ExplicitConstructorCall call = new ExplicitConstructorCall(ExplicitConstructorCall.This); call.arguments = new Expression[] {expression}; call.sourceStart = expression.sourceStart; call.sourceEnd = expression.sourceEnd; assistNodeParent = call; } else { int invocType = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER,1); int qualifierExprPtr = info; // find arguments int length = expressionLengthStack[expressionLengthPtr]; // search previous arguments if missing if(this.expressionPtr > 0 && this.expressionLengthPtr > 0 && length == 1) { int start = 0; if (invocType != ALLOCATION && invocType != QUALIFIED_ALLOCATION) start=(int) (identifierPositionStack[selector] >>> 32); else start=expressionStack[info].sourceStart; if(this.expressionStack[expressionPtr-1] != null && this.expressionStack[expressionPtr-1].sourceStart > start) { length += expressionLengthStack[expressionLengthPtr-1]; } } Expression[] arguments = null; if (length != 0) { arguments = new Expression[length]; expressionPtr -= length; System.arraycopy(expressionStack, expressionPtr + 1, arguments, 0, length-1); arguments[length-1] = expression; } if(invocType != ALLOCATION && invocType != QUALIFIED_ALLOCATION) { MessageSend messageSend = new MessageSend(); messageSend.selector = identifierStack[selector]; messageSend.arguments = arguments; // find receiver switch (invocType) { case NO_RECEIVER: messageSend.receiver = ThisReference.implicitThis(); break; case NAME_RECEIVER: // remove special flags for primitive types while (this.identifierLengthPtr >= 0 && this.identifierLengthStack[this.identifierLengthPtr] < 0) { this.identifierLengthPtr--; } // remove selector this.identifierPtr--; if(this.genericsPtr > -1 && this.genericsLengthPtr > -1 && this.genericsLengthStack[this.genericsLengthPtr] > 0) { // is inside a paremeterized method: bar.<X>.foo this.identifierLengthPtr--; } else { this.identifierLengthStack[this.identifierLengthPtr]--; } // consume the receiver int identifierLength = this.identifierLengthStack[this.identifierLengthPtr]; if(this.identifierPtr > -1 && identifierLength > 0 && this.identifierPtr + 1 >= identifierLength) { messageSend.receiver = this.getUnspecifiedReference(); } else { messageSend = null; } break; case SUPER_RECEIVER: messageSend.receiver = new SuperReference(0, 0); break; case EXPLICIT_RECEIVER: messageSend.receiver = this.expressionStack[qualifierExprPtr]; break; default : messageSend.receiver = ThisReference.implicitThis(); break; } assistNodeParent = messageSend; } else { if(invocType == ALLOCATION) { AllocationExpression allocationExpr = new AllocationExpression(); allocationExpr.arguments = arguments; // pushOnGenericsIdentifiersLengthStack(identifierLengthStack[identifierLengthPtr]); // pushOnGenericsLengthStack(0); // allocationExpr.type = getTypeReference(0); allocationExpr.member=expressionStack[info]; assistNodeParent = allocationExpr; } else { QualifiedAllocationExpression allocationExpr = new QualifiedAllocationExpression(); allocationExpr.enclosingInstance = this.expressionStack[qualifierExprPtr]; allocationExpr.arguments = arguments; pushOnGenericsIdentifiersLengthStack(identifierLengthStack[identifierLengthPtr]); pushOnGenericsLengthStack(0); allocationExpr.type = getTypeReference(0); assistNodeParent = allocationExpr; } } } break nextElement; case K_INSIDE_RETURN_STATEMENT : if(info == bracketDepth) { ReturnStatement returnStatement = new ReturnStatement(expression, expression.sourceStart, expression.sourceEnd); assistNodeParent = returnStatement; } break nextElement; case K_UNARY_OPERATOR : if(expressionPtr > -1) { Expression operatorExpression = null; switch (info) { case PLUS_PLUS : operatorExpression = new PrefixExpression(expression,IntLiteral.getOne(), PLUS, expression.sourceStart); break; case MINUS_MINUS : operatorExpression = new PrefixExpression(expression,IntLiteral.getOne(), MINUS, expression.sourceStart); break; default : operatorExpression = new UnaryExpression(expression, info); break; } assistNodeParent = operatorExpression; } break nextElement; case K_BINARY_OPERATOR : if(expressionPtr > -1) { Expression operatorExpression = null; Expression left = null; if(expressionPtr == 0) { // it is a ***_NotName rule if(this.identifierPtr > -1) { left = getUnspecifiedReferenceOptimized(); } } else { left = this.expressionStack[expressionPtr-1]; // is it a ***_NotName rule ? if(this.identifierPtr > -1) { int start = (int) (identifierPositionStack[this.identifierPtr] >>> 32); if(left.sourceStart < start) { left = getUnspecifiedReferenceOptimized(); } } } if(left != null) { switch (info) { case AND_AND : operatorExpression = new AND_AND_Expression(left, expression, info); break; case OR_OR : operatorExpression = new OR_OR_Expression(left, expression, info); break; case EQUAL_EQUAL : case NOT_EQUAL : operatorExpression = new EqualExpression(left, expression, info); break; default : operatorExpression = new BinaryExpression(left, expression, info); break; } } if(operatorExpression != null) { assistNodeParent = operatorExpression; } } break nextElement; case K_ARRAY_INITIALIZER : ArrayInitializer arrayInitializer = new ArrayInitializer(); arrayInitializer.expressions = new Expression[]{expression}; expressionPtr -= expressionLengthStack[expressionLengthPtr--]; if(expressionLengthPtr > -1 && expressionPtr > -1 && this.expressionStack[expressionPtr] != null && this.expressionStack[expressionPtr].sourceStart > info) { expressionLengthPtr--; } lastCheckPoint = scanner.currentPosition; if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_ARRAY_CREATION) { ArrayAllocationExpression allocationExpression = new ArrayAllocationExpression(); pushOnGenericsLengthStack(0); pushOnGenericsIdentifiersLengthStack(identifierLengthStack[identifierLengthPtr]); allocationExpression.type = getTypeReference(0); allocationExpression.type.bits |= ASTNode.IgnoreRawTypeCheck; // no need to worry about raw type usage int length = expressionLengthStack[expressionLengthPtr]; allocationExpression.dimensions = new Expression[length]; allocationExpression.initializer = arrayInitializer; assistNodeParent = allocationExpression; } else if(currentElement instanceof RecoveredField && !(currentElement instanceof RecoveredInitializer)) { RecoveredField recoveredField = (RecoveredField) currentElement; if(recoveredField.fieldDeclaration.type.dimensions() == 0) { Block block = new Block(0); block.sourceStart = info; currentElement = currentElement.add(block, 1); } else { statement = arrayInitializer; } } else if(currentElement instanceof RecoveredLocalVariable) { RecoveredLocalVariable recoveredLocalVariable = (RecoveredLocalVariable) currentElement; if(recoveredLocalVariable.localDeclaration.type.dimensions() == 0) { Block block = new Block(0); block.sourceStart = info; currentElement = currentElement.add(block, 1); } else { statement = arrayInitializer; } } else { statement = arrayInitializer; } break nextElement; case K_ARRAY_CREATION : ArrayAllocationExpression allocationExpression = new ArrayAllocationExpression(); allocationExpression.type = getTypeReference(0); allocationExpression.dimensions = new Expression[]{expression}; assistNodeParent = allocationExpression; break nextElement; case K_ASSISGNMENT_OPERATOR : if(expressionPtr > 0 && expressionStack[expressionPtr - 1] != null) { Assignment assignment; if(info == EQUAL) { assignment = new Assignment( expressionStack[expressionPtr - 1], expression, expression.sourceEnd ); } else { assignment = new CompoundAssignment( expressionStack[expressionPtr - 1], expression, info, expression.sourceEnd ); } assistNodeParent = assignment; } break nextElement; case K_CONDITIONAL_OPERATOR : if(info == QUESTION) { if(expressionPtr > 0) { expressionPtr--; expressionLengthPtr--; expressionStack[expressionPtr] = expressionStack[expressionPtr+1]; popElement(K_CONDITIONAL_OPERATOR); buildMoreCompletionContext(expression); return; } } else { if(expressionPtr > 1) { expressionPtr = expressionPtr - 2; expressionLengthPtr = expressionLengthPtr - 2; expressionStack[expressionPtr] = expressionStack[expressionPtr+2]; popElement(K_CONDITIONAL_OPERATOR); buildMoreCompletionContext(expression); return; } } break nextElement; case K_BETWEEN_LEFT_AND_RIGHT_BRACKET : ArrayReference arrayReference; if(identifierPtr < 0 && expressionPtr > 0 && expressionStack[expressionPtr] == expression) { arrayReference = new ArrayReference( expressionStack[expressionPtr-1], expression); } else { arrayReference = new ArrayReference( getUnspecifiedReferenceOptimized(), expression); } assistNodeParent = arrayReference; break; case K_BETWEEN_CASE_AND_COLON : if(this.expressionPtr > 0) { SwitchStatement switchStatement = new SwitchStatement(); switchStatement.expression = this.expressionStack[this.expressionPtr - 1]; if(this.astLengthPtr > -1 && this.astPtr > -1) { int length = this.astLengthStack[this.astLengthPtr]; int newAstPtr = this.astPtr - length; ASTNode firstNode = this.astStack[newAstPtr + 1]; if(length != 0 && firstNode.sourceStart > switchStatement.expression.sourceEnd) { switchStatement.statements = new Statement[length + 1]; System.arraycopy( this.astStack, newAstPtr + 1, switchStatement.statements, 0, length); } } CaseStatement caseStatement = new CaseStatement(expression, expression.sourceStart, expression.sourceEnd); if(switchStatement.statements == null) { switchStatement.statements = new Statement[]{caseStatement}; } else { switchStatement.statements[switchStatement.statements.length - 1] = caseStatement; } assistNodeParent = switchStatement; } break; } } if(assistNodeParent != null) { currentElement = currentElement.add((Statement)assistNodeParent, 0); } else { if(currentElement instanceof RecoveredField && !(currentElement instanceof RecoveredInitializer) && ((RecoveredField) currentElement).fieldDeclaration.initialization == null) { assistNodeParent = ((RecoveredField) currentElement).fieldDeclaration; currentElement = currentElement.add(statement, 0); } else if(currentElement instanceof RecoveredLocalVariable && ((RecoveredLocalVariable) currentElement).localDeclaration.initialization == null) { assistNodeParent = ((RecoveredLocalVariable) currentElement).localDeclaration; currentElement = currentElement.add(statement, 0); } else { currentElement = currentElement.add(expression, 0); } } } private void buildMoreGenericsCompletionContext(ASTNode node, boolean consumeTypeArguments) { int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if(kind != 0) { int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); nextElement : switch (kind) { case K_BINARY_OPERATOR : int prevKind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1); switch (prevKind) { case K_PARAMETERIZED_ALLOCATION : if(this.invocationType == ALLOCATION || this.invocationType == QUALIFIED_ALLOCATION) { currentElement = currentElement.add((TypeReference)node, 0); } break nextElement; case K_PARAMETERIZED_METHOD_INVOCATION : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 1) == 0) { currentElement = currentElement.add((TypeReference)node, 0); break nextElement; } } if(info == LESS && node instanceof TypeReference) { if(this.identifierLengthPtr > -1 && this.identifierLengthStack[this.identifierLengthPtr]!= 0) { TypeReference ref = this.getTypeReference(0); if(currentElement instanceof RecoveredType) { currentElement = currentElement.add(new CompletionOnFieldType(ref, false), 0); } else { currentElement = currentElement.add(ref, 0); } } else if (currentElement.enclosingMethod().methodDeclaration.isConstructor()) { currentElement = currentElement.add((TypeReference)node, 0); } } break; } } } private void buildMoreTryStatementCompletionContext(TypeReference exceptionRef) { if (this.astLengthPtr > -1 && this.astPtr > 1 && this.astStack[this.astPtr] instanceof Block && this.astStack[this.astPtr - 1] instanceof Argument) { TryStatement tryStatement = new TryStatement(); int newAstPtr = this.astPtr; int length = this.astLengthStack[this.astLengthPtr]; Block[] bks = (tryStatement.catchBlocks = new Block[length + 1]); Argument[] args = (tryStatement.catchArguments = new Argument[length + 1]); if (length != 0) { while (length-- > 0) { bks[length] = (Block) this.astStack[newAstPtr--]; bks[length].statements = null; // statements of catch block won't be used args[length] = (Argument) this.astStack[newAstPtr--]; } } bks[bks.length - 1] = new Block(0); args[args.length - 1] = new Argument(FAKE_ARGUMENT_NAME,0,exceptionRef,0); tryStatement.tryBlock = (Block) this.astStack[newAstPtr--]; assistNodeParent = tryStatement; currentElement.add(tryStatement, 0); } else if (this.astLengthPtr > -1 && this.astPtr > -1 && this.astStack[this.astPtr] instanceof Block) { TryStatement tryStatement = new TryStatement(); int newAstPtr = this.astPtr; Block[] bks = (tryStatement.catchBlocks = new Block[1]); Argument[] args = (tryStatement.catchArguments = new Argument[1]); bks[0] = new Block(0); args[0] = new Argument(FAKE_ARGUMENT_NAME,0,exceptionRef,0); tryStatement.tryBlock = (Block) this.astStack[newAstPtr--]; assistNodeParent = tryStatement; currentElement.add(tryStatement, 0); }else { currentElement = currentElement.add(exceptionRef, 0); } } public int bodyEnd(AbstractMethodDeclaration method){ return cursorLocation; } public int bodyEnd(Initializer initializer){ return cursorLocation; } /** * Checks if the completion is on the exception type of a catch clause. * Returns whether we found a completion node. */ private boolean checkCatchClause() { if ((topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_CATCH_AND_RIGHT_PAREN) && this.identifierPtr > -1) { // NB: if the cursor is on the variable, then it has been reduced (so identifierPtr is -1), // thus this can only be a completion on the type of the catch clause pushOnElementStack(K_NEXT_TYPEREF_IS_EXCEPTION); this.assistNode = getTypeReference(0); popElement(K_NEXT_TYPEREF_IS_EXCEPTION); this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } return false; } /** * Checks if the completion is on the type following a 'new'. * Returns whether we found a completion node. */ private boolean checkClassInstanceCreation() { if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_NEW_AND_LEFT_BRACKET) { int length = identifierLengthStack[identifierLengthPtr]; int numberOfIdentifiers = this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr]; if (length != numberOfIdentifiers || this.genericsLengthStack[this.genericsLengthPtr] != 0) { // no class instance creation with a parameterized type return true; } // completion on type inside an allocation expression TypeReference type; if (this.invocationType == ALLOCATION) { // non qualified allocation expression AllocationExpression allocExpr = new AllocationExpression(); if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_INSIDE_THROW_STATEMENT && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 1) == this.bracketDepth) { pushOnElementStack(K_NEXT_TYPEREF_IS_EXCEPTION); type = getTypeReference(0); popElement(K_NEXT_TYPEREF_IS_EXCEPTION); } else { type = getTypeReference(0); } if(type instanceof CompletionOnSingleTypeReference) { ((CompletionOnSingleTypeReference)type).isConstructorType = true; } allocExpr.type = type; allocExpr.sourceStart = type.sourceStart; allocExpr.sourceEnd = type.sourceEnd; pushOnExpressionStack(allocExpr); this.isOrphanCompletionNode = false; } else { // qualified allocation expression QualifiedAllocationExpression allocExpr = new QualifiedAllocationExpression(); pushOnGenericsIdentifiersLengthStack(identifierLengthStack[identifierLengthPtr]); pushOnGenericsLengthStack(0); if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_INSIDE_THROW_STATEMENT && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 1) == this.bracketDepth) { pushOnElementStack(K_NEXT_TYPEREF_IS_EXCEPTION); type = getTypeReference(0); popElement(K_NEXT_TYPEREF_IS_EXCEPTION); } else { type = getTypeReference(0); } allocExpr.type = type; allocExpr.enclosingInstance = this.expressionStack[this.qualifier]; allocExpr.sourceStart = this.intStack[this.intPtr--]; allocExpr.sourceEnd = type.sourceEnd; this.expressionStack[this.qualifier] = allocExpr; // attach it now (it replaces the qualifier expression) this.isOrphanCompletionNode = false; } this.assistNode = type; this.lastCheckPoint = type.sourceEnd + 1; popElement(K_BETWEEN_NEW_AND_LEFT_BRACKET); return true; } return false; } /** * Checks if the completion is on the dot following an array type, * a primitive type or an primitive array type. * Returns whether we found a completion node. */ private boolean checkClassLiteralAccess() { if (this.identifierLengthPtr >= 1 && this.previousToken == TokenNameDOT) { // (NB: the top id length is 1 and it is for the completion identifier) int length; // if the penultimate id length is negative, // the completion is after a primitive type or a primitive array type if ((length = this.identifierLengthStack[this.identifierLengthPtr-1]) < 0) { // build the primitive type node int dim = this.isAfterArrayType() ? this.intStack[this.intPtr--] : 0; SingleTypeReference typeRef = (SingleTypeReference)TypeReference.baseTypeReference(-length, dim); typeRef.sourceStart = this.intStack[this.intPtr--]; if (dim == 0) { typeRef.sourceEnd = this.intStack[this.intPtr--]; } else { this.intPtr--; typeRef.sourceEnd = this.endPosition; } //typeRef.sourceEnd = typeRef.sourceStart + typeRef.token.length; // NB: It's ok to use the length of the token since it doesn't contain any unicode // find the completion identifier and its source positions char[] source = identifierStack[identifierPtr]; long pos = this.identifierPositionStack[this.identifierPtr--]; this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one) // build the completion on class literal access node CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef); access.completionIdentifier = source; this.identifierLengthPtr--; // pop the length that was used to say it is a primitive type this.assistNode = access; this.isOrphanCompletionNode = true; return true; } // if the completion is after a regular array type if (isAfterArrayType()) { // find the completion identifier and its source positions char[] source = identifierStack[identifierPtr]; long pos = this.identifierPositionStack[this.identifierPtr--]; this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one) // get the type reference pushOnGenericsIdentifiersLengthStack(identifierLengthStack[identifierLengthPtr]); pushOnGenericsLengthStack(0); TypeReference typeRef = getTypeReference(this.intStack[this.intPtr--]); // build the completion on class literal access node CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef); access.completionIdentifier = source; this.assistNode = access; this.isOrphanCompletionNode = true; return true; } } return false; } private boolean checkKeyword() { if (currentElement instanceof RecoveredUnit) { // RecoveredUnit unit = (RecoveredUnit) currentElement; int index = -1; if ((index = this.indexOfAssistIdentifier()) > -1) { int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1; char[] ident = identifierStack[ptr]; long pos = identifierPositionStack[ptr]; char[][] keywords = new char[Keywords.COUNT][]; int count = 0; // if(unit.typeCount == 0 // && lastModifiers == ClassFileConstants.AccDefault) { // keywords[count++] = Keywords.IMPORT; // } // if(unit.typeCount == 0 // && unit.importCount == 0 // && lastModifiers == ClassFileConstants.AccDefault // && compilationUnit.currentPackage == null) { // keywords[count++] = Keywords.PACKAGE; // } // if((lastModifiers & ClassFileConstants.AccPublic) == 0) { // boolean hasNoPublicType = true; // for (int i = 0; i < unit.typeCount; i++) { // if((unit.types[i].typeDeclaration.modifiers & ClassFileConstants.AccPublic) != 0) { // hasNoPublicType = false; // } // } // if(hasNoPublicType) { // keywords[count++] = Keywords.PUBLIC; // } // } // if((lastModifiers & ClassFileConstants.AccAbstract) == 0 // && (lastModifiers & ClassFileConstants.AccFinal) == 0) { // keywords[count++] = Keywords.ABSTRACT; // } // if((lastModifiers & ClassFileConstants.AccAbstract) == 0 // && (lastModifiers & ClassFileConstants.AccFinal) == 0) { // keywords[count++] = Keywords.FINAL; // } // // keywords[count++] = Keywords.CLASS; // // if((lastModifiers & ClassFileConstants.AccFinal) == 0) { // keywords[count++] = Keywords.INTERFACE; // } if(count != 0) { System.arraycopy(keywords, 0, keywords = new char[count][], 0, count); this.assistNode = new CompletionOnKeyword2(ident, pos, keywords); this.lastCheckPoint = assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } } } return false; } private boolean checkInstanceofKeyword() { if(isInsideMethod()) { int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); int index; if(kind != K_BLOCK_DELIMITER && (index = indexOfAssistIdentifier()) > -1 && expressionPtr > -1 && expressionLengthStack[expressionPtr] == 1) { int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1; if(identifierStack[ptr].length > 0 && CharOperation.prefixEquals(identifierStack[ptr], Keywords.INSTANCEOF)) { this.assistNode = new CompletionOnKeyword3( identifierStack[ptr], identifierPositionStack[ptr], Keywords.INSTANCEOF); this.lastCheckPoint = assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } if(identifierStack[ptr].length > 0 && CharOperation.prefixEquals(identifierStack[ptr], Keywords.TYPEOF)) { this.assistNode = new CompletionOnKeyword3( identifierStack[ptr], identifierPositionStack[ptr], Keywords.TYPEOF); this.lastCheckPoint = assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } } } return false; } /** * Checks if the completion is inside a method invocation or a constructor invocation. * Returns whether we found a completion node. */ private boolean checkInvocation() { Expression topExpression = this.expressionPtr >= 0 ? this.expressionStack[this.expressionPtr] : null; boolean isEmptyNameCompletion = false; boolean isEmptyAssistIdentifier = false; if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SELECTOR_QUALIFIER && ((isEmptyNameCompletion = topExpression == this.assistNode && this.isEmptyNameCompletion()) // eg. it is something like "this.fred([cursor]" but it is not something like "this.fred(1 + [cursor]" || (isEmptyAssistIdentifier = this.indexOfAssistIdentifier() >= 0 && this.identifierStack[this.identifierPtr].length == 0))) { // eg. it is something like "this.fred(1 [cursor]" // pop empty name completion if (isEmptyNameCompletion) { this.expressionPtr--; this.expressionLengthStack[this.expressionLengthPtr]--; } else if (isEmptyAssistIdentifier) { this.identifierPtr--; this.identifierLengthPtr--; } // find receiver and qualifier int invocType = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 1); int qualifierExprPtr = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); // find arguments int numArgs = this.expressionPtr - qualifierExprPtr; int argStart = qualifierExprPtr + 1; Expression[] arguments = null; if (numArgs > 0) { // remember the arguments arguments = new Expression[numArgs]; System.arraycopy(this.expressionStack, argStart, arguments, 0, numArgs); // consume the expression arguments this.expressionPtr -= numArgs; int count = numArgs; while (count > 0) { count -= this.expressionLengthStack[this.expressionLengthPtr--]; } } // build ast node if (invocType != ALLOCATION && invocType != QUALIFIED_ALLOCATION) { // creates completion on message send CompletionOnMessageSend messageSend = new CompletionOnMessageSend(); messageSend.arguments = arguments; switch (invocType) { case NO_RECEIVER: // implicit this messageSend.receiver = ThisReference.implicitThis(); break; case NAME_RECEIVER: // remove special flags for primitive types while (this.identifierLengthPtr >= 0 && this.identifierLengthStack[this.identifierLengthPtr] < 0) { this.identifierLengthPtr--; } // remove selector this.identifierPtr--; if(this.genericsPtr > -1 && this.genericsLengthPtr > -1 && this.genericsLengthStack[this.genericsLengthPtr] > 0) { // is inside a paremeterized method: bar.<X>.foo this.identifierLengthPtr--; } else { this.identifierLengthStack[this.identifierLengthPtr]--; } // consume the receiver messageSend.receiver = this.getUnspecifiedReference(); break; case SUPER_RECEIVER: messageSend.receiver = new SuperReference(0, 0); break; case EXPLICIT_RECEIVER: messageSend.receiver = this.expressionStack[qualifierExprPtr]; } // set selector int selectorPtr = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 2); messageSend.selector = this.identifierStack[selectorPtr]; // remove selector if (this.identifierLengthPtr >=0 && this.identifierLengthStack[this.identifierLengthPtr] == 1) { this.identifierPtr--; this.identifierLengthPtr--; } // the entire message may be replaced in case qualification is needed messageSend.sourceStart = (int)(this.identifierPositionStack[selectorPtr] >> 32); //this.cursorLocation + 1; messageSend.sourceEnd = this.cursorLocation; // remember the message send as an orphan completion node this.assistNode = messageSend; this.lastCheckPoint = messageSend.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } else { int selectorPtr = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 2); if (selectorPtr == THIS_CONSTRUCTOR || selectorPtr == SUPER_CONSTRUCTOR) { // creates an explicit constructor call CompletionOnExplicitConstructorCall call = new CompletionOnExplicitConstructorCall(ExplicitConstructorCall.This); call.arguments = arguments; if (invocType == QUALIFIED_ALLOCATION) { call.qualification = this.expressionStack[qualifierExprPtr]; } // no source is going to be replaced call.sourceStart = this.cursorLocation + 1; call.sourceEnd = this.cursorLocation; // remember the explicit constructor call as an orphan completion node this.assistNode = call; this.lastCheckPoint = call.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } else { // creates an allocation expression CompletionOnQualifiedAllocationExpression allocExpr = new CompletionOnQualifiedAllocationExpression(); allocExpr.arguments = arguments; if(this.genericsLengthPtr < 0) { pushOnGenericsLengthStack(0); pushOnGenericsIdentifiersLengthStack(identifierLengthStack[identifierLengthPtr]); } allocExpr.type = super.getTypeReference(0); // we don't want a completion node here, so call super if (invocType == QUALIFIED_ALLOCATION) { allocExpr.enclosingInstance = this.expressionStack[qualifierExprPtr]; } // no source is going to be replaced allocExpr.sourceStart = this.cursorLocation + 1; allocExpr.sourceEnd = this.cursorLocation; // remember the allocation expression as an orphan completion node this.assistNode = allocExpr; this.lastCheckPoint = allocExpr.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } } } return false; } private boolean checkLabelStatement() { if(isInsideMethod() || isInsideFieldInitialization()) { int kind = this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if(kind != K_INSIDE_BREAK_STATEMENT && kind != K_INSIDE_CONTINUE_STATEMENT) return false; if (indexOfAssistIdentifier() != 0) return false; char[][] labels = new char[this.labelPtr + 1][]; int labelCount = 0; int labelKind = kind; int index = 1; while(labelKind != 0 && labelKind != K_METHOD_DELIMITER) { labelKind = this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, index); if(labelKind == K_LABEL) { int ptr = this.topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, index); labels[labelCount++] = this.labelStack[ptr]; } index++; } System.arraycopy(labels, 0, labels = new char[labelCount][], 0, labelCount); long position = this.identifierPositionStack[this.identifierPtr]; CompletionOnBrankStatementLabel statementLabel = new CompletionOnBrankStatementLabel( kind == K_INSIDE_BREAK_STATEMENT ? CompletionOnBrankStatementLabel.BREAK : CompletionOnBrankStatementLabel.CONTINUE, this.identifierStack[this.identifierPtr--], (int) (position >>> 32), (int)position, labels); this.assistNode = statementLabel; this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } return false; } /** * Checks if the completion is on a member access (ie. in an identifier following a dot). * Returns whether we found a completion node. */ private boolean checkMemberAccess() { if (this.previousToken == TokenNameDOT && this.qualifier > -1 && this.expressionPtr == this.qualifier) { if (this.identifierLengthPtr > 1 && this.identifierLengthStack[this.identifierLengthPtr - 1] < 0) { // its not a member access because the receiver is a base type // fix for bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=137623 return false; } // the receiver is an expression pushCompletionOnMemberAccessOnExpressionStack(false); return true; } return false; } /** * Checks if the completion is on a name reference. * Returns whether we found a completion node. */ private boolean checkNameCompletion() { /* We didn't find any other completion, but the completion identifier is on the identifier stack, so it can only be a completion on name. Note that we allow the completion on a name even if nothing is expected (eg. foo() b[cursor] would be a completion on 'b'). This policy gives more to the user than he/she would expect, but this simplifies the problem. To fix this, the recovery must be changed to work at a 'statement' granularity instead of at the 'expression' granularity as it does right now. */ // NB: at this point the completion identifier is on the identifier stack this.assistNode = getUnspecifiedReferenceOptimized(); this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } /** * Checks if the completion is in the context of a method and on the type of one of its arguments * Returns whether we found a completion node. */ private boolean checkRecoveredMethod() { if (currentElement instanceof RecoveredMethod){ /* check if current awaiting identifier is the completion identifier */ if (this.indexOfAssistIdentifier() < 0) return false; /* check if on line with an error already - to avoid completing inside illegal type names e.g. int[<cursor> */ if (lastErrorEndPosition <= cursorLocation+1 && Util.getLineNumber(lastErrorEndPosition, scanner.lineEnds, 0, scanner.linePtr) == Util.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart, scanner.lineEnds, 0, scanner.linePtr)){ return false; } RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement; /* only consider if inside method header */ if (!recoveredMethod.foundOpeningBrace && lastIgnoredToken == -1) { //if (rParenPos < lParenPos){ // inside arguments this.assistNode = this.getTypeReference(0); this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } } return false; } /** * Checks if the completion is in the context of a type and on a type reference in this type. * Persists the identifier into a fake field return type * Returns whether we found a completion node. */ private boolean checkRecoveredType() { if (currentElement instanceof RecoveredType){ /* check if current awaiting identifier is the completion identifier */ if (this.indexOfAssistIdentifier() < 0) return false; /* check if on line with an error already - to avoid completing inside illegal type names e.g. int[<cursor> */ if ((lastErrorEndPosition <= cursorLocation+1) && Util.getLineNumber(lastErrorEndPosition, scanner.lineEnds, 0, scanner.linePtr) == Util.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart, scanner.lineEnds, 0, scanner.linePtr)){ return false; } RecoveredType recoveredType = (RecoveredType)currentElement; /* filter out cases where scanner is still inside type header */ if (recoveredType.foundOpeningBrace) { // complete generics stack if necessary if((this.genericsIdentifiersLengthPtr < 0 && this.identifierPtr > -1) || (this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr] <= this.identifierPtr)) { pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); pushOnGenericsLengthStack(0); // handle type arguments } this.assistNode = this.getTypeReference(0); this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } else { if(recoveredType.typeDeclaration.superclass == null && this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_EXTENDS_KEYWORD) { this.consumeClassOrInterfaceName(); this.pushOnElementStack(K_NEXT_TYPEREF_IS_CLASS); this.assistNode = this.getTypeReference(0); this.popElement(K_NEXT_TYPEREF_IS_CLASS); this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } } } return false; } private void classHeaderExtendsOrImplements(boolean isInterface) { if (currentElement != null && currentToken == TokenNameIdentifier && this.cursorLocation+1 >= scanner.startPosition && this.cursorLocation < scanner.currentPosition){ this.pushIdentifier(); int index = -1; /* check if current awaiting identifier is the completion identifier */ if ((index = this.indexOfAssistIdentifier()) > -1) { int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1; RecoveredType recoveredType = (RecoveredType)currentElement; /* filter out cases where scanner is still inside type header */ if (!recoveredType.foundOpeningBrace) { TypeDeclaration type = recoveredType.typeDeclaration; if(!isInterface) { char[][] keywords = new char[Keywords.COUNT][]; int count = 0; if(type.superclass == null) { keywords[count++] = Keywords.EXTENDS; } keywords[count++] = Keywords.IMPLEMENTS; System.arraycopy(keywords, 0, keywords = new char[count][], 0, count); if(count > 0) { CompletionOnKeyword1 completionOnKeyword = new CompletionOnKeyword1( identifierStack[ptr], identifierPositionStack[ptr], keywords); completionOnKeyword.canCompleteEmptyToken = true; type.superclass = completionOnKeyword; type.superclass.bits |= ASTNode.IsSuperType; this.assistNode = completionOnKeyword; this.lastCheckPoint = completionOnKeyword.sourceEnd + 1; } } else { CompletionOnKeyword1 completionOnKeyword = new CompletionOnKeyword1( identifierStack[ptr], identifierPositionStack[ptr], Keywords.EXTENDS); completionOnKeyword.canCompleteEmptyToken = true; this.assistNode = completionOnKeyword; this.lastCheckPoint = completionOnKeyword.sourceEnd + 1; } } } } } /* * Check whether about to shift beyond the completion token. * If so, depending on the context, a special node might need to be created * and attached to the existing recovered structure so as to be remember in the * resulting parsed structure. */ public void completionIdentifierCheck(){ //if (assistNode != null) return; if (checkKeyword()) return; if (checkRecoveredType()) return; if (checkRecoveredMethod()) return; // if not in a method in non diet mode and if not inside a field initializer, only record references attached to types // if (!(isInsideMethod() && !this.diet) // && !isIndirectlyInsideFieldInitialization() // && !isInsideAttributeValue()) return; /* In some cases, the completion identifier may not have yet been consumed, e.g. int.[cursor] This is because the grammar does not allow any (empty) identifier to follow a base type. We thus have to manually force the identifier to be consumed (that is, pushed). */ if (assistIdentifier() == null && this.currentToken == TokenNameIdentifier) { // Test below copied from CompletionScanner.getCurrentIdentifierSource() if (cursorLocation < this.scanner.startPosition && this.scanner.currentPosition == this.scanner.startPosition){ // fake empty identifier got issued this.pushIdentifier(); } else if (cursorLocation+1 >= this.scanner.startPosition && cursorLocation < this.scanner.currentPosition){ this.pushIdentifier(); } } // check for different scenarii // no need to go further if we found a non empty completion node // (we still need to store labels though) if (this.assistNode != null) { // however inside an invocation, the completion identifier may already have been consumed into an empty name // completion, so this check should be before we check that we are at the cursor location if (!isEmptyNameCompletion() || checkInvocation()) return; } // no need to check further if we are not at the cursor location if (this.indexOfAssistIdentifier() < 0) return; if (checkClassInstanceCreation()) return; if (checkCatchClause()) return; if (checkMemberAccess()) return; if (checkClassLiteralAccess()) return; if (checkInstanceofKeyword()) return; // if the completion was not on an empty name, it can still be inside an invocation (eg. this.fred("abc"[cursor]) // (NB: Put this check before checkNameCompletion() because the selector of the invocation can be on the identifier stack) if (checkInvocation()) return; if (checkLabelStatement()) return; if (checkNameCompletion()) return; } protected void consumeArrayCreationHeader() { // nothing to do } protected void consumeAssignment() { popElement(K_ASSISGNMENT_OPERATOR); super.consumeAssignment(); } protected void consumeAssignmentOperator(int pos) { super.consumeAssignmentOperator(pos); pushOnElementStack(K_ASSISGNMENT_OPERATOR, pos); } protected void consumeBinaryExpression(int op) { super.consumeBinaryExpression(op); popElement(K_BINARY_OPERATOR); if(expressionStack[expressionPtr] instanceof BinaryExpression) { BinaryExpression exp = (BinaryExpression) expressionStack[expressionPtr]; if(assistNode != null && exp.right == assistNode) { assistNodeParent = exp; } } } protected void consumeCaseLabel() { super.consumeCaseLabel(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) != K_SWITCH_LABEL) { pushOnElementStack(K_SWITCH_LABEL); } } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeCompilationUnit() */ protected void consumeCompilationUnit() { this.javadoc = null; checkComment(); if (this.javadoc != null && this.cursorLocation > this.javadoc.sourceStart && this.cursorLocation < this.javadoc.sourceEnd) { // completion is in an orphan javadoc comment => replace compilation unit one to allow completion resolution compilationUnit.javadoc = this.javadoc; // create a fake interface declaration to allow resolution if (this.compilationUnit.types == null) { this.compilationUnit.types = new TypeDeclaration[1]; TypeDeclaration declaration = new TypeDeclaration(compilationUnit.compilationResult); declaration.name = FAKE_TYPE_NAME; declaration.modifiers = ClassFileConstants.AccDefault; this.compilationUnit.types[0] = declaration; } } super.consumeCompilationUnit(); } protected void consumeConditionalExpression(int op) { popElement(K_CONDITIONAL_OPERATOR); super.consumeConditionalExpression(op); } protected void consumeDefaultLabel() { super.consumeDefaultLabel(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SWITCH_LABEL) { popElement(K_SWITCH_LABEL); } pushOnElementStack(K_SWITCH_LABEL, DEFAULT); } protected void consumeDimWithOrWithOutExpr() { // DimWithOrWithOutExpr ::= '[' ']' pushOnExpressionStack(null); } protected void consumeEnterVariable() { identifierPtr--; identifierLengthPtr--; boolean isLocalDeclaration = true;//nestedMethod[nestedType] != 0; int variableIndex = variablesCounter[nestedType]; int extendedDimension = intStack[intPtr + 1]; if(isLocalDeclaration || indexOfAssistIdentifier() < 0 || variableIndex != 0 || extendedDimension != 0) { identifierPtr++; identifierLengthPtr++; super.consumeEnterVariable(); } else { restartRecovery = AssistParser.STOP_AT_CURSOR; // recovery if (currentElement != null) { if(!checkKeyword() && !(currentElement instanceof RecoveredUnit && ((RecoveredUnit)currentElement).statementCount == 0)) { int nameSourceStart = (int)(identifierPositionStack[identifierPtr] >>> 32); intPtr--; // pushOnGenericsIdentifiersLengthStack(identifierLengthStack[identifierLengthPtr]); // pushOnGenericsLengthStack(0); TypeReference type = getTypeReference(intStack[intPtr--]); intPtr--; if (!(currentElement instanceof RecoveredType) && (currentToken == TokenNameDOT || (Util.getLineNumber(type.sourceStart, scanner.lineEnds, 0, scanner.linePtr) != Util.getLineNumber(nameSourceStart, scanner.lineEnds, 0, scanner.linePtr)))){ lastCheckPoint = nameSourceStart; restartRecovery = true; return; } FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false); completionFieldDecl.modifiers = intStack[intPtr--]; assistNode = completionFieldDecl; lastCheckPoint = type.sourceEnd + 1; currentElement = currentElement.add(completionFieldDecl, 0); lastIgnoredToken = -1; } } } } protected void consumeEqualityExpression(int op) { super.consumeEqualityExpression(op); popElement(K_BINARY_OPERATOR); BinaryExpression exp = (BinaryExpression) expressionStack[expressionPtr]; if(assistNode != null && exp.right == assistNode) { assistNodeParent = exp; } } protected void consumeExitVariableWithInitialization() { super.consumeExitVariableWithInitialization(); // does not keep the initialization if completion is not inside AbstractVariableDeclaration variable = (AbstractVariableDeclaration) astStack[astPtr]; if (cursorLocation + 1 < variable.initialization.sourceStart || cursorLocation > variable.initialization.sourceEnd) { if (STOP_AT_CURSOR) variable.initialization = null; } else if (assistNode != null && assistNode == variable.initialization) { assistNodeParent = variable; } } protected void consumeCallExpressionWithSimpleName() { this.invocationType = NO_RECEIVER; this.qualifier = -1; if (this.indexOfAssistIdentifier() < 0) { super.consumeCallExpressionWithSimpleName(); } else { this.pushCompletionOnMemberAccessOnExpressionStack(false); } } protected void consumeMemberExpressionWithSimpleName() { this.invocationType = NO_RECEIVER; this.qualifier = -1; if (this.indexOfAssistIdentifier() < 0) { super.consumeMemberExpressionWithSimpleName(); } else { this.pushCompletionOnMemberAccessOnExpressionStack(false); } } protected void consumeForceNoDiet() { super.consumeForceNoDiet(); if (isInsideMethod()) { pushOnElementStack(K_LOCAL_INITIALIZER_DELIMITER); } } protected void consumeFormalParameter(boolean isVarArgs) { if (this.indexOfAssistIdentifier() < 0) { super.consumeFormalParameter(isVarArgs); } else { identifierLengthPtr--; char[] identifierName = identifierStack[identifierPtr]; long namePositions = identifierPositionStack[identifierPtr--]; // int extendedDimensions = this.intStack[this.intPtr--]; // int endOfEllipsis = 0; // if (isVarArgs) { // endOfEllipsis = this.intStack[this.intPtr--]; // } // int firstDimensions = this.intStack[this.intPtr--]; // final int typeDimensions = firstDimensions + extendedDimensions; // TypeReference type = getTypeReference(typeDimensions); // if (isVarArgs) { // type = copyDims(type, typeDimensions + 1); // if (extendedDimensions == 0) { // type.sourceEnd = endOfEllipsis; // } // type.bits |= ASTNode.IsVarArgs; // set isVarArgs // } intPtr -= 2; CompletionOnArgumentName arg = new CompletionOnArgumentName( identifierName, namePositions, null, intStack[intPtr + 1] & ~ClassFileConstants.AccDeprecated); // modifiers // consume annotations // int length; // if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { // System.arraycopy( // this.expressionStack, // (this.expressionPtr -= length) + 1, // arg.annotations = new Annotation[length], // 0, // length); // } arg.isCatchArgument = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_CATCH_AND_RIGHT_PAREN; pushOnAstStack(arg); assistNode = arg; this.lastCheckPoint = (int) namePositions; isOrphanCompletionNode = true; /* if incomplete method header, listLength counter will not have been reset, indicating that some arguments are available on the stack */ listLength++; } } protected void consumeInsideCastExpression() { int end = intStack[intPtr--]; boolean isParameterized =(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_PARAMETERIZED_CAST); if(isParameterized) { popElement(K_PARAMETERIZED_CAST); if(this.identifierLengthStack[this.identifierLengthPtr] > 0) { pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); } } else { if(this.identifierLengthStack[this.identifierLengthPtr] > 0) { pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); pushOnGenericsLengthStack(0); } } Expression castType = getTypeReference(intStack[intPtr--]); if(isParameterized) { intPtr--; } castType.sourceEnd = end - 1; castType.sourceStart = intStack[intPtr--] + 1; pushOnExpressionStack(castType); pushOnElementStack(K_CAST_STATEMENT); } protected void consumeCallExpressionWithArguments() { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.consumeCallExpressionWithArguments(); } protected void consumeMethodHeaderName(boolean isAnnotationMethod) { if(this.indexOfAssistIdentifier() < 0) { identifierPtr--; identifierLengthPtr--; if(this.indexOfAssistIdentifier() != 0 || this.identifierLengthStack[this.identifierLengthPtr] != this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr]) { identifierPtr++; identifierLengthPtr++; super.consumeMethodHeaderName(isAnnotationMethod); } else { restartRecovery = AssistParser.STOP_AT_CURSOR; // recovery if (currentElement != null) { //name // char[] selector = identifierStack[identifierPtr + 1]; // long selectorSource = identifierPositionStack[identifierPtr + 1]; // //type // TypeReference type = getTypeReference(intStack[intPtr--]); // ((CompletionOnSingleTypeReference)type).isCompletionNode = false; // //modifiers // int declarationSourceStart = intStack[intPtr--]; // int mod = intStack[intPtr--]; // if(Util.getLineNumber(type.sourceStart, scanner.lineEnds, 0, scanner.linePtr) // != Util.getLineNumber((int) (selectorSource >>> 32), scanner.lineEnds, 0, scanner.linePtr)) { // FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false); // // consume annotations // int length; // if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { // System.arraycopy( // this.expressionStack, // (this.expressionPtr -= length) + 1, // completionFieldDecl.annotations = new Annotation[length], // 0, // length); // } // completionFieldDecl.modifiers = mod; // assistNode = completionFieldDecl; // lastCheckPoint = type.sourceEnd + 1; // currentElement = currentElement.add(completionFieldDecl, 0); // lastIgnoredToken = -1; // } else { // CompletionOnMethodReturnType md = new CompletionOnMethodReturnType(type, this.compilationUnit.compilationResult); // // consume annotations // int length; // if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { // System.arraycopy( // this.expressionStack, // (this.expressionPtr -= length) + 1, // md.annotations = new Annotation[length], // 0, // length); // } // md.selector = selector; // md.declarationSourceStart = declarationSourceStart; // md.modifiers = mod; // md.bodyStart = lParenPos+1; // listLength = 0; // initialize listLength before reading parameters/throws // assistNode = md; // this.lastCheckPoint = md.bodyStart; // currentElement = currentElement.add(md, 0); // lastIgnoredToken = -1; // // javadoc // md.javadoc = this.javadoc; // this.javadoc = null; // } } } } else { // MethodHeaderName ::= Modifiersopt Type 'Identifier' '(' CompletionOnMethodName md = new CompletionOnMethodName(this.compilationUnit.compilationResult); //name md.selector = identifierStack[identifierPtr]; long selectorSource = identifierPositionStack[identifierPtr--]; identifierLengthPtr--; //type md.returnType = getTypeReference(intStack[intPtr--]); //modifiers md.declarationSourceStart = intStack[intPtr--]; md.modifiers = intStack[intPtr--]; this.expressionLengthPtr--; // javadoc md.javadoc = this.javadoc; this.javadoc = null; //highlight starts at selector start md.sourceStart = (int) (selectorSource >>> 32); md.selectorEnd = (int) selectorSource; pushOnAstStack(md); md.sourceEnd = lParenPos; md.bodyStart = lParenPos+1; listLength = 0; // initialize listLength before reading parameters/throws this.assistNode = md; this.lastCheckPoint = md.sourceEnd; // recovery if (currentElement != null){ if (currentElement instanceof RecoveredType //|| md.modifiers != 0 || (Util.getLineNumber(md.returnType.sourceStart, scanner.lineEnds, 0, scanner.linePtr) == Util.getLineNumber(md.sourceStart, scanner.lineEnds, 0, scanner.linePtr))){ lastCheckPoint = md.bodyStart; currentElement = currentElement.add(md, 0); lastIgnoredToken = -1; } else { lastCheckPoint = md.sourceStart; restartRecovery = AssistParser.STOP_AT_CURSOR; } } } } protected void consumeMethodHeaderRightParen() { super.consumeMethodHeaderRightParen(); if (currentElement != null && currentToken == TokenNameIdentifier && this.cursorLocation+1 >= scanner.startPosition && this.cursorLocation < scanner.currentPosition){ this.pushIdentifier(); int index = -1; /* check if current awaiting identifier is the completion identifier */ if ((index = this.indexOfAssistIdentifier()) > -1) { int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1; if (currentElement instanceof RecoveredMethod){ RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement; /* filter out cases where scanner is still inside type header */ if (!recoveredMethod.foundOpeningBrace) { CompletionOnKeyword1 completionOnKeyword = new CompletionOnKeyword1( identifierStack[ptr], identifierPositionStack[ptr], Keywords.THROWS); recoveredMethod.foundOpeningBrace = true; this.assistNode = completionOnKeyword; this.lastCheckPoint = completionOnKeyword.sourceEnd + 1; } } } } } protected void consumeLabel() { super.consumeLabel(); this.pushOnLabelStack(this.identifierStack[this.identifierPtr]); this.pushOnElementStack(K_LABEL, this.labelPtr); } protected void consumeMethodBody() { popElement(K_BLOCK_DELIMITER); super.consumeMethodBody(); } protected void consumeMethodHeader() { super.consumeMethodHeader(); pushOnElementStack(K_BLOCK_DELIMITER); } protected void consumeRestoreDiet() { super.consumeRestoreDiet(); if (isInsideMethod()) { popElement(K_LOCAL_INITIALIZER_DELIMITER); } } protected void consumeStatementBreakWithLabel() { super.consumeStatementBreakWithLabel(); if (this.record) { ASTNode breakStatement = this.astStack[this.astPtr]; if (!isAlreadyPotentialName(breakStatement.sourceStart)) { this.addPotentialName(null, breakStatement.sourceStart, breakStatement.sourceEnd); } } } protected void consumeStatementLabel() { this.popElement(K_LABEL); super.consumeStatementLabel(); } protected void consumeStatementSwitch() { super.consumeStatementSwitch(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SWITCH_LABEL) { popElement(K_SWITCH_LABEL); popElement(K_BLOCK_DELIMITER); } } protected void consumeNestedMethod() { super.consumeNestedMethod(); if(!(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BLOCK_DELIMITER)) pushOnElementStack(K_BLOCK_DELIMITER); } protected void consumePushPosition() { super.consumePushPosition(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BINARY_OPERATOR) { int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); popElement(K_BINARY_OPERATOR); pushOnElementStack(K_UNARY_OPERATOR, info); } } protected void consumeToken(int token) { if(isFirst) { super.consumeToken(token); return; } if(canBeExplicitConstructor == NEXTTOKEN) { canBeExplicitConstructor = YES; } else { canBeExplicitConstructor = NO; } int previous = this.previousToken; int prevIdentifierPtr = this.previousIdentifierPtr; if (isInsideMethod() || isInsideFieldInitialization() || isInsideAnnotation()) { switch(token) { case TokenNameLPAREN: if(previous == TokenNameIdentifier && topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_PARAMETERIZED_METHOD_INVOCATION) { popElement(K_PARAMETERIZED_METHOD_INVOCATION); } else { popElement(K_BETWEEN_NEW_AND_LEFT_BRACKET); } break; case TokenNameLBRACE: popElement(K_BETWEEN_NEW_AND_LEFT_BRACKET); break; case TokenNameLBRACKET: if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_NEW_AND_LEFT_BRACKET) { popElement(K_BETWEEN_NEW_AND_LEFT_BRACKET); pushOnElementStack(K_ARRAY_CREATION); } break; case TokenNameRBRACE: int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); switch (kind) { case K_BLOCK_DELIMITER: popElement(K_BLOCK_DELIMITER); break; case K_MEMBER_VALUE_ARRAY_INITIALIZER: popElement(K_MEMBER_VALUE_ARRAY_INITIALIZER); break; default: popElement(K_ARRAY_INITIALIZER); break; } break; case TokenNameRBRACKET: if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_LEFT_AND_RIGHT_BRACKET) { popElement(K_BETWEEN_LEFT_AND_RIGHT_BRACKET); } break; } } super.consumeToken(token); // if in field initializer (directly or not), on the completion identifier and not in recovery mode yet // then position end of file at cursor location (so that we have the same behavior as // in method bodies) if (token == TokenNameIdentifier && this.identifierStack[this.identifierPtr] == assistIdentifier() && this.currentElement == null && this.isIndirectlyInsideFieldInitialization()) { // this.scanner.eofPosition = cursorLocation < Integer.MAX_VALUE ? cursorLocation+1 : cursorLocation; } // if in a method or if in a field initializer if (isInsideMethod() || isInsideFieldInitialization() || isInsideAttributeValue()) { switch (token) { case TokenNameDOT: switch (previous) { case TokenNamethis: // eg. this[.]fred() this.invocationType = EXPLICIT_RECEIVER; break; case TokenNamesuper: // eg. super[.]fred() this.invocationType = SUPER_RECEIVER; break; case TokenNameIdentifier: // eg. bar[.]fred() if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) != K_BETWEEN_NEW_AND_LEFT_BRACKET) { if (this.identifierPtr != prevIdentifierPtr) { // if identifier has been consumed, eg. this.x[.]fred() this.invocationType = EXPLICIT_RECEIVER; } else { this.invocationType = NAME_RECEIVER; } } break; } break; case TokenNameIdentifier: if (previous == TokenNameDOT) { // eg. foo().[fred]() if (this.invocationType != SUPER_RECEIVER // eg. not super.[fred]() && this.invocationType != NAME_RECEIVER // eg. not bar.[fred]() && this.invocationType != ALLOCATION // eg. not new foo.[Bar]() && this.invocationType != QUALIFIED_ALLOCATION) { // eg. not fred().new foo.[Bar]() this.invocationType = EXPLICIT_RECEIVER; this.qualifier = this.expressionPtr; } } break; case TokenNamenew: pushOnElementStack(K_BETWEEN_NEW_AND_LEFT_BRACKET); this.qualifier = this.expressionPtr; // NB: even if there is no qualification, set it to the expression ptr so that the number of arguments are correctly computed if (previous == TokenNameDOT) { // eg. fred().[new] X() this.invocationType = QUALIFIED_ALLOCATION; } else { // eg. [new] X() this.invocationType = ALLOCATION; } break; case TokenNamethis: if (previous == TokenNameDOT) { // eg. fred().[this]() this.invocationType = QUALIFIED_ALLOCATION; this.qualifier = this.expressionPtr; } break; case TokenNamesuper: if (previous == TokenNameDOT) { // eg. fred().[super]() this.invocationType = QUALIFIED_ALLOCATION; this.qualifier = this.expressionPtr; } break; case TokenNamecatch: pushOnElementStack(K_BETWEEN_CATCH_AND_RIGHT_PAREN); break; case TokenNameLPAREN: if (this.invocationType == NO_RECEIVER || this.invocationType == NAME_RECEIVER || this.invocationType == SUPER_RECEIVER) { this.qualifier = this.expressionPtr; // remenber the last expression so that arguments are correctly computed } switch (previous) { case TokenNameIdentifier: // eg. fred[(]) or foo.fred[(]) if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SELECTOR) { int info = 0; if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER,1) == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN && (info=topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER,1) & LPAREN_NOT_CONSUMED) != 0) { this.popElement(K_SELECTOR); this.popElement(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN); if ((info & ANNOTATION_NAME_COMPLETION) != 0) { this.pushOnElementStack(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN, LPAREN_CONSUMED | ANNOTATION_NAME_COMPLETION); } else { this.pushOnElementStack(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN, LPAREN_CONSUMED); } } else { this.pushOnElementStack(K_SELECTOR_INVOCATION_TYPE, this.invocationType); int selectorQualifier=(this.invocationType==ALLOCATION)?this.expressionPtr:this.qualifier; this.pushOnElementStack(K_SELECTOR_QUALIFIER, selectorQualifier); } } this.qualifier = -1; this.invocationType = NO_RECEIVER; break; case TokenNamethis: // explicit constructor invocation, eg. this[(]1, 2) if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SELECTOR) { this.pushOnElementStack(K_SELECTOR_INVOCATION_TYPE, (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION); this.pushOnElementStack(K_SELECTOR_QUALIFIER, this.qualifier); } this.qualifier = -1; this.invocationType = NO_RECEIVER; break; case TokenNamesuper: // explicit constructor invocation, eg. super[(]1, 2) if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SELECTOR) { this.pushOnElementStack(K_SELECTOR_INVOCATION_TYPE, (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION); this.pushOnElementStack(K_SELECTOR_QUALIFIER, this.qualifier); } this.qualifier = -1; this.invocationType = NO_RECEIVER; break; case TokenNameGREATER: // explicit constructor invocation, eg. Fred<X>[(]1, 2) case TokenNameRIGHT_SHIFT: // or fred<X<X>>[(]1, 2) case TokenNameUNSIGNED_RIGHT_SHIFT: //or Fred<X<X<X>>>[(]1, 2) if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SELECTOR) { if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_BINARY_OPERATOR && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 1) == GREATER) { // it's not a selector invocation popElement(K_SELECTOR); } else { this.pushOnElementStack(K_SELECTOR_INVOCATION_TYPE, (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION); this.pushOnElementStack(K_SELECTOR_QUALIFIER, this.qualifier); } } this.qualifier = -1; this.invocationType = NO_RECEIVER; break; } break; case TokenNameLBRACE: this.bracketDepth++; int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if(kind == K_FIELD_INITIALIZER_DELIMITER || kind == K_LOCAL_INITIALIZER_DELIMITER || kind == K_ARRAY_CREATION) { pushOnElementStack(K_ARRAY_INITIALIZER, endPosition); } else if (kind == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN) { pushOnElementStack(K_MEMBER_VALUE_ARRAY_INITIALIZER, endPosition); } else { switch(previous) { case TokenNameRPAREN : switch(previousKind) { case K_BETWEEN_IF_AND_RIGHT_PAREN : pushOnElementStack(K_BLOCK_DELIMITER, IF); break; case K_BETWEEN_CATCH_AND_RIGHT_PAREN : pushOnElementStack(K_BLOCK_DELIMITER, CATCH); break; case K_BETWEEN_WHILE_AND_RIGHT_PAREN : pushOnElementStack(K_BLOCK_DELIMITER, WHILE); break; case K_BETWEEN_SWITCH_AND_RIGHT_PAREN : pushOnElementStack(K_BLOCK_DELIMITER, SWITCH); break; case K_BETWEEN_FOR_AND_RIGHT_PAREN : pushOnElementStack(K_BLOCK_DELIMITER, FOR); break; case K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN : pushOnElementStack(K_BLOCK_DELIMITER, SYNCHRONIZED); break; default : pushOnElementStack(K_BLOCK_DELIMITER); break; } break; case TokenNametry : pushOnElementStack(K_BLOCK_DELIMITER, TRY); break; case TokenNamedo: pushOnElementStack(K_BLOCK_DELIMITER, DO); break; default : pushOnElementStack(K_BLOCK_DELIMITER); break; } } break; case TokenNameLBRACKET: if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) != K_ARRAY_CREATION) { pushOnElementStack(K_BETWEEN_LEFT_AND_RIGHT_BRACKET); } else { if(previous == TokenNameIdentifier) { invocationType = NO_RECEIVER; qualifier = -1; } } this.bracketDepth++; break; case TokenNameRBRACE: this.bracketDepth--; break; case TokenNameRBRACKET: this.bracketDepth--; break; case TokenNameRPAREN: switch(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) { case K_BETWEEN_CATCH_AND_RIGHT_PAREN : popElement(K_BETWEEN_CATCH_AND_RIGHT_PAREN); break; case K_BETWEEN_IF_AND_RIGHT_PAREN : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == bracketDepth) { popElement(K_BETWEEN_IF_AND_RIGHT_PAREN); } break; case K_BETWEEN_WHILE_AND_RIGHT_PAREN : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == bracketDepth) { popElement(K_BETWEEN_WHILE_AND_RIGHT_PAREN); } break; case K_BETWEEN_FOR_AND_RIGHT_PAREN : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == bracketDepth) { popElement(K_BETWEEN_FOR_AND_RIGHT_PAREN); } break; case K_BETWEEN_SWITCH_AND_RIGHT_PAREN : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == bracketDepth) { popElement(K_BETWEEN_SWITCH_AND_RIGHT_PAREN); } break; case K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == bracketDepth) { popElement(K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN); } break; } break; case TokenNamethrow: pushOnElementStack(K_INSIDE_THROW_STATEMENT, bracketDepth); break; case TokenNameSEMICOLON: switch(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) { case K_INSIDE_THROW_STATEMENT : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_THROW_STATEMENT); } break; case K_INSIDE_RETURN_STATEMENT : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_RETURN_STATEMENT); } break; case K_INSIDE_ASSERT_STATEMENT : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_ASSERT_STATEMENT); } break; case K_INSIDE_BREAK_STATEMENT: if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_BREAK_STATEMENT); } break; case K_INSIDE_CONTINUE_STATEMENT: if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_CONTINUE_STATEMENT); } break; } break; case TokenNamereturn: pushOnElementStack(K_INSIDE_RETURN_STATEMENT, this.bracketDepth); break; case TokenNameMULTIPLY: pushOnElementStack(K_BINARY_OPERATOR, MULTIPLY); break; case TokenNameDIVIDE: pushOnElementStack(K_BINARY_OPERATOR, DIVIDE); break; case TokenNameREMAINDER: pushOnElementStack(K_BINARY_OPERATOR, REMAINDER); break; case TokenNamePLUS: pushOnElementStack(K_BINARY_OPERATOR, PLUS); break; case TokenNameMINUS: pushOnElementStack(K_BINARY_OPERATOR, MINUS); break; case TokenNameLEFT_SHIFT: pushOnElementStack(K_BINARY_OPERATOR, LEFT_SHIFT); break; case TokenNameRIGHT_SHIFT: pushOnElementStack(K_BINARY_OPERATOR, RIGHT_SHIFT); break; case TokenNameUNSIGNED_RIGHT_SHIFT: pushOnElementStack(K_BINARY_OPERATOR, UNSIGNED_RIGHT_SHIFT); break; case TokenNameLESS: switch(previous) { case TokenNameDOT : pushOnElementStack(K_PARAMETERIZED_METHOD_INVOCATION); break; case TokenNamenew : pushOnElementStack(K_PARAMETERIZED_ALLOCATION); break; } pushOnElementStack(K_BINARY_OPERATOR, LESS); break; case TokenNameGREATER: pushOnElementStack(K_BINARY_OPERATOR, GREATER); break; case TokenNameLESS_EQUAL: pushOnElementStack(K_BINARY_OPERATOR, LESS_EQUAL); break; case TokenNameGREATER_EQUAL: pushOnElementStack(K_BINARY_OPERATOR, GREATER_EQUAL); break; case TokenNameAND: pushOnElementStack(K_BINARY_OPERATOR, AND); break; case TokenNameXOR: pushOnElementStack(K_BINARY_OPERATOR, XOR); break; case TokenNameOR: pushOnElementStack(K_BINARY_OPERATOR, OR); break; case TokenNameAND_AND: pushOnElementStack(K_BINARY_OPERATOR, AND_AND); break; case TokenNameOR_OR: pushOnElementStack(K_BINARY_OPERATOR, OR_OR); break; case TokenNamePLUS_PLUS: pushOnElementStack(K_UNARY_OPERATOR, PLUS_PLUS); break; case TokenNameMINUS_MINUS: pushOnElementStack(K_UNARY_OPERATOR, MINUS_MINUS); break; case TokenNameTWIDDLE: pushOnElementStack(K_UNARY_OPERATOR, TWIDDLE); break; case TokenNameNOT: pushOnElementStack(K_UNARY_OPERATOR, NOT); break; case TokenNameEQUAL_EQUAL: pushOnElementStack(K_BINARY_OPERATOR, EQUAL_EQUAL); break; case TokenNameNOT_EQUAL: pushOnElementStack(K_BINARY_OPERATOR, NOT_EQUAL); break; case TokenNameinstanceof: pushOnElementStack(K_BINARY_OPERATOR, INSTANCEOF); break; case TokenNameQUESTION: if(previous != TokenNameLESS) { pushOnElementStack(K_CONDITIONAL_OPERATOR, QUESTION); } break; case TokenNameCOLON: if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_CONDITIONAL_OPERATOR && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == QUESTION) { popElement(K_CONDITIONAL_OPERATOR); pushOnElementStack(K_CONDITIONAL_OPERATOR, COLON); } else { if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_CASE_AND_COLON) { popElement(K_BETWEEN_CASE_AND_COLON); } else { popElement(K_BETWEEN_DEFAULT_AND_COLON); } } break; case TokenNameif: pushOnElementStack(K_BETWEEN_IF_AND_RIGHT_PAREN, bracketDepth); break; case TokenNamewhile: pushOnElementStack(K_BETWEEN_WHILE_AND_RIGHT_PAREN, bracketDepth); break; case TokenNamefor: pushOnElementStack(K_BETWEEN_FOR_AND_RIGHT_PAREN, bracketDepth); break; case TokenNameswitch: pushOnElementStack(K_BETWEEN_SWITCH_AND_RIGHT_PAREN, bracketDepth); break; case TokenNamesynchronized: pushOnElementStack(K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN, bracketDepth); break; // case TokenNameassert: // pushOnElementStack(K_INSIDE_ASSERT_STATEMENT, this.bracketDepth); // break; case TokenNamecase : pushOnElementStack(K_BETWEEN_CASE_AND_COLON); break; case TokenNamedefault : pushOnElementStack(K_BETWEEN_DEFAULT_AND_COLON); break; case TokenNameextends: pushOnElementStack(K_EXTENDS_KEYWORD); break; case TokenNamebreak: pushOnElementStack(K_INSIDE_BREAK_STATEMENT, bracketDepth); break; case TokenNamecontinue: pushOnElementStack(K_INSIDE_CONTINUE_STATEMENT, bracketDepth); break; } } else { switch(token) { case TokenNameextends: pushOnElementStack(K_EXTENDS_KEYWORD); break; case TokenNameLESS: pushOnElementStack(K_BINARY_OPERATOR, LESS); break; case TokenNameGREATER: pushOnElementStack(K_BINARY_OPERATOR, GREATER); break; case TokenNameRIGHT_SHIFT: pushOnElementStack(K_BINARY_OPERATOR, RIGHT_SHIFT); break; case TokenNameUNSIGNED_RIGHT_SHIFT: pushOnElementStack(K_BINARY_OPERATOR, UNSIGNED_RIGHT_SHIFT); break; } } } protected void consumeRightParen() { super.consumeRightParen(); } protected void consumeUnaryExpression(int op) { super.consumeUnaryExpression(op); popElement(K_UNARY_OPERATOR); if(expressionStack[expressionPtr] instanceof UnaryExpression) { UnaryExpression exp = (UnaryExpression) expressionStack[expressionPtr]; if(assistNode != null && exp.expression == assistNode) { assistNodeParent = exp; } } } protected void consumeUnaryExpression(int op, boolean post) { super.consumeUnaryExpression(op, post); popElement(K_UNARY_OPERATOR); if(expressionStack[expressionPtr] instanceof UnaryExpression) { UnaryExpression exp = (UnaryExpression) expressionStack[expressionPtr]; if(assistNode != null && exp.expression == assistNode) { assistNodeParent = exp; } } } public ImportReference createAssistImportReference(char[][] tokens, long[] positions){ return new CompletionOnImportReference(tokens, positions); } public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] assistName, long[] positions){ return new CompletionOnQualifiedNameReference( previousIdentifiers, assistName, positions, isInsideAttributeValue()); } public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] assistName, long[] positions){ switch (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) { case K_NEXT_TYPEREF_IS_EXCEPTION : return new CompletionOnQualifiedTypeReference( previousIdentifiers, assistName, positions, CompletionOnQualifiedTypeReference.K_EXCEPTION); case K_NEXT_TYPEREF_IS_CLASS : return new CompletionOnQualifiedTypeReference( previousIdentifiers, assistName, positions, CompletionOnQualifiedTypeReference.K_CLASS); case K_NEXT_TYPEREF_IS_INTERFACE : return new CompletionOnQualifiedTypeReference( previousIdentifiers, assistName, positions, CompletionOnQualifiedTypeReference.K_INTERFACE); default : return new CompletionOnQualifiedTypeReference( previousIdentifiers, assistName, positions); } } public NameReference createSingleAssistNameReference(char[] assistName, long position) { int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if(false){//!isInsideMethod()) { if (isInsideFieldInitialization()) { return new CompletionOnSingleNameReference( assistName, position, new char[][]{Keywords.FALSE, Keywords.TRUE}, false, isInsideAttributeValue()); } return new CompletionOnSingleNameReference(assistName, position, isInsideAttributeValue()); } else { boolean canBeExplicitConstructorCall = false; if(kind == K_BLOCK_DELIMITER && previousKind == K_BLOCK_DELIMITER && previousInfo == DO) { return new CompletionOnKeyword3(assistName, position, Keywords.WHILE); } else if(kind == K_BLOCK_DELIMITER && previousKind == K_BLOCK_DELIMITER && previousInfo == TRY) { return new CompletionOnKeyword3(assistName, position, new char[][]{Keywords.CATCH, Keywords.FINALLY}); } else if(kind == K_BLOCK_DELIMITER && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == SWITCH) { return new CompletionOnKeyword3(assistName, position, new char[][]{Keywords.CASE, Keywords.DEFAULT}); } else { char[][] keywords = new char[Keywords.COUNT][]; int count = 0; // if((lastModifiers & ClassFileConstants.AccStatic) == 0) { // keywords[count++]= Keywords.SUPER; keywords[count++]= Keywords.THIS; // } keywords[count++]= Keywords.NEW; if(kind == K_BLOCK_DELIMITER || kind==0) { if(canBeExplicitConstructor == YES) { canBeExplicitConstructorCall = true; } // keywords[count++]= Keywords.ASSERT; keywords[count++]= Keywords.DO; keywords[count++]= Keywords.FOR; keywords[count++]= Keywords.IF; keywords[count++]= Keywords.RETURN; keywords[count++]= Keywords.SWITCH; // keywords[count++]= Keywords.SYNCHRONIZED; keywords[count++]= Keywords.THROW; keywords[count++]= Keywords.TRY; keywords[count++]= Keywords.WHILE; keywords[count++]= Keywords.VAR; keywords[count++]= Keywords.FUNCTION; keywords[count++]= Keywords.DELETE; keywords[count++]= Keywords.TYPEOF; // keywords[count++]= Keywords.FINAL; // keywords[count++]= Keywords.CLASS; if(previousKind == K_BLOCK_DELIMITER) { switch (previousInfo) { case IF : keywords[count++]= Keywords.ELSE; break; case CATCH : keywords[count++]= Keywords.CATCH; keywords[count++]= Keywords.FINALLY; break; } } if(isInsideLoop()) { keywords[count++]= Keywords.CONTINUE; } if(isInsideBreakable()) { keywords[count++]= Keywords.BREAK; } } else if(kind != K_BETWEEN_CASE_AND_COLON && kind != K_BETWEEN_DEFAULT_AND_COLON) { keywords[count++]= Keywords.TRUE; keywords[count++]= Keywords.FALSE; keywords[count++]= Keywords.NULL; keywords[count++]= Keywords.UNDEFINED; keywords[count++]= Keywords.FUNCTION; if(kind == K_SWITCH_LABEL) { if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) != DEFAULT) { keywords[count++]= Keywords.DEFAULT; } keywords[count++]= Keywords.BREAK; keywords[count++]= Keywords.CASE; keywords[count++]= Keywords.DO; keywords[count++]= Keywords.FOR; keywords[count++]= Keywords.IF; keywords[count++]= Keywords.RETURN; keywords[count++]= Keywords.SWITCH; // keywords[count++]= Keywords.SYNCHRONIZED; keywords[count++]= Keywords.THROW; keywords[count++]= Keywords.TRY; keywords[count++]= Keywords.WHILE; keywords[count++]= Keywords.VAR; keywords[count++]= Keywords.FUNCTION; keywords[count++]= Keywords.DELETE; keywords[count++]= Keywords.TYPEOF; if(isInsideLoop()) { keywords[count++]= Keywords.CONTINUE; } } } System.arraycopy(keywords, 0 , keywords = new char[count][], 0, count); return new CompletionOnSingleNameReference(assistName, position, keywords, canBeExplicitConstructorCall, isInsideAttributeValue()); } } } public TypeReference createSingleAssistTypeReference(char[] assistName, long position) { switch (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) { case K_NEXT_TYPEREF_IS_EXCEPTION : return new CompletionOnSingleTypeReference(assistName, position, CompletionOnSingleTypeReference.K_EXCEPTION) ; case K_NEXT_TYPEREF_IS_CLASS : return new CompletionOnSingleTypeReference(assistName, position, CompletionOnSingleTypeReference.K_CLASS); case K_NEXT_TYPEREF_IS_INTERFACE : return new CompletionOnSingleTypeReference(assistName, position, CompletionOnSingleTypeReference.K_INTERFACE); default : return new CompletionOnSingleTypeReference(assistName, position); } } public TypeReference createParameterizedSingleAssistTypeReference(TypeReference[] typeArguments, char[] assistName, long position) { return this.createSingleAssistTypeReference(assistName, position); } protected StringLiteral createStringLiteral(char[] token, int start, int end, int lineNumber) { if (start <= this.cursorLocation && this.cursorLocation <= end){ char[] source = this.scanner.source; int contentStart = start; int contentEnd = end; // " could be as unicode \u0022 int pos = contentStart; if(source[pos] == '\"') { contentStart = pos + 1; } else if(source[pos] == '\\' && source[pos+1] == 'u') { pos += 2; while (source[pos] == 'u') { pos++; } if(source[pos] == 0 && source[pos + 1] == 0 && source[pos + 2] == 2 && source[pos + 3] == 2) { contentStart = pos + 4; } } pos = contentEnd; if(source[pos] == '\"') { contentEnd = pos - 1; } else if(source.length > 5 && source[pos-4] == 'u') { if(source[pos - 3] == 0 && source[pos - 2] == 0 && source[pos - 1] == 2 && source[pos] == 2) { pos -= 5; while (pos > -1 && source[pos] == 'u') { pos--; } if(pos > -1 && source[pos] == '\\') { contentEnd = pos - 1; } } } if(contentEnd < start) { contentEnd = end; } if(this.cursorLocation != end || end == contentEnd) { CompletionOnStringLiteral stringLiteral = new CompletionOnStringLiteral( token, start, end, contentStart, contentEnd, lineNumber); this.assistNode = stringLiteral; this.restartRecovery = AssistParser.STOP_AT_CURSOR; this.lastCheckPoint = end; return stringLiteral; } } return super.createStringLiteral(token, start, end, lineNumber); } protected TypeReference copyDims(TypeReference typeRef, int dim) { if (this.assistNode == typeRef) { return typeRef; } TypeReference result = super.copyDims(typeRef, dim); if (this.assistNodeParent == typeRef) { this.assistNodeParent = result; } return result; } public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLoc) { this.cursorLocation = cursorLoc; CompletionScanner completionScanner = (CompletionScanner)this.scanner; completionScanner.completionIdentifier = null; completionScanner.cursorLocation = cursorLoc; return this.dietParse(sourceUnit, compilationResult); } /* * Flush parser/scanner state regarding to code assist */ public void flushAssistState() { super.flushAssistState(); this.isOrphanCompletionNode = false; this.isAlreadyAttached = false; assistNodeParent = null; CompletionScanner completionScanner = (CompletionScanner)this.scanner; completionScanner.completedIdentifierStart = 0; completionScanner.completedIdentifierEnd = -1; } protected NameReference getUnspecifiedReference() { NameReference nameReference = super.getUnspecifiedReference(); if (this.record) { recordReference(nameReference); } return nameReference; } protected NameReference getUnspecifiedReferenceOptimized() { if (this.identifierLengthStack[this.identifierLengthPtr] > 1) { // reducing a qualified name // potential receiver is being poped, so reset potential receiver this.invocationType = NO_RECEIVER; this.qualifier = -1; } NameReference nameReference = super.getUnspecifiedReferenceOptimized(); if (this.record) { recordReference(nameReference); } return nameReference; } private boolean isAlreadyPotentialName(int identifierStart) { if (this.potentialVariableNamesPtr < 0) return false; return identifierStart <= this.potentialVariableNameEnds[this.potentialVariableNamesPtr]; } protected int indexOfAssistIdentifier(boolean useGenericsStack) { if (this.record) return -1; // when names are recorded there is no assist identifier return super.indexOfAssistIdentifier(useGenericsStack); } public void initialize() { super.initialize(); this.labelPtr = -1; this.initializeForBlockStatements(); } public void initialize(boolean initializeNLS) { super.initialize(initializeNLS); this.labelPtr = -1; this.initializeForBlockStatements(); } /* * Initializes the state of the parser that is about to go for BlockStatements. */ private void initializeForBlockStatements() { this.previousToken = -1; this.previousIdentifierPtr = -1; this.bracketDepth = 0; this.invocationType = NO_RECEIVER; this.qualifier = -1; popUntilElement(K_SWITCH_LABEL); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) != K_SWITCH_LABEL) { this.popUntilElement(K_BLOCK_DELIMITER); } } public void initializeScanner(){ this.scanner = new CompletionScanner(this.options.sourceLevel); } /** * Returns whether the completion is just after an array type * eg. String[].[cursor] */ private boolean isAfterArrayType() { // TBD: The following relies on the fact that array dimensions are small: it says that if the // top of the intStack is less than 11, then it must be a dimension // (smallest position of array type in a compilation unit is 11 as in "class X{Y[]") if ((this.intPtr > -1) && (this.intStack[this.intPtr] < 11)) { return true; } return false; } private boolean isEmptyNameCompletion() { return this.assistNode != null && this.assistNode instanceof CompletionOnSingleNameReference && (((CompletionOnSingleNameReference)this.assistNode).token.length == 0); } protected boolean isInsideAnnotation() { int i = elementPtr; while(i > -1) { if(elementKindStack[i] == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN) return true; i--; } return false; } protected boolean isIndirectlyInsideBlock(){ int i = elementPtr; while(i > -1) { if(elementKindStack[i] == K_BLOCK_DELIMITER) return true; i--; } return false; } protected boolean isInsideBlock(){ int i = elementPtr; while(i > -1) { switch (elementKindStack[i]) { case K_TYPE_DELIMITER : return false; case K_METHOD_DELIMITER : return false; case K_FIELD_INITIALIZER_DELIMITER : return false; case K_BLOCK_DELIMITER : return true; } i--; } return false; } protected boolean isInsideBreakable(){ int i = elementPtr; while(i > -1) { switch (elementKindStack[i]) { case K_TYPE_DELIMITER : return false; case K_METHOD_DELIMITER : return false; case K_FIELD_INITIALIZER_DELIMITER : return false; case K_SWITCH_LABEL : return true; case K_BLOCK_DELIMITER : switch(elementInfoStack[i]) { case FOR : case DO : case WHILE : return true; } } i--; } return false; } protected boolean isInsideLoop(){ int i = elementPtr; while(i > -1) { switch (elementKindStack[i]) { case K_TYPE_DELIMITER : return false; case K_METHOD_DELIMITER : return false; case K_FIELD_INITIALIZER_DELIMITER : return false; case K_BLOCK_DELIMITER : switch(elementInfoStack[i]) { case FOR : case DO : case WHILE : return true; } } i--; } return false; } protected boolean isInsideReturn(){ int i = elementPtr; while(i > -1) { switch (elementKindStack[i]) { case K_TYPE_DELIMITER : return false; case K_METHOD_DELIMITER : return false; case K_FIELD_INITIALIZER_DELIMITER : return false; case K_BLOCK_DELIMITER : return false; case K_INSIDE_RETURN_STATEMENT : return true; } i--; } return false; } public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLoc) { this.cursorLocation = cursorLoc; CompletionScanner completionScanner = (CompletionScanner)this.scanner; completionScanner.completionIdentifier = null; completionScanner.cursorLocation = cursorLoc; return this.parse(sourceUnit, compilationResult); } public void parseBlockStatements( ConstructorDeclaration cd, CompilationUnitDeclaration unit) { canBeExplicitConstructor = 1; super.parseBlockStatements(cd, unit); } public MethodDeclaration parseSomeStatements(int start, int end, int fakeBlocksCount, CompilationUnitDeclaration unit) { this.methodRecoveryActivated = true; initialize(); // simulate goForMethodBody except that we don't want to balance brackets because they are not going to be balanced goForBlockStatementsopt(); MethodDeclaration fakeMethod = new MethodDeclaration(unit.compilationResult()); fakeMethod.selector = FAKE_METHOD_NAME; fakeMethod.bodyStart = start; fakeMethod.bodyEnd = end; fakeMethod.declarationSourceStart = start; fakeMethod.declarationSourceEnd = end; fakeMethod.sourceStart = start; fakeMethod.sourceEnd = start; //fake method must ignore the method header referenceContext = fakeMethod; compilationUnit = unit; this.diet = false; this.restartRecovery = true; scanner.resetTo(start, end); consumeNestedMethod(); for (int i = 0; i < fakeBlocksCount; i++) { consumeOpenFakeBlock(); } try { parse(); } catch (AbortCompilation ex) { lastAct = ERROR_ACTION; } finally { nestedMethod[nestedType]--; } if (!this.hasError) { int length; if (astLengthPtr > -1 && (length = this.astLengthStack[this.astLengthPtr--]) != 0) { System.arraycopy( this.astStack, (this.astPtr -= length) + 1, fakeMethod.statements = new Statement[length], 0, length); } } return fakeMethod; } protected void popUntilCompletedAnnotationIfNecessary() { if(elementPtr < 0) return; int i = elementPtr; while(i > -1 && (elementKindStack[i] != K_BETWEEN_ANNOTATION_NAME_AND_RPAREN || (elementInfoStack[i] & ANNOTATION_NAME_COMPLETION) == 0)) { i--; } if(i >= 0) { previousKind = elementKindStack[i]; previousInfo = elementInfoStack[i]; elementPtr = i - 1; } } /* * Prepares the state of the parser to go for BlockStatements. */ protected void prepareForBlockStatements() { this.nestedMethod[this.nestedType = 0] = 1; this.variablesCounter[this.nestedType] = 0; this.realBlockStack[this.realBlockPtr = 1] = 0; this.initializeForBlockStatements(); } protected void pushOnLabelStack(char[] label){ if (this.labelPtr < -1) return; int stackLength = this.labelStack.length; if (++this.labelPtr >= stackLength) { System.arraycopy( this.labelStack, 0, this.labelStack = new char[stackLength + LabelStackIncrement][], 0, stackLength); } this.labelStack[this.labelPtr] = label; } /** * Creates a completion on member access node and push it * on the expression stack. */ private void pushCompletionOnMemberAccessOnExpressionStack(boolean isSuperAccess) { char[] source = identifierStack[identifierPtr]; long pos = identifierPositionStack[identifierPtr--]; CompletionOnMemberAccess fr = new CompletionOnMemberAccess(source, pos, isInsideAnnotation()); this.assistNode = fr; this.lastCheckPoint = fr.sourceEnd + 1; identifierLengthPtr--; if (isSuperAccess) { //considerates the fieldReference beginning at the 'super' .... fr.sourceStart = intStack[intPtr--]; fr.receiver = new SuperReference(fr.sourceStart, endPosition); pushOnExpressionStack(fr); } else { //optimize push/pop if ((fr.receiver = expressionStack[expressionPtr]).isThis()) { //fieldreference begins at the this fr.sourceStart = fr.receiver.sourceStart; } expressionStack[expressionPtr] = fr; } } public void recordCompletionOnReference(){ if (currentElement instanceof RecoveredType){ RecoveredType recoveredType = (RecoveredType)currentElement; /* filter out cases where scanner is still inside type header */ if (!recoveredType.foundOpeningBrace) return; /* generate a pseudo field with a completion on type reference */ currentElement.add( new CompletionOnFieldType(this.getTypeReference(0), false), 0); return; } if (!diet) return; // only record references attached to types } private void recordReference(NameReference nameReference) { if (!this.skipRecord && this.recordFrom <= nameReference.sourceStart && nameReference.sourceEnd <= this.recordTo && !isAlreadyPotentialName(nameReference.sourceStart)) { char[] token; if (nameReference instanceof SingleNameReference) { token = ((SingleNameReference) nameReference).token; } else { token = ((QualifiedNameReference) nameReference).tokens[0]; } // Most of the time a name which start with an uppercase is a type name. // As we don't want to resolve names to avoid to slow down performances then this name will be ignored if (Character.isUpperCase(token[0])) return; addPotentialName(token, nameReference.sourceStart, nameReference.sourceEnd); } } public void recoveryExitFromVariable() { if(currentElement != null && currentElement instanceof RecoveredLocalVariable) { RecoveredElement oldElement = currentElement; super.recoveryExitFromVariable(); if(oldElement != currentElement) { popElement(K_LOCAL_INITIALIZER_DELIMITER); } } else { super.recoveryExitFromVariable(); } } public void recoveryTokenCheck() { RecoveredElement oldElement = currentElement; switch (currentToken) { case TokenNameLBRACE : super.recoveryTokenCheck(); break; case TokenNameRBRACE : super.recoveryTokenCheck(); if(currentElement != oldElement && oldElement instanceof RecoveredBlock) { popElement(K_BLOCK_DELIMITER); } break; case TokenNamecase : super.recoveryTokenCheck(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BLOCK_DELIMITER && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == SWITCH) { pushOnElementStack(K_SWITCH_LABEL); } break; case TokenNamedefault : super.recoveryTokenCheck(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BLOCK_DELIMITER && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == SWITCH) { pushOnElementStack(K_SWITCH_LABEL, DEFAULT); } else if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SWITCH_LABEL) { popElement(K_SWITCH_LABEL); pushOnElementStack(K_SWITCH_LABEL, DEFAULT); } break; default : super.recoveryTokenCheck(); break; } } /* * Reset internal state after completion is over */ public void reset() { super.reset(); this.cursorLocation = 0; } /* * Reset internal state after completion is over */ public void resetAfterCompletion() { this.cursorLocation = 0; this.flushAssistState(); } /* * Reset context so as to resume to regular parse loop * If unable to reset for resuming, answers false. * * Move checkpoint location, reset internal stacks and * decide which grammar goal is activated. */ protected boolean resumeAfterRecovery() { if (this.assistNode != null) { /* if reached [eof] inside method body, but still inside nested type, or inside a field initializer, should continue in diet mode until the end of the method body or compilation unit */ if ((scanner.eofPosition == cursorLocation+1) && (!(referenceContext instanceof CompilationUnitDeclaration) || isIndirectlyInsideFieldInitialization() || assistNodeParent instanceof FieldDeclaration && !(assistNodeParent instanceof Initializer))) { /* disabled since does not handle possible field/message refs, that is, Obj[ASSIST HERE]ect.registerNatives() // consume extra tokens which were part of the qualified reference // so that the replaced source comprises them as well if (this.assistNode instanceof NameReference){ int oldEof = scanner.eofPosition; scanner.eofPosition = currentElement.topElement().sourceEnd()+1; scanner.currentPosition = this.cursorLocation+1; int token = -1; try { do { // first token might not have to be a dot if (token >= 0 || !this.completionBehindDot){ if ((token = scanner.getNextToken()) != TokenNameDOT) break; } if ((token = scanner.getNextToken()) != TokenNameIdentifier) break; this.assistNode.sourceEnd = scanner.currentPosition - 1; } while (token != TokenNameEOF); } catch (InvalidInputException e){ } finally { scanner.eofPosition = oldEof; } } */ /* restart in diet mode for finding sibling constructs */ if (currentElement instanceof RecoveredType || currentElement.enclosingType() != null){ if(lastCheckPoint <= this.assistNode.sourceEnd) { lastCheckPoint = this.assistNode.sourceEnd+1; } int end = currentElement.topElement().sourceEnd(); scanner.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end; } else { this.resetStacks(); return false; } } } return super.resumeAfterRecovery(); } public void setAssistIdentifier(char[] assistIdent){ ((CompletionScanner)scanner).completionIdentifier = assistIdent; } public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("elementKindStack : int[] = {"); //$NON-NLS-1$ for (int i = 0; i <= elementPtr; i++) { buffer.append(String.valueOf(elementKindStack[i])).append(','); } buffer.append("}\n"); //$NON-NLS-1$ buffer.append("elementInfoStack : int[] = {"); //$NON-NLS-1$ for (int i = 0; i <= elementPtr; i++) { buffer.append(String.valueOf(elementInfoStack[i])).append(','); } buffer.append("}\n"); //$NON-NLS-1$ buffer.append(super.toString()); return String.valueOf(buffer); } /* * Update recovery state based on current parser/scanner state */ protected void updateRecoveryState() { /* expose parser state to recovery state */ currentElement.updateFromParserState(); /* may be able to retrieve completionNode as an orphan, and then attach it */ this.completionIdentifierCheck(); this.attachOrphanCompletionNode(); // if an assist node has been found and a recovered element exists, // mark enclosing blocks as to be preserved if (this.assistNode != null && this.currentElement != null) { currentElement.preserveEnclosingBlocks(); } /* check and update recovered state based on current token, this action is also performed when shifting token after recovery got activated once. */ this.recoveryTokenCheck(); this.recoveryExitFromVariable(); } protected LocalDeclaration createLocalDeclaration(char[] assistName, int sourceStart, int sourceEnd) { if (this.indexOfAssistIdentifier() < 0) { return super.createLocalDeclaration(assistName, sourceStart, sourceEnd); } else { CompletionOnLocalName local = new CompletionOnLocalName(assistName, sourceStart, sourceEnd); this.assistNode = local; this.lastCheckPoint = sourceEnd + 1; return local; } } protected FieldDeclaration createFieldDeclaration(char[] assistName, int sourceStart, int sourceEnd) { if (this.indexOfAssistIdentifier() < 0 || (currentElement instanceof RecoveredUnit && ((RecoveredUnit)currentElement).statementCount == 0)) { return super.createFieldDeclaration(assistName, sourceStart, sourceEnd); } else { CompletionOnFieldName field = new CompletionOnFieldName(assistName, sourceStart, sourceEnd); this.assistNode = field; this.lastCheckPoint = sourceEnd + 1; return field; } } protected void classInstanceCreation(boolean isQualified, boolean isShort) { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.classInstanceCreation(isQualified, isShort); } public int getCursorLocation() { return this.cursorLocation; } protected MessageSend newMessageSend() { if (AssistParser.STOP_AT_CURSOR) return super.newMessageSend(); // '(' ArgumentListopt ')' // the arguments are on the expression stack int numArgs=expressionLengthStack[expressionLengthPtr]; Expression receiver = expressionStack[expressionPtr-numArgs]; // char[] selector = identifierStack[identifierPtr]; // if (selector != this.assistIdentifier()){ if (!(receiver instanceof CompletionOnMemberAccess || receiver instanceof CompletionOnSingleNameReference)) { return super.newMessageSend(); } MessageSend messageSend = new CompletionOnMessageSend(); int length; if ((length = expressionLengthStack[expressionLengthPtr--]) != 0) { expressionPtr -= length; System.arraycopy( expressionStack, expressionPtr + 1, messageSend.arguments = new Expression[length], 0, length); } assistNode = messageSend; if (!diet){ this.restartRecovery = AssistParser.STOP_AT_CURSOR; // force to restart in recovery mode this.lastIgnoredToken = -1; } this.isOrphanCompletionNode = true; return messageSend; } protected JavadocParser createJavadocParser() { return new CompletionJavadocParser(this); } /** * @see org.eclipse.wst.jsdt.internal.codeassist.impl.AssistParser#createAssistTypeForAllocation(org.eclipse.wst.jsdt.internal.compiler.ast.AllocationExpression) */ public void createAssistTypeForAllocation(AllocationExpression expression) { Expression member = expression.member; /* create a CompletionOnSingleTypeReference from the existing * member expression for the given allocation expression */ if (member instanceof SingleNameReference) { SingleNameReference snr = (SingleNameReference) member; long position = (((long)snr.sourceStart)<<32)+snr.sourceEnd; expression.member= new CompletionOnSingleTypeReference(snr.token,position); ((CompletionOnSingleTypeReference)expression.member).isConstructorType = true; } else if(member instanceof CompletionOnMemberAccess) { CompletionOnMemberAccess memberAccess = (CompletionOnMemberAccess) member; //iterate over the receivers to build the token and find the start of the expression IExpression receiver = memberAccess.getReceiver(); String token = new String(memberAccess.getToken()); int start = memberAccess.sourceStart(); while(receiver != null) { start = receiver.sourceStart(); if(receiver instanceof IFieldReference) { IFieldReference ref = (IFieldReference)receiver; token = new String(ref.getToken()) + "." + token; receiver = ref.getReceiver(); } else if(receiver instanceof ISingleNameReference) { ISingleNameReference ref = (ISingleNameReference)receiver; token = new String(ref.getToken()) + "." + token; receiver = null; } } //create and set the CompletionOnSingleTypeReference long position = (((long)start)<<32)+memberAccess.sourceEnd; expression.member = new CompletionOnSingleTypeReference(token.toCharArray(), position); ((CompletionOnSingleTypeReference)expression.member).isConstructorType = true; } } }