JDT codeassist module, nothing changed yet
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / codeassist / impl / AssistParser.java
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/codeassist/impl/AssistParser.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/codeassist/impl/AssistParser.java
new file mode 100644 (file)
index 0000000..b97679f
--- /dev/null
@@ -0,0 +1,1009 @@
+/*******************************************************************************
+ * 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;
+       }
+}
+}