JDT codeassist module, nothing changed yet
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / codeassist / select / SelectionParser.java
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/codeassist/select/SelectionParser.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/codeassist/select/SelectionParser.java
new file mode 100644 (file)
index 0000000..99f38b0
--- /dev/null
@@ -0,0 +1,686 @@
+/*******************************************************************************
+ * 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.select;
+
+/*
+ * 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 net.sourceforge.phpdt.internal.compiler.*;
+import net.sourceforge.phpdt.internal.compiler.env.*;
+
+import net.sourceforge.phpdt.internal.codeassist.impl.*;
+import net.sourceforge.phpdt.internal.compiler.ast.*;
+import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
+import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
+import net.sourceforge.phpdt.internal.compiler.parser.*;
+import net.sourceforge.phpdt.internal.compiler.problem.*;
+import net.sourceforge.phpdt.internal.compiler.util.CharOperation;
+
+public class SelectionParser extends AssistParser {
+
+       /* public fields */
+
+       public int selectionStart, selectionEnd;
+
+       public static final char[] SUPER = "super".toCharArray(); //$NON-NLS-1$
+       public static final char[] THIS = "this".toCharArray(); //$NON-NLS-1$
+       
+public SelectionParser(ProblemReporter problemReporter, boolean assertMode) {
+       super(problemReporter, assertMode);
+}
+public char[] assistIdentifier(){
+       return ((SelectionScanner)scanner).selectionIdentifier;
+}
+protected void attachOrphanCompletionNode(){
+       if (isOrphanCompletionNode){
+               AstNode orphan = this.assistNode;
+               isOrphanCompletionNode = false;
+               
+               
+               /* 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){
+                                       currentElement = currentElement.add(new SelectionOnFieldType((TypeReference)orphan), 0);
+                                       return;
+                               }
+                       }
+               }
+               
+               Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(orphan);
+               currentElement = currentElement.add(statement, 0);
+               currentToken = 0; // given we are not on an eof, we do not want side effects caused by looked-ahead token
+       }
+}
+
+private boolean checkRecoveredType() {
+       if (currentElement instanceof RecoveredType){
+               /* check if current awaiting identifier is the completion identifier */
+               if (this.indexOfAssistIdentifier() < 0) return false;
+
+               if ((lastErrorEndPosition >= selectionStart)
+                       && (lastErrorEndPosition <= selectionEnd+1)){
+                       return false;
+               }
+               RecoveredType recoveredType = (RecoveredType)currentElement;
+               /* filter out cases where scanner is still inside type header */
+               if (recoveredType.foundOpeningBrace) {
+                       this.assistNode = this.getTypeReference(0);
+                       this.lastCheckPoint = this.assistNode.sourceEnd + 1;
+                       this.isOrphanCompletionNode = true;
+                       return true;
+               }
+       }
+       return false;
+}
+protected void classInstanceCreation(boolean alwaysQualified) {
+       
+       // ClassInstanceCreationExpression ::= 'new' ClassType '(' ArgumentListopt ')' ClassBodyopt
+
+       // ClassBodyopt produces a null item on the astStak if it produces NO class body
+       // An empty class body produces a 0 on the length stack.....
+
+       int length;
+       if (((length = astLengthStack[astLengthPtr]) == 1)
+               && (astStack[astPtr] == null)) {
+
+               if (this.indexOfAssistIdentifier() < 0) {
+                       super.classInstanceCreation(alwaysQualified);
+                       return;
+               }
+               QualifiedAllocationExpression alloc;
+               astPtr--;
+               astLengthPtr--;
+               alloc = new SelectionOnQualifiedAllocationExpression();
+               alloc.sourceEnd = endPosition; //the position has been stored explicitly
+
+               if ((length = expressionLengthStack[expressionLengthPtr--]) != 0) {
+                       expressionPtr -= length;
+                       System.arraycopy(
+                               expressionStack, 
+                               expressionPtr + 1, 
+                               alloc.arguments = new Expression[length], 
+                               0, 
+                               length); 
+               }
+               // trick to avoid creating a selection on type reference
+               char [] oldIdent = this.assistIdentifier();
+               this.setAssistIdentifier(null);                 
+               alloc.type = getTypeReference(0);
+               this.setAssistIdentifier(oldIdent);
+               
+               //the default constructor with the correct number of argument
+               //will be created and added by the TC (see createsInternalConstructorWithBinding)
+               alloc.sourceStart = intStack[intPtr--];
+               pushOnExpressionStack(alloc);
+
+               this.assistNode = alloc;
+               this.lastCheckPoint = alloc.sourceEnd + 1;
+               if (!diet){
+                       this.restartRecovery    = true; // force to restart in recovery mode
+                       this.lastIgnoredToken = -1;     
+               }
+               this.isOrphanCompletionNode = true;
+       } else {
+               super.classInstanceCreation(alwaysQualified);
+       }
+}
+
+protected void consumeArrayCreationExpression() {
+       // ArrayCreationExpression ::= 'new' PrimitiveType DimWithOrWithOutExprs ArrayInitializeropt
+       // ArrayCreationExpression ::= 'new' ClassOrInterfaceType DimWithOrWithOutExprs ArrayInitializeropt
+
+       super.consumeArrayCreationExpression();
+
+       ArrayAllocationExpression alloc = (ArrayAllocationExpression)expressionStack[expressionPtr];
+       if (alloc.type == assistNode){
+               if (!diet){
+                       this.restartRecovery    = true; // force to restart in recovery mode
+                       this.lastIgnoredToken = -1;     
+               }
+               this.isOrphanCompletionNode = true;
+       }
+}
+protected void consumeEnterAnonymousClassBody() {
+       // EnterAnonymousClassBody ::= $empty
+
+       if (this.indexOfAssistIdentifier() < 0) {
+               super.consumeEnterAnonymousClassBody();
+               return;
+       }
+       QualifiedAllocationExpression alloc;
+       AnonymousLocalTypeDeclaration anonymousType = 
+               new AnonymousLocalTypeDeclaration(this.compilationUnit.compilationResult); 
+       alloc = 
+               anonymousType.allocation = new SelectionOnQualifiedAllocationExpression(anonymousType); 
+       markCurrentMethodWithLocalType();
+       pushOnAstStack(anonymousType);
+
+       alloc.sourceEnd = rParenPos; //the position has been stored explicitly
+       int argumentLength;
+       if ((argumentLength = expressionLengthStack[expressionLengthPtr--]) != 0) {
+               expressionPtr -= argumentLength;
+               System.arraycopy(
+                       expressionStack, 
+                       expressionPtr + 1, 
+                       alloc.arguments = new Expression[argumentLength], 
+                       0, 
+                       argumentLength); 
+       }
+       // trick to avoid creating a selection on type reference
+       char [] oldIdent = this.assistIdentifier();
+       this.setAssistIdentifier(null);                 
+       alloc.type = getTypeReference(0);
+       this.setAssistIdentifier(oldIdent);             
+
+       anonymousType.sourceEnd = alloc.sourceEnd;
+       //position at the type while it impacts the anonymous declaration
+       anonymousType.sourceStart = anonymousType.declarationSourceStart = alloc.type.sourceStart;
+       alloc.sourceStart = intStack[intPtr--];
+       pushOnExpressionStack(alloc);
+
+       assistNode = alloc;
+       this.lastCheckPoint = alloc.sourceEnd + 1;
+       if (!diet){
+               this.restartRecovery    = true; // force to restart in recovery mode
+               this.lastIgnoredToken = -1;     
+       }
+       this.isOrphanCompletionNode = true;
+               
+       anonymousType.bodyStart = scanner.currentPosition;      
+       listLength = 0; // will be updated when reading super-interfaces
+       // recovery
+       if (currentElement != null){ 
+               lastCheckPoint = anonymousType.bodyStart;
+               currentElement = currentElement.add(anonymousType, 0); // the recoveryTokenCheck will deal with the open brace
+               lastIgnoredToken = -1;          
+       }
+}
+protected void consumeEnterVariable() {
+       // EnterVariable ::= $empty
+       // do nothing by default
+
+       super.consumeEnterVariable();
+
+       AbstractVariableDeclaration variable = (AbstractVariableDeclaration) astStack[astPtr];
+       if (variable.type == assistNode){
+               if (!diet){
+                       this.restartRecovery    = true; // force to restart in recovery mode
+                       this.lastIgnoredToken = -1;     
+               }
+               isOrphanCompletionNode = false; // already attached inside variable decl
+       }
+}
+
+protected void consumeExitVariableWithInitialization() {
+       super.consumeExitVariableWithInitialization();
+       
+       // does not keep the initialization if selection is not inside
+       AbstractVariableDeclaration variable = (AbstractVariableDeclaration) astStack[astPtr];
+       int start = variable.initialization.sourceStart;
+       int end =  variable.initialization.sourceEnd;
+       if ((selectionStart < start) &&  (selectionEnd < start) ||
+               (selectionStart > end) && (selectionEnd > end)) {
+               variable.initialization = null;
+       }
+}
+
+protected void consumeFieldAccess(boolean isSuperAccess) {
+       // FieldAccess ::= Primary '.' 'Identifier'
+       // FieldAccess ::= 'super' '.' 'Identifier'
+
+       if (this.indexOfAssistIdentifier() < 0) {
+               super.consumeFieldAccess(isSuperAccess);
+               return;
+       } 
+       FieldReference fieldReference = 
+               new SelectionOnFieldReference(
+                       identifierStack[identifierPtr], 
+                       identifierPositionStack[identifierPtr--]);
+       identifierLengthPtr--;
+       if (isSuperAccess) { //considerates the fieldReferenceerence beginning at the 'super' ....      
+               fieldReference.sourceStart = intStack[intPtr--];
+               fieldReference.receiver = new SuperReference(fieldReference.sourceStart, endPosition);
+               pushOnExpressionStack(fieldReference);
+       } else { //optimize push/pop
+               if ((fieldReference.receiver = expressionStack[expressionPtr]).isThis()) { //fieldReferenceerence begins at the this
+                       fieldReference.sourceStart = fieldReference.receiver.sourceStart;
+               }
+               expressionStack[expressionPtr] = fieldReference;
+       }
+       assistNode = fieldReference;
+       this.lastCheckPoint = fieldReference.sourceEnd + 1;
+       if (!diet){
+               this.restartRecovery    = true; // force to restart in recovery mode
+               this.lastIgnoredToken = -1;
+       }
+       this.isOrphanCompletionNode = true;     
+}
+protected void consumeFormalParameter() {
+       if (this.indexOfAssistIdentifier() < 0) {
+               super.consumeFormalParameter();
+       } else {
+
+               identifierLengthPtr--;
+               char[] name = identifierStack[identifierPtr];
+               long namePositions = identifierPositionStack[identifierPtr--];
+               TypeReference type = getTypeReference(intStack[intPtr--] + intStack[intPtr--]);
+               intPtr -= 2;
+               Argument arg = 
+                       new SelectionOnArgumentName(
+                               name, 
+                               namePositions, 
+                               type, 
+                               intStack[intPtr + 1] & ~AccDeprecated); // modifiers
+               pushOnAstStack(arg);
+               
+               assistNode = arg;
+               this.lastCheckPoint = (int) namePositions;
+               isOrphanCompletionNode = true;
+               
+               if (!diet){
+                       this.restartRecovery    = true; // force to restart in recovery mode
+                       this.lastIgnoredToken = -1;     
+               }
+
+               /* if incomplete method header, listLength counter will not have been reset,
+                       indicating that some arguments are available on the stack */
+               listLength++;
+       }       
+}
+protected void consumeInstanceOfExpression(int op) {
+       if (indexOfAssistIdentifier() < 0) {
+               super.consumeInstanceOfExpression(op);
+       } else {
+               getTypeReference(intStack[intPtr--]);
+               this.isOrphanCompletionNode = true;
+               this.restartRecovery = true;
+               this.lastIgnoredToken = -1;
+       }
+}
+protected void consumeMethodInvocationName() {
+       // MethodInvocation ::= Name '(' ArgumentListopt ')'
+
+       // when the name is only an identifier...we have a message send to "this" (implicit)
+
+       char[] selector = identifierStack[identifierPtr];
+       int accessMode;
+       if(selector == this.assistIdentifier()) {
+               if(CharOperation.equals(selector, SUPER)) {
+                       accessMode = ExplicitConstructorCall.Super;
+               } else if(CharOperation.equals(selector, THIS)) {
+                       accessMode = ExplicitConstructorCall.This;
+               } else {
+                       super.consumeMethodInvocationName();
+                       return;
+               }
+       } else {
+               super.consumeMethodInvocationName();
+               return;
+       }
+       
+       final ExplicitConstructorCall constructorCall = new SelectionOnExplicitConstructorCall(accessMode);
+       constructorCall.sourceEnd = rParenPos;
+       constructorCall.sourceStart = (int) (identifierPositionStack[identifierPtr] >>> 32);
+       int length;
+       if ((length = expressionLengthStack[expressionLengthPtr--]) != 0) {
+               expressionPtr -= length;
+               System.arraycopy(expressionStack, expressionPtr + 1, constructorCall.arguments = new Expression[length], 0, length);
+       }
+
+       if (!diet){
+               pushOnAstStack(constructorCall);
+               this.restartRecovery    = true; // force to restart in recovery mode
+               this.lastIgnoredToken = -1;
+       } else {
+               pushOnExpressionStack(new Expression(){
+                       public TypeBinding resolveType(BlockScope scope) {
+                               constructorCall.resolve(scope);
+                               return null;
+                       }
+               });
+       }
+       this.assistNode = constructorCall;      
+       this.lastCheckPoint = constructorCall.sourceEnd + 1;
+       this.isOrphanCompletionNode = true;
+}
+protected void consumeMethodInvocationPrimary() {
+       //optimize the push/pop
+       //MethodInvocation ::= Primary '.' 'Identifier' '(' ArgumentListopt ')'
+
+       char[] selector = identifierStack[identifierPtr];
+       int accessMode;
+       if(selector == this.assistIdentifier()) {
+               if(CharOperation.equals(selector, SUPER)) {
+                       accessMode = ExplicitConstructorCall.Super;
+               } else if(CharOperation.equals(selector, THIS)) {
+                       accessMode = ExplicitConstructorCall.This;
+               } else {
+                       super.consumeMethodInvocationPrimary();
+                       return;
+               }
+       } else {
+               super.consumeMethodInvocationPrimary();
+               return;
+       }
+       
+       final ExplicitConstructorCall constructorCall = new SelectionOnExplicitConstructorCall(accessMode);
+       constructorCall.sourceEnd = rParenPos;
+       int length;
+       if ((length = expressionLengthStack[expressionLengthPtr--]) != 0) {
+               expressionPtr -= length;
+               System.arraycopy(expressionStack, expressionPtr + 1, constructorCall.arguments = new Expression[length], 0, length);
+       }
+       constructorCall.qualification = expressionStack[expressionPtr--];
+       constructorCall.sourceStart = constructorCall.qualification.sourceStart;
+       
+       if (!diet){
+               pushOnAstStack(constructorCall);
+               this.restartRecovery    = true; // force to restart in recovery mode
+               this.lastIgnoredToken = -1;
+       } else {
+               pushOnExpressionStack(new Expression(){
+                       public TypeBinding resolveType(BlockScope scope) {
+                               constructorCall.resolve(scope);
+                               return null;
+                       }
+               });
+       }
+       
+       this.assistNode = constructorCall;
+       this.lastCheckPoint = constructorCall.sourceEnd + 1;
+       this.isOrphanCompletionNode = true;
+}
+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 ImportReference createAssistImportReference(char[][] tokens, long[] positions){
+       return new SelectionOnImportReference(tokens, positions);
+}
+public ImportReference createAssistPackageReference(char[][] tokens, long[] positions){
+       return new SelectionOnPackageReference(tokens, positions);
+}
+protected LocalDeclaration createLocalDeclaration(Expression initialization,char[] name,int sourceStart,int sourceEnd) {
+       if (this.indexOfAssistIdentifier() < 0) {
+               return super.createLocalDeclaration(initialization, name, sourceStart, sourceEnd);
+       } else {
+               SelectionOnLocalName local = new SelectionOnLocalName(initialization, name, sourceStart, sourceEnd);
+               this.assistNode = local;
+               this.lastCheckPoint = sourceEnd + 1;
+               if (!diet){
+                       this.restartRecovery    = true; // force to restart in recovery mode
+                       this.lastIgnoredToken = -1;     
+               }
+               return local;
+       }
+}
+public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions){
+       return new SelectionOnQualifiedNameReference(
+                                       previousIdentifiers, 
+                                       name, 
+                                       positions);     
+}
+public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] name, long[] positions){
+       return new SelectionOnQualifiedTypeReference(
+                                       previousIdentifiers, 
+                                       name, 
+                                       positions);     
+}
+public NameReference createSingleAssistNameReference(char[] name, long position) {
+       return new SelectionOnSingleNameReference(name, position);
+}
+public TypeReference createSingleAssistTypeReference(char[] name, long position) {
+       return new SelectionOnSingleTypeReference(name, position);
+}
+public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int selectionStart, int selectionEnd) {
+
+       this.selectionStart = selectionStart;
+       this.selectionEnd = selectionEnd;       
+       SelectionScanner selectionScanner = (SelectionScanner)this.scanner;
+       selectionScanner.selectionIdentifier = null;
+       selectionScanner.selectionStart = selectionStart;
+       selectionScanner.selectionEnd = selectionEnd;   
+       return this.dietParse(sourceUnit, compilationResult);
+}
+protected NameReference getUnspecifiedReference() {
+       /* build a (unspecified) NameReference which may be qualified*/
+
+       int completionIndex;
+
+       /* no need to take action if not inside completed identifiers */
+       if ((completionIndex = indexOfAssistIdentifier()) < 0) {
+               return super.getUnspecifiedReference();
+       }
+
+       int length = identifierLengthStack[identifierLengthPtr];
+       if (CharOperation.equals(assistIdentifier(), SUPER)){
+               Reference reference;
+               if (completionIndex > 0){ // qualified super
+                       // discard 'super' from identifier stacks
+                       identifierLengthStack[identifierLengthPtr] = completionIndex;
+                       int ptr = identifierPtr -= (length - completionIndex);
+                       reference = 
+                               new SelectionOnQualifiedSuperReference(
+                                       getTypeReference(0), 
+                                       (int)(identifierPositionStack[ptr+1] >>> 32),
+                                       (int) identifierPositionStack[ptr+1]);
+               } else { // standard super
+                       identifierPtr -= length;
+                       identifierLengthPtr--;
+                       reference = new SelectionOnSuperReference((int)(identifierPositionStack[identifierPtr+1] >>> 32), (int) identifierPositionStack[identifierPtr+1]);
+               }
+               pushOnAstStack(reference);
+               this.assistNode = reference;    
+               this.lastCheckPoint = reference.sourceEnd + 1;
+               if (!diet || dietInt != 0){
+                       this.restartRecovery    = true; // force to restart in recovery mode
+                       this.lastIgnoredToken = -1;             
+               }
+               this.isOrphanCompletionNode = true;
+               return new SingleNameReference(new char[0], 0); // dummy reference
+       }
+       NameReference nameReference;
+       /* retrieve identifiers subset and whole positions, the completion node positions
+               should include the entire replaced source. */
+       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 */
+       if (completionIndex == 0) {
+               /* completion inside first identifier */
+               nameReference = this.createSingleAssistNameReference(assistIdentifier(), positions[0]);
+       } else {
+               /* completion inside subsequent identifier */
+               nameReference = this.createQualifiedAssistNameReference(subset, assistIdentifier(), positions);
+       }
+       assistNode = nameReference;
+       this.lastCheckPoint = nameReference.sourceEnd + 1;
+       if (!diet){
+               this.restartRecovery    = true; // force to restart in recovery mode
+               this.lastIgnoredToken = -1;     
+       }
+       this.isOrphanCompletionNode = true;
+       return nameReference;
+}
+/*
+ * 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 index = indexOfAssistIdentifier();
+       NameReference reference = super.getUnspecifiedReferenceOptimized();
+
+       if (index >= 0){
+               if (!diet){
+                       this.restartRecovery    = true; // force to restart in recovery mode
+                       this.lastIgnoredToken = -1;             
+               }
+               this.isOrphanCompletionNode = true;
+       }
+       return reference;
+}
+public void initializeScanner(){
+       this.scanner = new SelectionScanner(this.assertMode);
+}
+protected MessageSend newMessageSend() {
+       // '(' ArgumentListopt ')'
+       // the arguments are on the expression stack
+
+       char[] selector = identifierStack[identifierPtr];
+       if (selector != this.assistIdentifier()){
+               return super.newMessageSend();
+       }       
+       MessageSend messageSend = new SelectionOnMessageSend();
+       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    = true; // force to restart in recovery mode
+               this.lastIgnoredToken = -1;     
+       }
+       
+       this.isOrphanCompletionNode = true;
+       return messageSend;
+}
+public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int selectionStart, int selectionEnd) {
+
+       this.selectionStart = selectionStart;
+       this.selectionEnd = selectionEnd;       
+       SelectionScanner selectionScanner = (SelectionScanner)this.scanner;
+       selectionScanner.selectionIdentifier = null;
+       selectionScanner.selectionStart = selectionStart;
+       selectionScanner.selectionEnd = selectionEnd;   
+       return this.parse(sourceUnit, compilationResult);
+}
+/*
+ * 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 reached assist node inside method body, but still inside nested type,
+               should continue in diet mode until the end of the method body */
+       if (this.assistNode != null
+               && !(referenceContext instanceof CompilationUnitDeclaration)){
+               currentElement.preserveEnclosingBlocks();
+               if (currentElement.enclosingType() == null){
+                       this.resetStacks();
+                       return false;
+               }
+       }
+       return super.resumeAfterRecovery();                     
+}
+
+public void selectionIdentifierCheck(){
+       if (checkRecoveredType()) return;
+}
+public void setAssistIdentifier(char[] assistIdent){
+       ((SelectionScanner)scanner).selectionIdentifier = assistIdent;
+}
+/*
+ * 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.selectionIdentifierCheck();
+       this.attachOrphanCompletionNode();
+       
+       /* check and update recovered state based on current token,
+               this action is also performed when shifting token after recovery
+               got activated once. 
+       */
+       this.recoveryTokenCheck();
+}
+}