--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ ******************************************************************************/
+package net.sourceforge.phpdt.internal.codeassist.impl;
+
+/*
+ * Parser extension for code assist task
+ *
+ */
+
+import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.AstNode;
+import net.sourceforge.phpdt.internal.compiler.ast.Block;
+import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.ConstructorDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.ExplicitConstructorCall;
+import net.sourceforge.phpdt.internal.compiler.ast.Expression;
+import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.ImportReference;
+import net.sourceforge.phpdt.internal.compiler.ast.Initializer;
+import net.sourceforge.phpdt.internal.compiler.ast.LocalDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.MessageSend;
+import net.sourceforge.phpdt.internal.compiler.ast.MethodDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.NameReference;
+import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.TypeReference;
+import net.sourceforge.phpdt.internal.compiler.parser.Parser;
+import net.sourceforge.phpdt.internal.compiler.parser.RecoveredElement;
+import net.sourceforge.phpdt.internal.compiler.parser.RecoveredField;
+import net.sourceforge.phpdt.internal.compiler.parser.RecoveredInitializer;
+import net.sourceforge.phpdt.internal.compiler.parser.RecoveredMethod;
+import net.sourceforge.phpdt.internal.compiler.parser.RecoveredType;
+import net.sourceforge.phpdt.internal.compiler.parser.RecoveredUnit;
+import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
+import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
+
+public abstract class AssistParser extends Parser {
+
+ public AstNode assistNode;
+ public boolean isOrphanCompletionNode;
+
+ /* recovery */
+ int[] blockStarts = new int[30];
+
+ // the previous token read by the scanner
+ protected int previousToken;
+
+ // the index in the identifier stack of the previous identifier
+ protected int previousIdentifierPtr;
+
+ // the stacks of selectors for invocations (ie. method invocations, allocation expressions and
+ // explicit constructor invocations)
+ // the selector stack contains pointers to the identifier stack or one of the selector constants below
+ protected int invocationPtr;
+ protected int[] selectorStack = new int[StackIncrement];
+
+ // selector constants
+ protected static final int THIS_CONSTRUCTOR = -1;
+ protected static final int SUPER_CONSTRUCTOR = -2;
+
+ // whether the parser is in a field initializer
+ // (false is pushed each time a new type is entered,
+ // it is changed to true when the initializer is entered,
+ // it is changed back to false when the initializer is exited,
+ // and it is poped when the type is exited)
+ protected int inFieldInitializationPtr;
+ protected boolean[] inFieldInitializationStack = new boolean[StackIncrement];
+
+ // whether the parser is in a method, constructor or initializer
+ // (false is pushed each time a new type is entered,
+ // it is changed to true when the method is entered,
+ // it is changed back to false when the method is exited,
+ // and it is poped when the type is exited)
+ protected int inMethodPtr;
+ protected boolean[] inMethodStack = new boolean[StackIncrement];
+public AssistParser(ProblemReporter problemReporter, boolean assertMode) {
+ super(problemReporter, true, assertMode);
+}
+public abstract char[] assistIdentifier();
+public int bodyEnd(AbstractMethodDeclaration method){
+ return method.declarationSourceEnd;
+}
+public int bodyEnd(Initializer initializer){
+ return initializer.declarationSourceEnd;
+}
+/*
+ * Build initial recovery state.
+ * Recovery state is inferred from the current state of the parser (reduced node stack).
+ */
+public RecoveredElement buildInitialRecoveryState(){
+
+ /* recovery in unit structure */
+ if (referenceContext instanceof CompilationUnitDeclaration){
+ RecoveredElement element = super.buildInitialRecoveryState();
+ flushAssistState();
+ initInMethodAndInFieldInitializationStack(element);
+ return element;
+ }
+
+ /* recovery in method body */
+ lastCheckPoint = 0;
+
+ RecoveredElement element = null;
+ if (referenceContext instanceof AbstractMethodDeclaration){
+ element = new RecoveredMethod((AbstractMethodDeclaration) referenceContext, null, 0, this);
+ lastCheckPoint = ((AbstractMethodDeclaration) referenceContext).bodyStart;
+ } else {
+ /* Initializer bodies are parsed in the context of the type declaration, we must thus search it inside */
+ if (referenceContext instanceof TypeDeclaration){
+ TypeDeclaration type = (TypeDeclaration) referenceContext;
+ for (int i = 0; i < type.fields.length; i++){
+ FieldDeclaration field = type.fields[i];
+ if (!field.isField()
+ && field.declarationSourceStart <= scanner.initialPosition
+ && scanner.initialPosition <= field.declarationSourceEnd
+ && scanner.eofPosition <= field.declarationSourceEnd+1){
+ element = new RecoveredInitializer((Initializer) field, null, 1, this);
+ lastCheckPoint = field.declarationSourceStart;
+ break;
+ }
+ }
+ }
+ }
+
+ if (element == null) return element;
+
+ /* add initial block */
+ Block block = new Block(0);
+ int lastStart = blockStarts[0];
+ block.sourceStart = lastStart;
+ element = element.add(block, 1);
+ int blockIndex = 1; // ignore first block start, since manually rebuilt here
+
+ for(int i = 0; i <= astPtr; i++){
+ AstNode node = astStack[i];
+
+ /* check for intermediate block creation, so recovery can properly close them afterwards */
+ int nodeStart = node.sourceStart;
+ for (int j = blockIndex; j <= realBlockPtr; j++){
+ if (blockStarts[j] > nodeStart){
+ blockIndex = j; // shift the index to the new block
+ break;
+ }
+ if (blockStarts[j] != lastStart){ // avoid multiple block if at same position
+ block = new Block(0);
+ block.sourceStart = lastStart = blockStarts[j];
+ element = element.add(block, 1);
+ }
+ blockIndex = j+1; // shift the index to the new block
+ }
+ if (node instanceof LocalDeclaration){
+ LocalDeclaration local = (LocalDeclaration) node;
+ if (local.declarationSourceEnd == 0){
+ element = element.add(local, 0);
+ if (local.initialization == null){
+ lastCheckPoint = local.sourceEnd + 1;
+ } else {
+ lastCheckPoint = local.initialization.sourceEnd + 1;
+ }
+ } else {
+ element = element.add(local, 0);
+ lastCheckPoint = local.declarationSourceEnd + 1;
+ }
+ continue;
+ }
+ if (node instanceof AbstractMethodDeclaration){
+ AbstractMethodDeclaration method = (AbstractMethodDeclaration) node;
+ if (method.declarationSourceEnd == 0){
+ element = element.add(method, 0);
+ lastCheckPoint = method.bodyStart;
+ } else {
+ element = element.add(method, 0);
+ lastCheckPoint = method.declarationSourceEnd + 1;
+ }
+ continue;
+ }
+ if (node instanceof Initializer){
+ Initializer initializer = (Initializer) node;
+ if (initializer.declarationSourceEnd == 0){
+ element = element.add(initializer, 1);
+ lastCheckPoint = initializer.bodyStart;
+ } else {
+ element = element.add(initializer, 0);
+ lastCheckPoint = initializer.declarationSourceEnd + 1;
+ }
+ continue;
+ }
+ if (node instanceof FieldDeclaration){
+ FieldDeclaration field = (FieldDeclaration) node;
+ if (field.declarationSourceEnd == 0){
+ element = element.add(field, 0);
+ if (field.initialization == null){
+ lastCheckPoint = field.sourceEnd + 1;
+ } else {
+ lastCheckPoint = field.initialization.sourceEnd + 1;
+ }
+ } else {
+ element = element.add(field, 0);
+ lastCheckPoint = field.declarationSourceEnd + 1;
+ }
+ continue;
+ }
+ if (node instanceof TypeDeclaration){
+ TypeDeclaration type = (TypeDeclaration) node;
+ if (type.declarationSourceEnd == 0){
+ element = element.add(type, 0);
+ lastCheckPoint = type.bodyStart;
+ } else {
+ element = element.add(type, 0);
+ lastCheckPoint = type.declarationSourceEnd + 1;
+ }
+ continue;
+ }
+ if (node instanceof ImportReference){
+ ImportReference importRef = (ImportReference) node;
+ element = element.add(importRef, 0);
+ lastCheckPoint = importRef.declarationSourceEnd + 1;
+ }
+ }
+ if (this.currentToken == TokenNameRBRACE) {
+ this.currentToken = 0; // closing brace has already been taken care of
+ }
+
+ /* might need some extra block (after the last reduced node) */
+ int pos = this.assistNode == null ? lastCheckPoint : this.assistNode.sourceStart;
+ for (int j = blockIndex; j <= realBlockPtr; j++){
+ if ((blockStarts[j] < pos) && (blockStarts[j] != lastStart)){ // avoid multiple block if at same position
+ block = new Block(0);
+ block.sourceStart = lastStart = blockStarts[j];
+ element = element.add(block, 1);
+ }
+ }
+
+ initInMethodAndInFieldInitializationStack(element);
+ return element;
+}
+protected void consumeClassBodyDeclarationsopt() {
+ super.consumeClassBodyDeclarationsopt();
+ this.inFieldInitializationPtr--;
+ this.inMethodPtr--;
+}
+protected void consumeClassBodyopt() {
+ super.consumeClassBodyopt();
+ this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
+}
+protected void consumeClassHeader() {
+ super.consumeClassHeader();
+ this.pushNotInInitializer();
+ this.pushNotInMethod();
+}
+protected void consumeConstructorBody() {
+ super.consumeConstructorBody();
+ this.inMethodStack[this.inMethodPtr] = false;
+}
+protected void consumeConstructorHeader() {
+ super.consumeConstructorHeader();
+ this.inMethodStack[this.inMethodPtr] = true;
+}
+protected void consumeEmptyClassBodyDeclarationsopt() {
+ super.consumeEmptyClassBodyDeclarationsopt();
+ this.inFieldInitializationPtr--;
+ this.inMethodPtr--;
+}
+protected void consumeEnterAnonymousClassBody() {
+ super.consumeEnterAnonymousClassBody();
+ this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
+ this.pushNotInInitializer();
+ this.pushNotInMethod();
+}
+protected void consumeExplicitConstructorInvocation(int flag, int recFlag) {
+ super.consumeExplicitConstructorInvocation(flag, recFlag);
+ this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
+}
+protected void consumeForceNoDiet() {
+ super.consumeForceNoDiet();
+ // if we are not in a method (ie. we are not in a local variable initializer)
+ // then we are entering a field initializer
+ if (!this.inMethodStack[this.inMethodPtr]) {
+ this.inFieldInitializationStack[this.inFieldInitializationPtr] = true;
+ }
+}
+protected void consumeInterfaceHeader() {
+ super.consumeInterfaceHeader();
+ this.pushNotInInitializer();
+ this.pushNotInMethod();
+}
+protected void consumeInterfaceMemberDeclarationsopt() {
+ super.consumeInterfaceMemberDeclarationsopt();
+ this.inFieldInitializationPtr--;
+ this.inMethodPtr--;
+}
+protected void consumeMethodBody() {
+ super.consumeMethodBody();
+ this.inMethodStack[this.inMethodPtr] = false;
+}
+protected void consumeMethodHeader() {
+ super.consumeMethodHeader();
+ this.inMethodStack[this.inMethodPtr] = true;
+}
+protected void consumeMethodInvocationName() {
+ super.consumeMethodInvocationName();
+ this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
+ MessageSend messageSend = (MessageSend)expressionStack[expressionPtr];
+ if (messageSend == assistNode){
+ this.lastCheckPoint = messageSend.sourceEnd + 1;
+ }
+}
+protected void consumeMethodInvocationPrimary() {
+ super.consumeMethodInvocationPrimary();
+ this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
+ MessageSend messageSend = (MessageSend)expressionStack[expressionPtr];
+ if (messageSend == assistNode){
+ this.lastCheckPoint = messageSend.sourceEnd + 1;
+ }
+}
+protected void consumeMethodInvocationSuper() {
+ super.consumeMethodInvocationSuper();
+ this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
+ MessageSend messageSend = (MessageSend)expressionStack[expressionPtr];
+ if (messageSend == assistNode){
+ this.lastCheckPoint = messageSend.sourceEnd + 1;
+ }
+}
+protected void consumeNestedMethod() {
+ super.consumeNestedMethod();
+ this.inMethodStack[this.inMethodPtr] = true;
+}
+protected void consumeOpenBlock() {
+ // OpenBlock ::= $empty
+
+ super.consumeOpenBlock();
+ try {
+ blockStarts[realBlockPtr] = scanner.startPosition;
+ } catch (IndexOutOfBoundsException e) {
+ //realBlockPtr is correct
+ int oldStackLength = blockStarts.length;
+ int oldStack[] = blockStarts;
+ blockStarts = new int[oldStackLength + StackIncrement];
+ System.arraycopy(oldStack, 0, blockStarts, 0, oldStackLength);
+ blockStarts[realBlockPtr] = scanner.startPosition;
+ }
+}
+protected void consumePackageDeclarationName() {
+ // PackageDeclarationName ::= 'package' Name
+ /* build an ImportRef build from the last name
+ stored in the identifier stack. */
+
+ int index;
+
+ /* no need to take action if not inside assist identifiers */
+ if ((index = indexOfAssistIdentifier()) < 0) {
+ super.consumePackageDeclarationName();
+ return;
+ }
+ /* retrieve identifiers subset and whole positions, the assist node positions
+ should include the entire replaced source. */
+ int length = identifierLengthStack[identifierLengthPtr];
+ char[][] subset = identifierSubSet(index+1); // include the assistIdentifier
+ identifierLengthPtr--;
+ identifierPtr -= length;
+ long[] positions = new long[length];
+ System.arraycopy(
+ identifierPositionStack,
+ identifierPtr + 1,
+ positions,
+ 0,
+ length);
+
+ /* build specific assist node on package statement */
+ ImportReference reference = this.createAssistPackageReference(subset, positions);
+ assistNode = reference;
+ this.lastCheckPoint = reference.sourceEnd + 1;
+ compilationUnit.currentPackage = reference;
+
+ if (currentToken == TokenNameSEMICOLON){
+ reference.declarationSourceEnd = scanner.currentPosition - 1;
+ } else {
+ reference.declarationSourceEnd = (int) positions[length-1];
+ }
+ //endPosition is just before the ;
+ reference.declarationSourceStart = intStack[intPtr--];
+ // flush annotations defined prior to import statements
+ reference.declarationSourceEnd = this.flushAnnotationsDefinedPriorTo(reference.declarationSourceEnd);
+
+ // recovery
+ if (currentElement != null){
+ lastCheckPoint = reference.declarationSourceEnd+1;
+ restartRecovery = true; // used to avoid branching back into the regular automaton
+ }
+}
+protected void consumeRestoreDiet() {
+ super.consumeRestoreDiet();
+ // if we are not in a method (ie. we were not in a local variable initializer)
+ // then we are exiting a field initializer
+ if (!this.inMethodStack[this.inMethodPtr]) {
+ this.inFieldInitializationStack[this.inFieldInitializationPtr] = false;
+ }
+}
+protected void consumeSingleTypeImportDeclarationName() {
+ // SingleTypeImportDeclarationName ::= 'import' Name
+ /* push an ImportRef build from the last name
+ stored in the identifier stack. */
+
+ int index;
+
+ /* no need to take action if not inside assist identifiers */
+ if ((index = indexOfAssistIdentifier()) < 0) {
+ super.consumeSingleTypeImportDeclarationName();
+ return;
+ }
+ /* retrieve identifiers subset and whole positions, the assist node positions
+ should include the entire replaced source. */
+ int length = identifierLengthStack[identifierLengthPtr];
+ char[][] subset = identifierSubSet(index+1); // include the assistIdentifier
+ identifierLengthPtr--;
+ identifierPtr -= length;
+ long[] positions = new long[length];
+ System.arraycopy(
+ identifierPositionStack,
+ identifierPtr + 1,
+ positions,
+ 0,
+ length);
+
+ /* build specific assist node on import statement */
+ ImportReference reference = this.createAssistImportReference(subset, positions);
+ assistNode = reference;
+ this.lastCheckPoint = reference.sourceEnd + 1;
+
+ pushOnAstStack(reference);
+
+ if (currentToken == TokenNameSEMICOLON){
+ reference.declarationSourceEnd = scanner.currentPosition - 1;
+ } else {
+ reference.declarationSourceEnd = (int) positions[length-1];
+ }
+ //endPosition is just before the ;
+ reference.declarationSourceStart = intStack[intPtr--];
+ // flush annotations defined prior to import statements
+ reference.declarationSourceEnd = this.flushAnnotationsDefinedPriorTo(reference.declarationSourceEnd);
+
+ // recovery
+ if (currentElement != null){
+ lastCheckPoint = reference.declarationSourceEnd+1;
+ currentElement = currentElement.add(reference, 0);
+ lastIgnoredToken = -1;
+ restartRecovery = true; // used to avoid branching back into the regular automaton
+ }
+}
+protected void consumeStaticInitializer() {
+ super.consumeStaticInitializer();
+ this.inMethodStack[this.inMethodPtr] = false;
+}
+protected void consumeStaticOnly() {
+ super.consumeStaticOnly();
+ this.inMethodStack[this.inMethodPtr] = true;
+}
+protected void consumeToken(int token) {
+ super.consumeToken(token);
+ // register message send selector only if inside a method or if looking at a field initializer
+ // and if the current token is an open parenthesis
+ if ((this.inMethodStack[this.inMethodPtr] || this.inFieldInitializationStack[this.inFieldInitializationPtr]) && token == TokenNameLPAREN) {
+ switch (this.previousToken) {
+ case TokenNameIdentifier:
+ this.pushOnSelectorStack(this.identifierPtr);
+ break;
+// case TokenNamethis: // explicit constructor invocation, eg. this(1, 2)
+// this.pushOnSelectorStack(THIS_CONSTRUCTOR);
+// break;
+// case TokenNamesuper: // explicit constructor invocation, eg. super(1, 2)
+// this.pushOnSelectorStack(SUPER_CONSTRUCTOR);
+// break;
+ }
+ }
+ this.previousToken = token;
+ if (token == TokenNameIdentifier) {
+ this.previousIdentifierPtr = this.identifierPtr;
+ }
+}
+protected void consumeTypeImportOnDemandDeclarationName() {
+ // TypeImportOnDemandDeclarationName ::= 'import' Name '.' '*'
+ /* push an ImportRef build from the last name
+ stored in the identifier stack. */
+
+ int index;
+
+ /* no need to take action if not inside assist identifiers */
+ if ((index = indexOfAssistIdentifier()) < 0) {
+ super.consumeTypeImportOnDemandDeclarationName();
+ return;
+ }
+ /* retrieve identifiers subset and whole positions, the assist node positions
+ should include the entire replaced source. */
+ int length = identifierLengthStack[identifierLengthPtr];
+ char[][] subset = identifierSubSet(index+1); // include the assistIdentifier
+ identifierLengthPtr--;
+ identifierPtr -= length;
+ long[] positions = new long[length];
+ System.arraycopy(
+ identifierPositionStack,
+ identifierPtr + 1,
+ positions,
+ 0,
+ length);
+
+ /* build specific assist node on import statement */
+ ImportReference reference = this.createAssistImportReference(subset, positions);
+ reference.onDemand = true;
+ assistNode = reference;
+ this.lastCheckPoint = reference.sourceEnd + 1;
+
+ pushOnAstStack(reference);
+
+ if (currentToken == TokenNameSEMICOLON){
+ reference.declarationSourceEnd = scanner.currentPosition - 1;
+ } else {
+ reference.declarationSourceEnd = (int) positions[length-1];
+ }
+ //endPosition is just before the ;
+ reference.declarationSourceStart = intStack[intPtr--];
+ // flush annotations defined prior to import statements
+ reference.declarationSourceEnd = this.flushAnnotationsDefinedPriorTo(reference.declarationSourceEnd);
+
+ // recovery
+ if (currentElement != null){
+ lastCheckPoint = reference.declarationSourceEnd+1;
+ currentElement = currentElement.add(reference, 0);
+ lastIgnoredToken = -1;
+ restartRecovery = true; // used to avoid branching back into the regular automaton
+ }
+}
+public abstract ImportReference createAssistImportReference(char[][] tokens, long[] positions);
+public abstract ImportReference createAssistPackageReference(char[][] tokens, long[] positions);
+public abstract NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions);
+public abstract TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] name, long[] positions);
+public abstract NameReference createSingleAssistNameReference(char[] name, long position);
+public abstract TypeReference createSingleAssistTypeReference(char[] name, long position);
+/*
+ * Flush parser/scanner state regarding to code assist
+ */
+public void flushAssistState(){
+ this.assistNode = null;
+ this.isOrphanCompletionNode = false;
+ this.setAssistIdentifier(null);
+}
+/*
+ * Build specific type reference nodes in case the cursor is located inside the type reference
+ */
+protected TypeReference getTypeReference(int dim) {
+
+ int index;
+
+ /* no need to take action if not inside completed identifiers */
+ if ((index = indexOfAssistIdentifier()) < 0) {
+ return super.getTypeReference(dim);
+ }
+
+ /* retrieve identifiers subset and whole positions, the assist node positions
+ should include the entire replaced source. */
+ int length = identifierLengthStack[identifierLengthPtr];
+ char[][] subset = identifierSubSet(index);
+ identifierLengthPtr--;
+ identifierPtr -= length;
+ long[] positions = new long[length];
+ System.arraycopy(
+ identifierPositionStack,
+ identifierPtr + 1,
+ positions,
+ 0,
+ length);
+
+ /* build specific assist on type reference */
+ TypeReference reference;
+ if (index == 0) {
+ /* assist inside first identifier */
+ reference = this.createSingleAssistTypeReference(
+ assistIdentifier(),
+ positions[0]);
+ } else {
+ /* assist inside subsequent identifier */
+ reference = this.createQualifiedAssistTypeReference(
+ subset,
+ assistIdentifier(),
+ positions);
+ }
+ assistNode = reference;
+ this.lastCheckPoint = reference.sourceEnd + 1;
+ return reference;
+}
+/*
+ * Copy of code from superclass with the following change:
+ * In the case of qualified name reference if the cursor location is on the
+ * qualified name reference, then create a CompletionOnQualifiedNameReference
+ * instead.
+ */
+protected NameReference getUnspecifiedReferenceOptimized() {
+
+ int completionIndex;
+
+ /* no need to take action if not inside completed identifiers */
+ if ((completionIndex = indexOfAssistIdentifier()) < 0) {
+ return super.getUnspecifiedReferenceOptimized();
+ }
+
+ /* retrieve identifiers subset and whole positions, the completion node positions
+ should include the entire replaced source. */
+ int length = identifierLengthStack[identifierLengthPtr];
+ char[][] subset = identifierSubSet(completionIndex);
+ identifierLengthPtr--;
+ identifierPtr -= length;
+ long[] positions = new long[length];
+ System.arraycopy(
+ identifierPositionStack,
+ identifierPtr + 1,
+ positions,
+ 0,
+ length);
+
+ /* build specific completion on name reference */
+ NameReference reference;
+ if (completionIndex == 0) {
+ /* completion inside first identifier */
+ reference = this.createSingleAssistNameReference(assistIdentifier(), positions[0]);
+ } else {
+ /* completion inside subsequent identifier */
+ reference = this.createQualifiedAssistNameReference(subset, assistIdentifier(), positions);
+ };
+ reference.bits &= ~AstNode.RestrictiveFlagMASK;
+ reference.bits |= LOCAL | FIELD;
+
+ assistNode = reference;
+ lastCheckPoint = reference.sourceEnd + 1;
+ return reference;
+}
+public void goForBlockStatementsopt() {
+ //tells the scanner to go for block statements opt parsing
+
+ firstToken = TokenNameTWIDDLE;
+ scanner.recordLineSeparator = false;
+}
+public void goForConstructorBlockStatementsopt() {
+ //tells the scanner to go for constructor block statements opt parsing
+
+ firstToken = TokenNameNOT;
+ scanner.recordLineSeparator = false;
+}
+/*
+ * Retrieve a partial subset of a qualified name reference up to the completion point.
+ * It does not pop the actual awaiting identifiers, so as to be able to retrieve position
+ * information afterwards.
+ */
+protected char[][] identifierSubSet(int subsetLength){
+
+ if (subsetLength == 0) return null;
+
+ char[][] subset;
+ System.arraycopy(
+ identifierStack,
+ identifierPtr - identifierLengthStack[identifierLengthPtr] + 1,
+ (subset = new char[subsetLength][]),
+ 0,
+ subsetLength);
+ return subset;
+}
+/*
+ * Iterate the most recent group of awaiting identifiers (grouped for qualified name reference (eg. aa.bb.cc)
+ * so as to check whether one of them is the assist identifier.
+ * If so, then answer the index of the assist identifier (0 being the first identifier of the set).
+ * eg. aa(0).bb(1).cc(2)
+ * If no assist identifier was found, answers -1.
+ */
+protected int indexOfAssistIdentifier(){
+
+ if (identifierLengthPtr < 0){
+ return -1; // no awaiting identifier
+ }
+
+ char[] assistIdentifier ;
+ if ((assistIdentifier = this.assistIdentifier()) == null){
+ return -1; // no assist identifier found yet
+ }
+
+ // iterate awaiting identifiers backwards
+ int length = identifierLengthStack[identifierLengthPtr];
+ for (int i = 0; i < length; i++){
+ if (identifierStack[identifierPtr - i] == assistIdentifier){
+ return length - i - 1;
+ }
+ }
+ // none of the awaiting identifiers is the completion one
+ return -1;
+}
+public void initialize() {
+ super.initialize();
+ this.flushAssistState();
+ this.invocationPtr = -1;
+ this.inMethodStack[this.inMethodPtr = 0] = false;
+ this.inFieldInitializationStack[this.inFieldInitializationPtr = 0] = false;
+ this.previousIdentifierPtr = -1;
+}
+public abstract void initializeScanner();
+
+protected void initInMethodAndInFieldInitializationStack(RecoveredElement currentElement) {
+
+ int length = currentElement.depth() + 1;
+ int ptr = length;
+ boolean[] methodStack = new boolean[length];
+ boolean[] fieldInitializationStack = new boolean[length];
+ boolean inMethod = false;
+ boolean inFieldInitializer = false;
+
+ RecoveredElement element = currentElement;
+ while(element != null){
+ if(element instanceof RecoveredMethod ||
+ element instanceof RecoveredInitializer) {
+ if(element.parent == null) {
+ methodStack[--ptr] = true;
+ fieldInitializationStack[ptr] = false;
+ }
+ inMethod = true;
+ } else if(element instanceof RecoveredField){
+ inFieldInitializer = element.sourceEnd() == 0;
+ } else if(element instanceof RecoveredType){
+ methodStack[--ptr] = inMethod;
+ fieldInitializationStack[ptr] = inFieldInitializer;
+
+ inMethod = false;
+ inFieldInitializer = false;
+ } else if(element instanceof RecoveredUnit) {
+ methodStack[--ptr] = false;
+ fieldInitializationStack[ptr] = false;
+ }
+ element = element.parent;
+ }
+
+ inMethodPtr = length - ptr - 1;
+ inFieldInitializationPtr = inMethodPtr;
+ System.arraycopy(methodStack, ptr, inMethodStack, 0, inMethodPtr + 1);
+ System.arraycopy(fieldInitializationStack, ptr, inFieldInitializationStack, 0, inFieldInitializationPtr + 1);
+
+}
+
+/**
+ * Returns whether we are directly or indirectly inside a field initializer.
+ */
+protected boolean insideFieldInitialization() {
+ for (int i = this.inFieldInitializationPtr; i >= 0; i--) {
+ if (this.inFieldInitializationStack[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+/**
+ * Parse the block statements inside the given method declaration and try to complete at the
+ * cursor location.
+ */
+public void parseBlockStatements(AbstractMethodDeclaration md, CompilationUnitDeclaration unit) {
+ if (md instanceof MethodDeclaration) {
+ parseBlockStatements((MethodDeclaration) md, unit);
+ } else if (md instanceof ConstructorDeclaration) {
+ parseBlockStatements((ConstructorDeclaration) md, unit);
+ }
+}
+/**
+ * Parse the block statements inside the given constructor declaration and try to complete at the
+ * cursor location.
+ */
+public void parseBlockStatements(ConstructorDeclaration cd, CompilationUnitDeclaration unit) {
+ //only parse the method body of cd
+ //fill out its statements
+
+ //convert bugs into parse error
+
+ initialize();
+
+ // simulate goForConstructorBody except that we don't want to balance brackets because they are not going to be balanced
+ goForConstructorBlockStatementsopt();
+
+ referenceContext = cd;
+ compilationUnit = unit;
+
+ scanner.resetTo(cd.bodyStart, bodyEnd(cd));
+ consumeNestedMethod();
+ try {
+ parse();
+ } catch (AbortCompilation ex) {
+ lastAct = ERROR_ACTION;
+ }
+}
+/**
+ * Parse the block statements inside the given initializer and try to complete at the
+ * cursor location.
+ */
+public void parseBlockStatements(
+ Initializer ini,
+ TypeDeclaration type,
+ CompilationUnitDeclaration unit) {
+
+ initialize();
+
+ // simulate goForInitializer except that we don't want to balance brackets because they are not going to be balanced
+ goForBlockStatementsopt();
+
+ referenceContext = type;
+ compilationUnit = unit;
+
+ scanner.resetTo(ini.sourceStart, bodyEnd(ini)); // just after the beginning {
+ consumeNestedMethod();
+ try {
+ parse();
+ } catch (AbortCompilation ex) {
+ lastAct = ERROR_ACTION;
+ } finally {
+ nestedMethod[nestedType]--;
+ }
+}
+/**
+ * Parse the block statements inside the given method declaration and try to complete at the
+ * cursor location.
+ */
+public void parseBlockStatements(MethodDeclaration md, CompilationUnitDeclaration unit) {
+ //only parse the method body of md
+ //fill out method statements
+
+ //convert bugs into parse error
+
+ if (md.isAbstract())
+ return;
+ if (md.isNative())
+ return;
+ if ((md.modifiers & AccSemicolonBody) != 0)
+ return;
+
+ initialize();
+
+ // simulate goForMethodBody except that we don't want to balance brackets because they are not going to be balanced
+ goForBlockStatementsopt();
+
+ referenceContext = md;
+ compilationUnit = unit;
+
+ scanner.resetTo(md.bodyStart, bodyEnd(md)); // reset the scanner to parser from { down to the cursor location
+ consumeNestedMethod();
+ try {
+ parse();
+ } catch (AbortCompilation ex) {
+ lastAct = ERROR_ACTION;
+ } finally {
+ nestedMethod[nestedType]--;
+ }
+}
+/*
+ * 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.invocationPtr = -1;
+}
+/*
+ * Pushes 'false' on the inInitializerStack.
+ */
+protected void pushNotInInitializer() {
+ try {
+ this.inFieldInitializationStack[++this.inFieldInitializationPtr] = false;
+ } catch (IndexOutOfBoundsException e) {
+ //except in test's cases, it should never raise
+ int oldStackLength = this.inFieldInitializationStack.length;
+ System.arraycopy(this.inFieldInitializationStack , 0, (this.inFieldInitializationStack = new boolean[oldStackLength + StackIncrement]), 0, oldStackLength);
+ this.inFieldInitializationStack[this.inFieldInitializationPtr] = false;
+ }
+}
+/*
+ * Pushes 'false' on the inMethodStack.
+ */
+protected void pushNotInMethod() {
+ try {
+ this.inMethodStack[++this.inMethodPtr] = false;
+ } catch (IndexOutOfBoundsException e) {
+ //except in test's cases, it should never raise
+ int oldStackLength = this.inMethodStack.length;
+ System.arraycopy(this.inMethodStack , 0, (this.inMethodStack = new boolean[oldStackLength + StackIncrement]), 0, oldStackLength);
+ this.inMethodStack[this.inMethodPtr] = false;
+ }
+}
+/**
+ * Pushes the given the given selector (an identifier pointer to the identifier stack) on the selector stack.
+ */
+protected void pushOnSelectorStack(int selectorIdPtr) {
+ if (this.invocationPtr < -1) return;
+ try {
+ this.selectorStack[++this.invocationPtr] = selectorIdPtr;
+ } catch (IndexOutOfBoundsException e) {
+ int oldStackLength = this.selectorStack.length;
+ int oldSelectorStack[] = this.selectorStack;
+ this.selectorStack = new int[oldStackLength + StackIncrement];
+ System.arraycopy(oldSelectorStack, 0, this.selectorStack, 0, oldStackLength);
+ this.selectorStack[this.invocationPtr] = selectorIdPtr;
+ }
+}
+public void reset(){
+ this.flushAssistState();
+}
+/*
+ * Reset context so as to resume to regular parse loop
+ */
+protected void resetStacks() {
+ super.resetStacks();
+ this.inFieldInitializationStack[this.inFieldInitializationPtr = 0] = false;
+ this.inMethodStack[this.inMethodPtr = 0] = false;
+}
+/*
+ * 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() {
+
+ // reset internal stacks
+ astPtr = -1;
+ astLengthPtr = -1;
+ expressionPtr = -1;
+ expressionLengthPtr = -1;
+ identifierPtr = -1;
+ identifierLengthPtr = -1;
+ intPtr = -1;
+ dimensions = 0 ;
+ recoveredStaticInitializerStart = 0;
+
+ // if in diet mode, reset the diet counter because we're going to restart outside an initializer.
+ if (diet) dietInt = 0;
+
+ /* attempt to move checkpoint location */
+ if (!this.moveRecoveryCheckpoint()) return false;
+
+ initInMethodAndInFieldInitializationStack(currentElement);
+
+ // only look for headers
+ if (referenceContext instanceof CompilationUnitDeclaration
+ || this.assistNode != null){
+
+ if(inMethodStack[inMethodPtr] &&
+ insideFieldInitialization() &&
+ this.assistNode == null
+ ){
+ this.prepareForBlockStatements();
+ goForBlockStatementsOrMethodHeaders();
+ } else {
+ nestedMethod[nestedType = 0] = 0;
+ variablesCounter[nestedType] = 0;
+ realBlockStack[realBlockPtr = 0] = 0;
+ goForHeaders();
+ diet = true; // passed this point, will not consider method bodies
+ }
+ return true;
+ }
+ if (referenceContext instanceof AbstractMethodDeclaration
+ || referenceContext instanceof TypeDeclaration){
+
+ if (currentElement instanceof RecoveredType){
+ nestedMethod[nestedType = 0] = 0;
+ variablesCounter[nestedType] = 0;
+ realBlockStack[realBlockPtr = 0] = 0;
+ goForHeaders();
+ } else {
+ this.prepareForBlockStatements();
+ goForBlockStatementsOrMethodHeaders();
+ }
+ return true;
+ }
+ // does not know how to restart
+ return false;
+}
+public abstract void setAssistIdentifier(char[] assistIdent);
+/**
+ * If the given ast node is inside an explicit constructor call
+ * then wrap it with a fake constructor call.
+ * Returns the wrapped completion node or the completion node itself.
+ */
+protected AstNode wrapWithExplicitConstructorCallIfNeeded(AstNode ast) {
+ int selector;
+ if (ast != null && this.invocationPtr >= 0 && ast instanceof Expression &&
+ (((selector = this.selectorStack[this.invocationPtr]) == THIS_CONSTRUCTOR) ||
+ (selector == SUPER_CONSTRUCTOR))) {
+ ExplicitConstructorCall call = new ExplicitConstructorCall(
+ (selector == THIS_CONSTRUCTOR) ?
+ ExplicitConstructorCall.This :
+ ExplicitConstructorCall.Super
+ );
+ call.arguments = new Expression[] {(Expression)ast};
+ call.sourceStart = ast.sourceStart;
+ call.sourceEnd = ast.sourceEnd;
+ return call;
+ } else {
+ return ast;
+ }
+}
+}