1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v0.5 
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v05.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  ******************************************************************************/
 
  11 package net.sourceforge.phpdt.internal.codeassist.complete;
 
  14  * Parser able to build specific completion parse nodes, given a cursorLocation.
 
  16  * Cursor location denotes the position of the last character behind which completion
 
  18  *  -1 means completion at the very beginning of the source
 
  19  *      0  means completion behind the first character
 
  20  *  n  means completion behind the n-th character
 
  22 import net.sourceforge.phpdt.internal.compiler.*;
 
  23 import net.sourceforge.phpdt.internal.compiler.env.*;
 
  25 import net.sourceforge.phpdt.internal.compiler.ast.*;
 
  26 import net.sourceforge.phpdt.internal.compiler.parser.*;
 
  27 import net.sourceforge.phpdt.internal.compiler.problem.*;
 
  28 import net.sourceforge.phpdt.internal.codeassist.impl.*;
 
  30 public class CompletionParser extends AssistParser {
 
  34         public int cursorLocation;
 
  35         public char[][] labels; // the visible labels up to the cursor location
 
  36         public AstNode assistNodeParent; // the parent node of assist node
 
  37         /* the following fields are internal flags */
 
  39         boolean betweenNewAndLeftBraket; // whether we are between the keyword 'new' and the following left braket, ie. '[', '(' or '{'
 
  40         boolean betweenCatchAndRightParen; // whether we are between the keyword 'catch' and the following ')'
 
  41         boolean completionBehindDot; // true when completion identifier immediately follows a dot
 
  43         boolean nextTypeReferenceIsClass;
 
  44         boolean nextTypeReferenceIsException;
 
  45         boolean nextTypeReferenceIsInterface;
 
  48         int throwBracketDepth;
 
  50         // the stacks of types and qualifiers for invocations (ie. method invocations, allocation expressions and
 
  51         // explicit constructor invocations). They use the same stack pointer as the selector stack (ie. invocationPtr)
 
  52         // the invocation type stack contains one of the invocation type constants below
 
  53         // the qualifier stack contains pointers to the expression stack or -1 if there is no qualifier
 
  54         // (a qualifier is the expression that qualifies a 'new', a 'super' constructor or a 'this' constructor
 
  55         //  or it is the receiver of a message send)
 
  56         int[] invocationTypeStack = new int[StackIncrement];
 
  57         int[] qualifierStack = new int[StackIncrement];
 
  59         // invocation type constants
 
  60         static final int EXPLICIT_RECEIVER = 0;
 
  61         static final int NO_RECEIVER = -1;
 
  62         static final int SUPER_RECEIVER = -2;
 
  63         static final int NAME_RECEIVER = -3;
 
  64         static final int ALLOCATION = -4;
 
  65         static final int QUALIFIED_ALLOCATION = -5;
 
  67         // the type of the current invocation (one of the invocation type constants)
 
  70         // a pointer in the expression stack to the qualifier of a invocation
 
  73         // a stack of label counters
 
  74         // a new counter is pushed on the stack each time when a method (or a constructor) is entered, 
 
  75         // it is poped when the method (or constructor) is exited,
 
  76         // it is incremented when a new label is defined
 
  78         int[] labelCounterStack = new int[StackIncrement];
 
  80         // a stack of invocationPtr: contains the first invocationPtr of a block
 
  81         // the current invocationPtr+1 is pushed when a block is entered
 
  82         // it is poped when a block is exited 
 
  83         int blockInvocationPtr;
 
  84         int[] blockInvocationStack = new int[StackIncrement];
 
  86         // last modifiers info
 
  87         int lastModifiers = AccDefault;
 
  88         int lastModifiersStart = -1;
 
  90 public CompletionParser(ProblemReporter problemReporter, boolean assertMode) {
 
  91         super(problemReporter, assertMode);
 
  93 public char[] assistIdentifier(){
 
  94         return ((CompletionScanner)scanner).completionIdentifier;
 
  96 protected void attachOrphanCompletionNode(){
 
  97         if (this.isOrphanCompletionNode) {
 
  98                 AstNode orphan = this.assistNode;
 
  99                 this.isOrphanCompletionNode = false;
 
 101                 /* if in context of a type, then persists the identifier into a fake field return type */
 
 102                 if (currentElement instanceof RecoveredType){
 
 103                         RecoveredType recoveredType = (RecoveredType)currentElement;
 
 104                         /* filter out cases where scanner is still inside type header */
 
 105                         if (recoveredType.foundOpeningBrace) {
 
 106                                 /* generate a pseudo field with a completion on type reference */       
 
 107                                 if (orphan instanceof TypeReference){
 
 108                                         CompletionOnFieldType fieldDeclaration = new CompletionOnFieldType((TypeReference)orphan, false);
 
 110                                         // retrieve available modifiers if any
 
 111                                         if (intPtr >= 2 && intStack[intPtr-1] == this.lastModifiersStart && intStack[intPtr-2] == this.lastModifiers){
 
 112                                                 fieldDeclaration.modifiersSourceStart = intStack[intPtr-1];
 
 113                                                 fieldDeclaration.modifiers = intStack[intPtr-2];
 
 116                                         currentElement = currentElement.add(fieldDeclaration, 0);
 
 121                 /* if in context of a method, persists if inside arguments as a type */
 
 122                 if (currentElement instanceof RecoveredMethod){
 
 123                         RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement;
 
 124                         /* only consider if inside method header */
 
 125                         if (!recoveredMethod.foundOpeningBrace) {
 
 126                                 //if (rParenPos < lParenPos){ // inside arguments
 
 127                                 if (orphan instanceof TypeReference){
 
 128                                         currentElement = currentElement.parent.add(
 
 129                                                 new CompletionOnFieldType((TypeReference)orphan, true), 0);
 
 135                 // add the completion node to the method declaration or constructor declaration
 
 136                 if (orphan instanceof Statement) {
 
 137                         /* check for completion at the beginning of method body
 
 138                                 behind an invalid signature
 
 140                         RecoveredMethod method = currentElement.enclosingMethod();
 
 142                                 AbstractMethodDeclaration methodDecl = method.methodDeclaration;
 
 143                                 if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace
 
 144                                         && (scanner.getLineNumber(orphan.sourceStart) == scanner.getLineNumber(methodDecl.sourceEnd))){
 
 148                         // add the completion node as a statement to the list of block statements
 
 149                         currentElement = currentElement.add((Statement)orphan, 0);
 
 154         // the following code applies only in methods, constructors or initializers
 
 155         if ((!this.inMethodStack[this.inMethodPtr] && !this.inFieldInitializationStack[this.inFieldInitializationPtr])) { 
 
 159         // push top expression on ast stack if it contains the completion node
 
 160         Expression expression;
 
 161         if (this.expressionPtr > -1 && containsCompletionNode(expression = this.expressionStack[this.expressionPtr])) {
 
 162                 /* check for completion at the beginning of method body
 
 163                         behind an invalid signature
 
 165                 RecoveredMethod method = currentElement.enclosingMethod();
 
 167                         AbstractMethodDeclaration methodDecl = method.methodDeclaration;
 
 168                         if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace
 
 169                                 && (scanner.getLineNumber(expression.sourceStart) == scanner.getLineNumber(methodDecl.sourceEnd))){
 
 173                 if (expression instanceof AllocationExpression) {
 
 174                         // keep the context if it is an allocation expression
 
 175                         Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(expression);
 
 176                         currentElement = currentElement.add(statement, 0);
 
 178                         Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(this.assistNode);
 
 179                         currentElement = currentElement.add(statement, 0);
 
 183 public int bodyEnd(AbstractMethodDeclaration method){
 
 184         return cursorLocation;
 
 186 public int bodyEnd(Initializer initializer){
 
 187         return cursorLocation;
 
 190  * Checks if the completion is on the exception type of a catch clause.
 
 191  * Returns whether we found a completion node.
 
 193 private boolean checkCatchClause() {
 
 194         if (this.betweenCatchAndRightParen && this.identifierPtr > -1) { 
 
 195                 // NB: if the cursor is on the variable, then it has been reduced (so identifierPtr is -1), 
 
 196                 //     thus this can only be a completion on the type of the catch clause
 
 197                 this.assistNode = getTypeReference(0);
 
 198                 this.lastCheckPoint = this.assistNode.sourceEnd + 1;
 
 199                 this.isOrphanCompletionNode = true;
 
 205  * Checks if the completion is on the type following a 'new'.
 
 206  * Returns whether we found a completion node.
 
 208 private boolean checkClassInstanceCreation() {
 
 209         if (this.betweenNewAndLeftBraket) {
 
 210                 // completion on type inside an allocation expression
 
 212                 if(this.throwBracketDepth != -1 && this.throwBracketDepth == this.bracketDepth) {
 
 213                         this.nextTypeReferenceIsException = true;       
 
 215                 TypeReference type = getTypeReference(0);
 
 216                 this.nextTypeReferenceIsException = false;
 
 217                 this.assistNode = type;
 
 218                 this.lastCheckPoint = type.sourceEnd + 1;
 
 219                 if (this.invocationType == ALLOCATION) {
 
 220                         // non qualified allocation expression
 
 221                         AllocationExpression allocExpr = new AllocationExpression();
 
 222                         allocExpr.type = type;
 
 223                         allocExpr.sourceStart = type.sourceStart;
 
 224                         allocExpr.sourceEnd = type.sourceEnd;
 
 225                         pushOnExpressionStack(allocExpr);
 
 226                         this.isOrphanCompletionNode = false;
 
 228                         // qualified allocation expression
 
 229                         QualifiedAllocationExpression allocExpr = new QualifiedAllocationExpression();
 
 230                         allocExpr.type = type;
 
 231                         allocExpr.enclosingInstance = this.expressionStack[this.qualifier];
 
 232                         allocExpr.sourceStart = this.intStack[this.intPtr--];
 
 233                         allocExpr.sourceEnd = type.sourceEnd;
 
 234                         this.expressionStack[this.qualifier] = allocExpr; // attach it now (it replaces the qualifier expression)
 
 235                         this.isOrphanCompletionNode = false;
 
 242  * Checks if the completion is on the dot following an array type,
 
 243  * a primitive type or an primitive array type.
 
 244  * Returns whether we found a completion node.
 
 246 private boolean checkClassLiteralAccess() {
 
 247         if (this.identifierLengthPtr >= 1 && this.previousToken == TokenNameDOT) { // (NB: the top id length is 1 and it is for the completion identifier)
 
 249                 // if the penultimate id length is negative, 
 
 250                 // the completion is after a primitive type or a primitive array type
 
 251                 if ((length = this.identifierLengthStack[this.identifierLengthPtr-1]) < 0) {
 
 252                         // build the primitive type node
 
 253                         int dim = this.isAfterArrayType() ? this.intStack[this.intPtr--] : 0;
 
 254                         SingleTypeReference typeRef = (SingleTypeReference)TypeReference.baseTypeReference(-length, dim);
 
 255                         typeRef.sourceStart = this.intStack[this.intPtr--];
 
 257                                 typeRef.sourceEnd = this.intStack[this.intPtr--];
 
 260                                 typeRef.sourceEnd = this.endPosition;
 
 262                         //typeRef.sourceEnd = typeRef.sourceStart + typeRef.token.length; // NB: It's ok to use the length of the token since it doesn't contain any unicode
 
 264                         // find the completion identifier and its source positions
 
 265                         char[] source = identifierStack[identifierPtr];
 
 266                         long pos = this.identifierPositionStack[this.identifierPtr--];
 
 267                         this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one)
 
 269                         // build the completion on class literal access node
 
 270                         CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef);
 
 271                         access.completionIdentifier = source;
 
 272                         this.identifierLengthPtr--; // pop the length that was used to say it is a primitive type
 
 273                         this.assistNode = access;
 
 274                         this.isOrphanCompletionNode = true;
 
 278                 // if the completion is after a regular array type
 
 279                 if (isAfterArrayType()) {
 
 280                         // find the completion identifier and its source positions
 
 281                         char[] source = identifierStack[identifierPtr];
 
 282                         long pos = this.identifierPositionStack[this.identifierPtr--];
 
 283                         this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one)
 
 285                         // get the type reference
 
 286                         TypeReference typeRef = getTypeReference(this.intPtr--);
 
 288                         // build the completion on class literal access node
 
 289                         CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef);
 
 290                         access.completionIdentifier = source;
 
 291                         this.assistNode = access;
 
 292                         this.isOrphanCompletionNode = true;
 
 300  * Checks if the completion is inside a method invocation or a constructor invocation.
 
 301  * Returns whether we found a completion node.
 
 303 private boolean checkInvocation() {
 
 304         Expression topExpression = this.expressionPtr >= 0 ? 
 
 305                 this.expressionStack[this.expressionPtr] :
 
 307         boolean isEmptyNameCompletion = false;
 
 308         boolean isEmptyAssistIdentifier = false;
 
 309         int startInvocationPtr = this.blockInvocationPtr >= 0 ? this.blockInvocationStack[this.blockInvocationPtr] : 0;
 
 310         if (this.invocationPtr >= startInvocationPtr
 
 311                 && ((isEmptyNameCompletion = topExpression == this.assistNode && this.isEmptyNameCompletion()) // eg. it is something like "this.fred([cursor]" but it is not something like "this.fred(1 + [cursor]"
 
 312                         || (isEmptyAssistIdentifier = this.indexOfAssistIdentifier() >= 0 && this.identifierStack[this.identifierPtr].length == 0))) { // eg. it is something like "this.fred(1 [cursor]"
 
 314                 // pop empty name completion
 
 315                 if (isEmptyNameCompletion) {
 
 316                         this.expressionPtr--;
 
 317                         this.expressionLengthStack[this.expressionLengthPtr]--;
 
 318                 } else if (isEmptyAssistIdentifier) {
 
 319                         this.identifierPtr--;
 
 320                         this.identifierLengthPtr--;
 
 323                 // find receiver and qualifier
 
 324                 int invocationType = this.invocationTypeStack[this.invocationPtr];
 
 325                 int qualifierExprPtr = this.qualifierStack[this.invocationPtr];
 
 328                 int numArgs = this.expressionPtr - qualifierExprPtr;
 
 329                 int argStart = qualifierExprPtr + 1;
 
 330                 Expression[] arguments = null;
 
 332                         // remember the arguments
 
 333                         arguments = new Expression[numArgs];
 
 334                         System.arraycopy(this.expressionStack, argStart, arguments, 0, numArgs);
 
 336                         // consume the expression arguments
 
 337                         this.expressionPtr -= numArgs;
 
 340                                 count -= this.expressionLengthStack[this.expressionLengthPtr--];
 
 345                 if (invocationType != ALLOCATION && invocationType != QUALIFIED_ALLOCATION) {
 
 346                         // creates completion on message send   
 
 347                         CompletionOnMessageSend messageSend = new CompletionOnMessageSend();
 
 348                         messageSend.arguments = arguments;
 
 349                         switch (invocationType) {
 
 352                                         messageSend.receiver = ThisReference.ThisImplicit;
 
 355                                         // remove special flags for primitive types
 
 356                                         while (this.identifierLengthPtr >= 0 && this.identifierLengthStack[this.identifierLengthPtr] < 0) {
 
 357                                                 this.identifierLengthPtr--;
 
 361                                         this.identifierPtr--; 
 
 362                                         this.identifierLengthStack[this.identifierLengthPtr]--;
 
 363                                         // consume the receiver
 
 364                                         messageSend.receiver = this.getUnspecifiedReference();
 
 367                                         messageSend.receiver = SuperReference.Super;
 
 369                                 case EXPLICIT_RECEIVER:
 
 370                                         messageSend.receiver = this.expressionStack[qualifierExprPtr];
 
 374                         int selectorPtr = this.selectorStack[this.invocationPtr];
 
 375                         messageSend.selector = this.identifierStack[selectorPtr];
 
 377                         if (this.identifierLengthPtr >=0 && this.identifierLengthStack[this.identifierLengthPtr] == 1) {
 
 378                                 this.identifierPtr--; 
 
 379                                 this.identifierLengthPtr--;
 
 382                         // the entire message may be replaced in case qualification is needed
 
 383                         messageSend.sourceStart = (int)(this.identifierPositionStack[selectorPtr] >> 32); //this.cursorLocation + 1;
 
 384                         messageSend.sourceEnd = this.cursorLocation;
 
 386                         // remember the message send as an orphan completion node
 
 387                         this.assistNode = messageSend;
 
 388                         this.lastCheckPoint = messageSend.sourceEnd + 1;
 
 389                         this.isOrphanCompletionNode = true;
 
 392                         int selectorPtr = this.selectorStack[this.invocationPtr];
 
 393                         if (selectorPtr == THIS_CONSTRUCTOR || selectorPtr == SUPER_CONSTRUCTOR) {
 
 394                                 // creates an explicit constructor call
 
 395                                 CompletionOnExplicitConstructorCall call = new CompletionOnExplicitConstructorCall(
 
 396                                         (selectorPtr == THIS_CONSTRUCTOR) ? ExplicitConstructorCall.This : ExplicitConstructorCall.Super);
 
 397                                 call.arguments = arguments;
 
 398                                 if (invocationType == QUALIFIED_ALLOCATION) {
 
 399                                         call.qualification = this.expressionStack[qualifierExprPtr];
 
 402                                 // no source is going to be replaced
 
 403                                 call.sourceStart = this.cursorLocation + 1;
 
 404                                 call.sourceEnd = this.cursorLocation;
 
 406                                 // remember the explicit constructor call as an orphan completion node
 
 407                                 this.assistNode = call;
 
 408                                 this.lastCheckPoint = call.sourceEnd + 1;
 
 409                                 this.isOrphanCompletionNode = true;
 
 412                                 // creates an allocation expression 
 
 413                                 CompletionOnQualifiedAllocationExpression allocExpr = new CompletionOnQualifiedAllocationExpression();
 
 414                                 allocExpr.arguments = arguments;
 
 415                                 allocExpr.type = super.getTypeReference(0); // we don't want a completion node here, so call super
 
 416                                 if (invocationType == QUALIFIED_ALLOCATION) {
 
 417                                         allocExpr.enclosingInstance = this.expressionStack[qualifierExprPtr];
 
 419                                 // no source is going to be replaced
 
 420                                 allocExpr.sourceStart = this.cursorLocation + 1;
 
 421                                 allocExpr.sourceEnd = this.cursorLocation;
 
 423                                 // remember the allocation expression as an orphan completion node
 
 424                                 this.assistNode = allocExpr;
 
 425                                 this.lastCheckPoint = allocExpr.sourceEnd + 1;
 
 426                                 this.isOrphanCompletionNode = true;
 
 434  * Checks if the completion is on a member access (ie. in an identifier following a dot).
 
 435  * Returns whether we found a completion node.
 
 437 private boolean checkMemberAccess() {
 
 438         if (this.previousToken == TokenNameDOT && this.qualifier > -1 && this.expressionPtr == this.qualifier) {
 
 439                 // the receiver is an expression
 
 440                 pushCompletionOnMemberAccessOnExpressionStack(false);
 
 446  * Checks if the completion is on a name reference.
 
 447  * Returns whether we found a completion node.
 
 449 private boolean checkNameCompletion() {
 
 451                 We didn't find any other completion, but the completion identifier is on the identifier stack,
 
 452                 so it can only be a completion on name.
 
 453                 Note that we allow the completion on a name even if nothing is expected (eg. foo() b[cursor] would
 
 454                 be a completion on 'b'). This policy gives more to the user than he/she would expect, but this 
 
 455                 simplifies the problem. To fix this, the recovery must be changed to work at a 'statement' granularity
 
 456                 instead of at the 'expression' granularity as it does right now.
 
 459         // NB: at this point the completion identifier is on the identifier stack
 
 460         this.assistNode = getUnspecifiedReferenceOptimized();
 
 461         this.lastCheckPoint = this.assistNode.sourceEnd + 1;
 
 462         this.isOrphanCompletionNode = true;
 
 466  * Checks if the completion is in the context of a method and on the type of one of its arguments
 
 467  * Returns whether we found a completion node.
 
 469 private boolean checkRecoveredMethod() {
 
 470         if (currentElement instanceof RecoveredMethod){
 
 471                 /* check if current awaiting identifier is the completion identifier */
 
 472                 if (this.indexOfAssistIdentifier() < 0) return false;
 
 474                 /* check if on line with an error already - to avoid completing inside 
 
 475                         illegal type names e.g.  int[<cursor> */
 
 476                 if (lastErrorEndPosition <= cursorLocation+1
 
 477                         && scanner.getLineNumber(lastErrorEndPosition) 
 
 478                                 == scanner.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart)){
 
 481                 RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement;
 
 482                 /* only consider if inside method header */
 
 483                 if (!recoveredMethod.foundOpeningBrace
 
 484                         && lastIgnoredToken == -1) {
 
 485                         //if (rParenPos < lParenPos){ // inside arguments
 
 486                         this.assistNode = this.getTypeReference(0);
 
 487                         this.lastCheckPoint = this.assistNode.sourceEnd + 1;
 
 488                         this.isOrphanCompletionNode = true;
 
 495  * Checks if the completion is in the context of a type and on a type reference in this type.
 
 496  * Persists the identifier into a fake field return type
 
 497  * Returns whether we found a completion node.
 
 499 private boolean checkRecoveredType() {
 
 500         if (currentElement instanceof RecoveredType){
 
 501                 /* check if current awaiting identifier is the completion identifier */
 
 502                 if (this.indexOfAssistIdentifier() < 0) return false;
 
 504                 /* check if on line with an error already - to avoid completing inside 
 
 505                         illegal type names e.g.  int[<cursor> */
 
 506                 if ((lastErrorEndPosition <= cursorLocation+1)
 
 507                         && scanner.getLineNumber(lastErrorEndPosition) 
 
 508                                 == scanner.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart)){
 
 511                 RecoveredType recoveredType = (RecoveredType)currentElement;
 
 512                 /* filter out cases where scanner is still inside type header */
 
 513                 if (recoveredType.foundOpeningBrace) {
 
 514                         this.assistNode = this.getTypeReference(0);
 
 515                         this.lastCheckPoint = this.assistNode.sourceEnd + 1;
 
 516                         this.isOrphanCompletionNode = true;
 
 523  * Check whether about to shift beyond the completion token.
 
 524  * If so, depending on the context, a special node might need to be created
 
 525  * and attached to the existing recovered structure so as to be remember in the
 
 526  * resulting parsed structure.
 
 528 public void completionIdentifierCheck(){
 
 530         if (checkRecoveredType()) return;
 
 531         if (checkRecoveredMethod()) return;
 
 533         // if not in a method in non diet mode and if not inside a field initializer, only record references attached to types
 
 534         if (!(this.inMethodStack[this.inMethodPtr] && !this.diet)
 
 535                 && !insideFieldInitialization()) return; 
 
 538                 In some cases, the completion identifier may not have yet been consumed,
 
 540                 This is because the grammar does not allow any (empty) identifier to follow
 
 541                 a base type. We thus have to manually force the identifier to be consumed
 
 544         if (assistIdentifier() == null && this.currentToken == TokenNameIdentifier) { // Test below copied from CompletionScanner.getCurrentIdentifierSource()
 
 545                 if (cursorLocation < this.scanner.startPosition && this.scanner.currentPosition == this.scanner.startPosition){ // fake empty identifier got issued
 
 546                         this.pushIdentifier();                                  
 
 547                 } else if (cursorLocation+1 >= this.scanner.startPosition && cursorLocation < this.scanner.currentPosition){
 
 548                         this.pushIdentifier();
 
 552         // check for different scenarii
 
 554                 // no need to go further if we found a non empty completion node
 
 555                 // (we still need to store labels though)
 
 556                 if (this.assistNode != null) {
 
 557                         // however inside an invocation, the completion identifier may already have been consumed into an empty name 
 
 558                         // completion, so this check should be before we check that we are at the cursor location
 
 559                         if (!isEmptyNameCompletion() || checkInvocation()) return;
 
 562                 // no need to check further if we are not at the cursor location
 
 563                 if (this.indexOfAssistIdentifier() < 0) return;
 
 565                 if (checkClassInstanceCreation()) return;
 
 566                 if (checkCatchClause()) return;
 
 567                 if (checkMemberAccess()) return;
 
 568                 if (checkClassLiteralAccess()) return;
 
 570                 // if the completion was not on an empty name, it can still be inside an invocation (eg. this.fred("abc"[cursor])
 
 571                 // (NB: Put this check before checkNameCompletion() because the selector of the invocation can be on the identifier stack)
 
 572                 if (checkInvocation()) return;
 
 574                 if (checkNameCompletion()) return;
 
 576                 storeLabelsIfNeeded();
 
 579 protected void consumeCaseLabel() {
 
 580         Expression caseExpression = this.expressionStack[this.expressionPtr];
 
 581         if (caseExpression instanceof SingleNameReference || caseExpression instanceof QualifiedNameReference) {
 
 582                 // label counter was wrongly incremented in consumeToken
 
 583                 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
 
 585         super.consumeCaseLabel();
 
 587 protected void consumeClassHeaderExtends() {
 
 588         this.nextTypeReferenceIsClass = true;
 
 589         super.consumeClassHeaderExtends();
 
 590         this.nextTypeReferenceIsClass = false;
 
 592 protected void consumeClassTypeElt() {
 
 593         this.nextTypeReferenceIsException = true;
 
 594         super.consumeClassTypeElt();
 
 595         this.nextTypeReferenceIsException = false;
 
 597 protected void consumeConditionalExpression(int op) {
 
 598         Expression valueIfTrue = this.expressionStack[this.expressionPtr - 1];
 
 599         if (valueIfTrue instanceof SingleNameReference || valueIfTrue instanceof QualifiedNameReference) {
 
 600                 // label counter was wrongly incremented in consumeToken
 
 601                 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
 
 603         super.consumeConditionalExpression(op);
 
 605 protected void consumeConstructorBody() {
 
 606         super.consumeConstructorBody();
 
 607         this.labelCounterPtr--;
 
 608         if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
 
 610 protected void consumeConstructorHeader() {
 
 611         super.consumeConstructorHeader();
 
 612         pushBlockInvocationPtr();
 
 614 protected void consumeConstructorHeaderName() {
 
 616         /* no need to take action if not inside assist identifiers */
 
 617         if (indexOfAssistIdentifier() < 0) {
 
 618                 super.consumeConstructorHeaderName();
 
 622         /* force to start recovering in order to get fake field behavior */
 
 623         if (currentElement == null){
 
 624                 this.hasReportedError = true; // do not report any error
 
 626         this.restartRecovery = true;
 
 628 protected void consumeEnterVariable() {
 
 630         identifierLengthPtr--;
 
 632         boolean isLocalDeclaration = nestedMethod[nestedType] != 0;
 
 633         int variableIndex = variablesCounter[nestedType];
 
 634         int extendedDimension = intStack[intPtr + 1];
 
 636         if(isLocalDeclaration || indexOfAssistIdentifier() < 0 || variableIndex != 0 || extendedDimension != 0) {
 
 638                 identifierLengthPtr++;
 
 639                 super.consumeEnterVariable();
 
 641                 restartRecovery = true;
 
 644                 if (currentElement != null) {
 
 645                         int nameSourceStart = (int)(identifierPositionStack[identifierPtr] >>> 32);
 
 648                         TypeReference type = getTypeReference(intStack[intPtr--]);
 
 651                         if (!(currentElement instanceof RecoveredType)
 
 652                                 && (currentToken == TokenNameDOT
 
 653                                         || (scanner.getLineNumber(type.sourceStart)
 
 654                                                         != scanner.getLineNumber(nameSourceStart)))){
 
 655                                 lastCheckPoint = nameSourceStart;
 
 656                                 restartRecovery = true;
 
 660                         FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false);
 
 661                         completionFieldDecl.modifiers = intStack[intPtr--];
 
 662                         assistNode = completionFieldDecl;
 
 663                         lastCheckPoint = type.sourceEnd + 1;
 
 664                         currentElement = currentElement.add(completionFieldDecl, 0);
 
 665                         lastIgnoredToken = -1;
 
 669 protected void consumeExitVariableWithInitialization() {
 
 670         super.consumeExitVariableWithInitialization();
 
 672         // does not keep the initialization if completion is not inside
 
 673         AbstractVariableDeclaration variable = (AbstractVariableDeclaration) astStack[astPtr];
 
 674         if (cursorLocation + 1 < variable.initialization.sourceStart ||
 
 675                 cursorLocation > variable.initialization.sourceEnd) {
 
 676                 variable.initialization = null;
 
 681  * Copy of code from superclass with the following change:
 
 682  * If the cursor location is on the field access, then create a 
 
 683  * CompletionOnMemberAccess instead.
 
 685 protected void consumeFieldAccess(boolean isSuperAccess) {
 
 686         // FieldAccess ::= Primary '.' 'Identifier'
 
 687         // FieldAccess ::= 'super' '.' 'Identifier'
 
 689         // potential receiver is being poped, so reset potential receiver
 
 690         this.invocationType = NO_RECEIVER;
 
 692         if (this.indexOfAssistIdentifier() < 0) {
 
 693                 super.consumeFieldAccess(isSuperAccess);
 
 695                 this.pushCompletionOnMemberAccessOnExpressionStack(isSuperAccess);
 
 699 protected void consumeFormalParameter() {
 
 700         if (this.indexOfAssistIdentifier() < 0) {
 
 701                 super.consumeFormalParameter();
 
 704                 identifierLengthPtr--;
 
 705                 char[] name = identifierStack[identifierPtr];
 
 706                 long namePositions = identifierPositionStack[identifierPtr--];
 
 707                 TypeReference type = getTypeReference(intStack[intPtr--] + intStack[intPtr--]);
 
 710                         new CompletionOnArgumentName(
 
 714                                 intStack[intPtr + 1] & ~AccDeprecated); // modifiers
 
 718                 this.lastCheckPoint = (int) namePositions;
 
 719                 isOrphanCompletionNode = true;
 
 721                 /* if incomplete method header, listLength counter will not have been reset,
 
 722                         indicating that some arguments are available on the stack */
 
 726 protected void consumeInterfaceType() {
 
 727         this.nextTypeReferenceIsInterface = true;
 
 728         super.consumeInterfaceType();
 
 729         this.nextTypeReferenceIsInterface = false;
 
 731 protected void consumeMethodHeaderName() {
 
 732         if(this.indexOfAssistIdentifier() < 0) {
 
 734                 identifierLengthPtr--;
 
 735                 if(this.indexOfAssistIdentifier() != 0) {
 
 737                         identifierLengthPtr++;
 
 738                         super.consumeMethodHeaderName();
 
 740                         restartRecovery = true;
 
 743                         if (currentElement != null) {
 
 745                                 char[] selector = identifierStack[identifierPtr + 1];
 
 746                                 long selectorSource = identifierPositionStack[identifierPtr + 1];
 
 749                                 TypeReference type = getTypeReference(intStack[intPtr--]);
 
 750                                 ((CompletionOnSingleTypeReference)type).isCompletionNode = false;
 
 752                                 int declarationSourceStart = intStack[intPtr--];
 
 753                                 int modifiers = intStack[intPtr--];
 
 755                                 if(scanner.getLineNumber(type.sourceStart) != scanner.getLineNumber((int) (selectorSource >>> 32))) {
 
 756                                         FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false);
 
 757                                         completionFieldDecl.modifiers = modifiers;
 
 758                                         assistNode = completionFieldDecl;
 
 759                                         lastCheckPoint = type.sourceEnd + 1;
 
 760                                         currentElement = currentElement.add(completionFieldDecl, 0);
 
 761                                         lastIgnoredToken = -1;
 
 763                                         CompletionOnMethodReturnType md = new CompletionOnMethodReturnType(type, this.compilationUnit.compilationResult);
 
 764                                         md.selector = selector;
 
 765                                         md.declarationSourceStart = declarationSourceStart;
 
 766                                         md.modifiers = modifiers;
 
 767                                         md.bodyStart = lParenPos+1;
 
 768                                         listLength = 0; // initialize listLength before reading parameters/throws
 
 770                                         this.lastCheckPoint = md.bodyStart;
 
 771                                         currentElement = currentElement.add(md, 0);
 
 772                                         lastIgnoredToken = -1;
 
 777                 // MethodHeaderName ::= Modifiersopt Type 'Identifier' '('
 
 778                 CompletionOnMethodName md = new CompletionOnMethodName(this.compilationUnit.compilationResult);
 
 781                 md.selector = identifierStack[identifierPtr];
 
 782                 long selectorSource = identifierPositionStack[identifierPtr--];
 
 784                 md.returnType = getTypeReference(intStack[intPtr--]);
 
 786                 md.declarationSourceStart = intStack[intPtr--];
 
 787                 md.modifiers = intStack[intPtr--];
 
 789                 //highlight starts at selector start
 
 790                 md.sourceStart = (int) (selectorSource >>> 32);
 
 791                 md.selectorEnd = (int) selectorSource;
 
 793                 md.sourceEnd = lParenPos;
 
 794                 md.bodyStart = lParenPos+1;
 
 795                 listLength = 0; // initialize listLength before reading parameters/throws
 
 797                 this.assistNode = md;   
 
 798                 this.lastCheckPoint = md.sourceEnd;
 
 800                 if (currentElement != null){
 
 801                         if (currentElement instanceof RecoveredType 
 
 802                                 //|| md.modifiers != 0
 
 803                                 || (scanner.getLineNumber(md.returnType.sourceStart)
 
 804                                                 == scanner.getLineNumber(md.sourceStart))){
 
 805                                 lastCheckPoint = md.bodyStart;
 
 806                                 currentElement = currentElement.add(md, 0);
 
 807                                 lastIgnoredToken = -1;
 
 809                                 lastCheckPoint = md.sourceStart;
 
 810                                 restartRecovery = true;
 
 817 protected void consumeMethodBody() {
 
 818         super.consumeMethodBody();
 
 819         this.labelCounterPtr--;
 
 820         if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
 
 823 protected void consumeMethodHeader() {
 
 824         super.consumeMethodHeader();
 
 825         pushBlockInvocationPtr();
 
 827 protected void consumeModifiers() {
 
 828         super.consumeModifiers();
 
 829         // save from stack values
 
 830         this.lastModifiersStart = intStack[intPtr];
 
 831         this.lastModifiers =    intStack[intPtr-1];
 
 833 protected void consumeNestedMethod() {
 
 834         super.consumeNestedMethod();
 
 835         this.pushNewLabelCounter();
 
 837 protected void consumeStatementLabel() {
 
 838         super.consumeStatementLabel();
 
 839         if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
 
 841 protected void consumeToken(int token) {
 
 842         int previous = this.previousToken;
 
 843         int previousIdentifierPtr = this.previousIdentifierPtr;
 
 844         super.consumeToken(token);
 
 846         // if in field initializer (directly or not), on the completion identifier and not in recovery mode yet
 
 847         // then position end of file at cursor location (so that we have the same behavior as
 
 849         if (token == TokenNameIdentifier
 
 850                         && this.identifierStack[this.identifierPtr] == assistIdentifier()
 
 851                         && this.currentElement == null
 
 852                         && this.insideFieldInitialization()) {
 
 853                 this.scanner.eofPosition = cursorLocation < Integer.MAX_VALUE ? cursorLocation+1 : cursorLocation;
 
 856         // if in a method or if in a field initializer 
 
 857         if (this.inMethodStack[this.inMethodPtr] || this.inFieldInitializationStack[this.inFieldInitializationPtr]) {
 
 861 //                                      case TokenNamethis: // eg. this[.]fred()
 
 862 //                                              this.invocationType = EXPLICIT_RECEIVER;
 
 864 //                                      case TokenNamesuper: // eg. super[.]fred()
 
 865 //                                              this.invocationType = SUPER_RECEIVER;
 
 867                                         case TokenNameIdentifier: // eg. bar[.]fred()
 
 868                                                 if (!this.betweenNewAndLeftBraket) { // eg. not new z.y[.]X()
 
 869                                                         if (this.identifierPtr != previousIdentifierPtr) { // if identifier has been consumed, eg. this.x[.]fred()
 
 870                                                                 this.invocationType = EXPLICIT_RECEIVER;
 
 872                                                                 this.invocationType = NAME_RECEIVER;
 
 878                         case TokenNameIdentifier:
 
 879                                 if (previous == TokenNameDOT) { // eg. foo().[fred]()
 
 880                                         // if current identifier is the empty completion one
 
 881                                         if (identifierStack[identifierPtr] == CompletionScanner.EmptyCompletionIdentifier){
 
 882                                                 this.completionBehindDot = true;
 
 884                                         if (this.invocationType != SUPER_RECEIVER // eg. not super.[fred]()
 
 885                                                 && this.invocationType != NAME_RECEIVER // eg. not bar.[fred]()
 
 886                                                 && this.invocationType != ALLOCATION // eg. not new foo.[Bar]()
 
 887                                                 && this.invocationType != QUALIFIED_ALLOCATION) { // eg. not fred().new foo.[Bar]()
 
 889                                                 this.invocationType = EXPLICIT_RECEIVER;
 
 890                                                 this.qualifier = this.expressionPtr;
 
 895                                 this.betweenNewAndLeftBraket = true;
 
 896                                 this.qualifier = this.expressionPtr; // NB: even if there is no qualification, set it to the expression ptr so that the number of arguments are correctly computed
 
 897                                 if (previous == TokenNameDOT) { // eg. fred().[new] X()
 
 898                                         this.invocationType = QUALIFIED_ALLOCATION;
 
 899                                 } else { // eg. [new] X()
 
 900                                         this.invocationType = ALLOCATION;
 
 903 //                      case TokenNamethis:
 
 904 //                              if (previous == TokenNameDOT) { // eg. fred().[this]()
 
 905 //                                      this.invocationType = QUALIFIED_ALLOCATION;
 
 906 //                                      this.qualifier = this.expressionPtr;
 
 909 //                      case TokenNamesuper:
 
 910 //                              if (previous == TokenNameDOT) { // eg. fred().[super]()
 
 911 //                                      this.invocationType = QUALIFIED_ALLOCATION;
 
 912 //                                      this.qualifier = this.expressionPtr;
 
 915 //                      case TokenNamecatch:
 
 916 //                              this.betweenCatchAndRightParen = true;
 
 918                         case TokenNameLPAREN:
 
 919                                 this.betweenNewAndLeftBraket = false;
 
 921                                 if (this.invocationType == NO_RECEIVER || this.invocationType == NAME_RECEIVER) {
 
 922                                         this.qualifier = this.expressionPtr; // remenber the last expression so that arguments are correctly computed
 
 925                                         case TokenNameIdentifier: // eg. fred[(]) or foo.fred[(])
 
 926                                                 this.pushOnInvocationStacks(this.invocationType, this.qualifier);
 
 927                                                 this.invocationType = NO_RECEIVER;
 
 929 //                                      case TokenNamethis: // explicit constructor invocation, eg. this[(]1, 2)
 
 930 //                                              this.pushOnInvocationStacks(
 
 931 //                                                      (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION, 
 
 933 //                                              this.invocationType = NO_RECEIVER;
 
 935 //                                      case TokenNamesuper: // explicit constructor invocation, eg. super[(]1, 2)
 
 936 //                                              this.pushOnInvocationStacks(
 
 937 //                                                      (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION, 
 
 939 //                                              this.invocationType = NO_RECEIVER;
 
 943                         case TokenNameLBRACE:
 
 944                                 this.betweenNewAndLeftBraket = false;
 
 946                                 this.pushBlockInvocationPtr();
 
 948                         case TokenNameLBRACKET:
 
 949                                 this.betweenNewAndLeftBraket = false;
 
 952                         case TokenNameRBRACE:
 
 954                                 if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
 
 956                         case TokenNameRBRACKET:
 
 959                         case TokenNameRPAREN:
 
 960                                 this.betweenCatchAndRightParen = false;
 
 964                                 if (previous == TokenNameIdentifier) {
 
 965                                         if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]++;
 
 968 //                      case TokenNamethrow:
 
 969 //                              this.throwBracketDepth= bracketDepth;
 
 975  * Return whether the given ast node contains the completion node.
 
 977 private boolean containsCompletionNode(AstNode ast) {
 
 978         if (this.assistNode == null || ast instanceof Literal) {
 
 981         if (this.assistNode == ast) {
 
 984         if (ast instanceof Reference || ast instanceof TypeReference) {
 
 985                 return ast == this.assistNode;
 
 987         if (ast instanceof Assignment) {
 
 988                 Assignment assign = (Assignment)ast;
 
 989                 return containsCompletionNode(assign.lhs) || containsCompletionNode(assign.expression);
 
 991         if (ast instanceof UnaryExpression) {
 
 992                 UnaryExpression unary = (UnaryExpression)ast;
 
 993                 return containsCompletionNode(unary.expression);
 
 995         if (ast instanceof BinaryExpression) {
 
 996                 BinaryExpression binary = (BinaryExpression)ast;
 
 997                 return containsCompletionNode(binary.left) || containsCompletionNode(binary.right);
 
 999         if (ast instanceof InstanceOfExpression) {
 
1000                 InstanceOfExpression instanceOfExpr = (InstanceOfExpression)ast;
 
1001                 return containsCompletionNode(instanceOfExpr.expression) || containsCompletionNode(instanceOfExpr.type);
 
1003         if (ast instanceof ConditionalExpression) {
 
1004                 ConditionalExpression conditional = (ConditionalExpression)ast;
 
1005                 return containsCompletionNode(conditional.condition) || containsCompletionNode(conditional.valueIfTrue) || containsCompletionNode(conditional.valueIfFalse);
 
1007         if (ast instanceof AllocationExpression) {
 
1008                 AllocationExpression alloc = (AllocationExpression)ast;
 
1009                 return containsCompletionNode(alloc.type);
 
1011         if (ast instanceof CastExpression) {
 
1012                 CastExpression cast = (CastExpression)ast;
 
1013                 return containsCompletionNode(cast.expression) || containsCompletionNode(cast.type);
 
1015         if (ast instanceof ExplicitConstructorCall) {
 
1016                 ExplicitConstructorCall call = (ExplicitConstructorCall)ast;
 
1017                 Expression[] arguments = call.arguments;
 
1018                 if (arguments != null) {
 
1019                         for (int i = 0; i < arguments.length; i++) {
 
1020                                 if (containsCompletionNode(arguments[i])) {
 
1029 public ImportReference createAssistImportReference(char[][] tokens, long[] positions){
 
1030         return new CompletionOnImportReference(tokens, positions);
 
1032 public ImportReference createAssistPackageReference(char[][] tokens, long[] positions){
 
1033         return new CompletionOnPackageReference(tokens, positions);
 
1035 public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions){
 
1036         return new CompletionOnQualifiedNameReference(
 
1037                                         previousIdentifiers, 
 
1041 public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] name, long[] positions){
 
1042         return this.betweenCatchAndRightParen || this.nextTypeReferenceIsException // check for exception scenario 
 
1043                                 ? new CompletionOnQualifiedExceptionReference(
 
1044                                         previousIdentifiers,  
 
1047                                 : this.nextTypeReferenceIsInterface
 
1048                                         ? new CompletionOnQualifiedInterfaceReference(
 
1049                                                 previousIdentifiers, 
 
1052                                         : this.nextTypeReferenceIsClass
 
1053                                                 ? new CompletionOnQualifiedClassReference(
 
1054                                                         previousIdentifiers, 
 
1057                                                 : new CompletionOnQualifiedTypeReference(
 
1058                                                         previousIdentifiers, 
 
1062 public NameReference createSingleAssistNameReference(char[] name, long position) {
 
1063         return new CompletionOnSingleNameReference(name, position);
 
1065 public TypeReference createSingleAssistTypeReference(char[] name, long position) {
 
1066         return this.betweenCatchAndRightParen || this.nextTypeReferenceIsException // check for exception scenario 
 
1067                 ? new CompletionOnExceptionReference(name, position) 
 
1068                 : this.nextTypeReferenceIsInterface
 
1069                         ? new CompletionOnInterfaceReference(name, position) 
 
1070                         : this.nextTypeReferenceIsClass
 
1071                                 ? new CompletionOnClassReference(name, position) 
 
1072                                 : new CompletionOnSingleTypeReference(name, position);
 
1074 public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
 
1076         this.cursorLocation = cursorLocation;
 
1077         CompletionScanner completionScanner = (CompletionScanner)this.scanner;
 
1078         completionScanner.completionIdentifier = null;
 
1079         completionScanner.cursorLocation = cursorLocation;
 
1080         return this.dietParse(sourceUnit, compilationResult);
 
1083  * Flush parser/scanner state regarding to code assist
 
1085 public void flushAssistState() {
 
1087         super.flushAssistState();
 
1088         this.isOrphanCompletionNode = false;
 
1089         CompletionScanner completionScanner = (CompletionScanner)this.scanner;
 
1090         completionScanner.completedIdentifierStart = 0;
 
1091         completionScanner.completedIdentifierEnd = -1;
 
1093 protected NameReference getUnspecifiedReferenceOptimized() {
 
1094         if (this.identifierLengthStack[this.identifierLengthPtr] > 1) { // reducing a qualified name
 
1095                 // potential receiver is being poped, so reset potential receiver
 
1096                 this.invocationType = NO_RECEIVER;
 
1098         return super.getUnspecifiedReferenceOptimized();
 
1101  * Return whether the given ast node has information interresting for code completion.
 
1103 private boolean hasCompletionInformation(AstNode ast) {
 
1105                 ast instanceof AbstractMethodDeclaration ||
 
1106                 ast instanceof AbstractVariableDeclaration ||
 
1107                 ast instanceof LabeledStatement ||
 
1108                 ast instanceof TypeDeclaration);
 
1110 public void initialize() {
 
1112         this.initializeForBlockStatements();
 
1113         this.labelCounterPtr = -1;
 
1116  * Initializes the state of the parser that is about to go for BlockStatements.
 
1118 private void initializeForBlockStatements() {
 
1119         this.previousToken = -1;
 
1120         this.previousIdentifierPtr = -1;
 
1121         this.completionBehindDot = false;
 
1122         this.betweenNewAndLeftBraket = false;
 
1123         this.betweenCatchAndRightParen = false;
 
1124         this.bracketDepth = 0;
 
1125         this.throwBracketDepth = -1;
 
1126         this.invocationType = NO_RECEIVER;
 
1127         this.qualifier = -1;
 
1128         this.blockInvocationPtr = -1;
 
1130 public void initializeScanner(){
 
1131         this.scanner = new CompletionScanner(this.assertMode);
 
1134  * Returns whether the completion is just after an array type
 
1135  * eg. String[].[cursor]
 
1137 private boolean isAfterArrayType() {
 
1138         // TBD: The following relies on the fact that array dimensions are small: it says that if the
 
1139         //      top of the intStack is less than 11, then it must be a dimension 
 
1140         //      (smallest position of array type in a compilation unit is 11 as in "class X{Y[]")
 
1141         if ((this.intPtr > -1) && (this.intStack[this.intPtr] < 11)) {
 
1146 private boolean isEmptyNameCompletion() {
 
1148                 this.assistNode != null && 
 
1149                 this.assistNode instanceof CompletionOnSingleNameReference &&
 
1150                 (((CompletionOnSingleNameReference)this.assistNode).token.length == 0);
 
1152 public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
 
1154         this.cursorLocation = cursorLocation;
 
1155         CompletionScanner completionScanner = (CompletionScanner)this.scanner;
 
1156         completionScanner.completionIdentifier = null;
 
1157         completionScanner.cursorLocation = cursorLocation;
 
1158         return this.parse(sourceUnit, compilationResult);
 
1161  * Prepares the state of the parser to go for BlockStatements.
 
1163 protected void prepareForBlockStatements() {
 
1164         super.prepareForBlockStatements();
 
1165         this.initializeForBlockStatements();
 
1167 protected void pushBlockInvocationPtr() {
 
1169                 this.blockInvocationStack[++this.blockInvocationPtr] = this.invocationPtr+1;
 
1170         } catch (IndexOutOfBoundsException e) {
 
1171                 int oldStackLength = this.blockInvocationStack.length;
 
1172                 int[] oldStack = this.blockInvocationStack;
 
1173                 this.blockInvocationStack = new int[oldStackLength + StackIncrement];
 
1174                 System.arraycopy(oldStack, 0, this.blockInvocationStack, 0, oldStackLength);
 
1175                 this.blockInvocationStack[this.blockInvocationPtr] = this.invocationPtr+1;
 
1179  * Creates a completion on member access node and push it
 
1180  * on the expression stack.
 
1182 private void pushCompletionOnMemberAccessOnExpressionStack(boolean isSuperAccess) {
 
1183         char[] source = identifierStack[identifierPtr];
 
1184         long pos = identifierPositionStack[identifierPtr--];
 
1185         CompletionOnMemberAccess fr = new CompletionOnMemberAccess(source, pos);
 
1186         this.assistNode = fr;
 
1187         this.lastCheckPoint = fr.sourceEnd + 1;
 
1188         identifierLengthPtr--;
 
1189         if (isSuperAccess) { //considerates the fieldReference beginning at the 'super' ....    
 
1190                 fr.sourceStart = intStack[intPtr--];
 
1191                 fr.receiver = new SuperReference(fr.sourceStart, endPosition);
 
1192                 pushOnExpressionStack(fr);
 
1193         } else { //optimize push/pop
 
1194                 if ((fr.receiver = expressionStack[expressionPtr]).isThis()) { //fieldreference begins at the this
 
1195                         fr.sourceStart = fr.receiver.sourceStart;
 
1197                 expressionStack[expressionPtr] = fr;
 
1200 protected void pushNewLabelCounter() {
 
1202                 this.labelCounterStack[++this.labelCounterPtr] = 0;
 
1203         } catch (IndexOutOfBoundsException e) {
 
1204                 int oldStackLength = this.labelCounterStack.length;
 
1205                 int[] oldStack = this.labelCounterStack;
 
1206                 this.labelCounterStack = new int[oldStackLength + StackIncrement];
 
1207                 System.arraycopy(oldStack, 0, this.labelCounterStack, 0, oldStackLength);
 
1208                 this.labelCounterStack[this.labelCounterPtr] = 0;
 
1212  * Pushes the given invocation type (one of the invocation type constants) on the invocation type stack,
 
1213  * and the given qualifier (an expression pointer to the expression stack) on the qualifier stack.
 
1215 protected void pushOnInvocationStacks(int invocationType, int qualifierExprPtr) {
 
1216         // NB: invocationPtr has already been incremented by a call to pushOnSelectorStack()
 
1218                 this.invocationTypeStack[this.invocationPtr] = invocationType;
 
1219                 this.qualifierStack[this.invocationPtr] = qualifierExprPtr;
 
1220         } catch (IndexOutOfBoundsException e) {
 
1221                 int oldStackLength = this.invocationTypeStack.length;
 
1222                 int oldInvocationTypeStack[] = this.invocationTypeStack;
 
1223                 int oldQualifierStack[] = this.qualifierStack;
 
1224                 this.invocationTypeStack = new int[oldStackLength + StackIncrement];
 
1225                 this.qualifierStack = new int[oldStackLength + StackIncrement];
 
1226                 System.arraycopy(oldInvocationTypeStack, 0, this.invocationTypeStack, 0, oldStackLength);
 
1227                 System.arraycopy(oldQualifierStack, 0, this.qualifierStack, 0, oldStackLength);
 
1228                 this.invocationTypeStack[this.invocationPtr] = invocationType;
 
1229                 this.qualifierStack[this.invocationPtr] = qualifierExprPtr;
 
1232 public void recordCompletionOnReference(){
 
1234         if (currentElement instanceof RecoveredType){
 
1235                 RecoveredType recoveredType = (RecoveredType)currentElement;
 
1237                 /* filter out cases where scanner is still inside type header */
 
1238                 if (!recoveredType.foundOpeningBrace) return;
 
1240                 /* generate a pseudo field with a completion on type reference */       
 
1242                         new CompletionOnFieldType(this.getTypeReference(0), false), 0);
 
1245         if (!diet) return; // only record references attached to types
 
1248 protected void reportSyntaxError(int act, int currentKind, int stateStackTop) {
 
1250         /* Intercept error state on EOF inside method bodies, due to 
 
1251            cursor location being used as an EOF position.
 
1253         if (!diet && currentToken == TokenNameEOF) return;
 
1254         super.reportSyntaxError(act, currentKind, stateStackTop);
 
1257  * Reset internal state after completion is over
 
1260 public void reset() {
 
1262         this.cursorLocation = 0;
 
1265  * Reset internal state after completion is over
 
1268 public void resetAfterCompletion() {
 
1269         this.cursorLocation = 0;
 
1270         this.flushAssistState();
 
1273  * Reset context so as to resume to regular parse loop
 
1274  * If unable to reset for resuming, answers false.
 
1276  * Move checkpoint location, reset internal stacks and
 
1277  * decide which grammar goal is activated.
 
1279 protected boolean resumeAfterRecovery() {
 
1280         if (this.assistNode != null) {
 
1281                 /* if reached [eof] inside method body, but still inside nested type,
 
1282                         or inside a field initializer, should continue in diet mode until 
 
1283                         the end of the method body or compilation unit */
 
1284                 if ((scanner.eofPosition == cursorLocation+1)
 
1285                         && (!(referenceContext instanceof CompilationUnitDeclaration) 
 
1286                                 || insideFieldInitialization())) {
 
1288                         /*      disabled since does not handle possible field/message refs, i.e. Obj[ASSIST HERE]ect.registerNatives()              
 
1289                         // consume extra tokens which were part of the qualified reference
 
1290                         //   so that the replaced source comprises them as well 
 
1291                         if (this.assistNode instanceof NameReference){
 
1292                                 int oldEof = scanner.eofPosition;
 
1293                                 scanner.eofPosition = currentElement.topElement().sourceEnd()+1;
 
1294                                 scanner.currentPosition = this.cursorLocation+1;
 
1298                                                 // first token might not have to be a dot
 
1299                                                 if (token >= 0 || !this.completionBehindDot){
 
1300                                                         if ((token = scanner.getNextToken()) != TokenNameDOT) break;
 
1302                                                 if ((token = scanner.getNextToken()) != TokenNameIdentifier) break;
 
1303                                                 this.assistNode.sourceEnd = scanner.currentPosition - 1;
 
1304                                         } while (token != TokenNameEOF);
 
1305                                 } catch (InvalidInputException e){
 
1307                                         scanner.eofPosition = oldEof;
 
1311                         /* restart in diet mode for finding sibling constructs */
 
1312                         if (currentElement.enclosingType() != null){
 
1313                                 lastCheckPoint = this.assistNode.sourceEnd+1;
 
1314                                 int end = currentElement.topElement().sourceEnd();
 
1315                                 scanner.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end;
 
1322         return super.resumeAfterRecovery();
 
1324 public void setAssistIdentifier(char[] assistIdent){
 
1325         ((CompletionScanner)scanner).completionIdentifier = assistIdent;
 
1328  * Stores the labels left on the identifier stack if they have not been stored yet.
 
1330 private void storeLabelsIfNeeded() {
 
1331 //      int counter = this.labelCounterPtr >= 0 ? this.labelCounterStack[this.labelCounterPtr] : 0;
 
1332 //      if (this.labels == null && this.identifierPtr >= 0) {
 
1333 //              this.labels = new char[counter][];
 
1334 //              System.arraycopy(this.identifierStack, this.identifierPtr - counter + 1, this.labels, 0, counter);
 
1336 //      this.identifierPtr -= counter;
 
1337 //      this.identifierLengthPtr -= counter; // labels have not been concatenated yet
 
1340  * Update recovery state based on current parser/scanner state
 
1342 protected void updateRecoveryState() {
 
1344         /* expose parser state to recovery state */
 
1345         currentElement.updateFromParserState();
 
1347         /* may be able to retrieve completionNode as an orphan, and then attach it */
 
1348         this.completionIdentifierCheck();
 
1349         this.attachOrphanCompletionNode();
 
1351         // if an assist node has been found and a recovered element exists,
 
1352         // mark enclosing blocks as to be preserved
 
1353         if (this.assistNode != null && this.currentElement != null) {
 
1354                 currentElement.preserveEnclosingBlocks();
 
1357         /* check and update recovered state based on current token,
 
1358                 this action is also performed when shifting token after recovery
 
1361         this.recoveryTokenCheck();
 
1364 protected LocalDeclaration createLocalDeclaration(Expression initialization, char[] name, int sourceStart, int sourceEnd) {
 
1365         if (this.indexOfAssistIdentifier() < 0) {
 
1366                 return super.createLocalDeclaration(initialization, name, sourceStart, sourceEnd);
 
1368                 CompletionOnLocalName local = new CompletionOnLocalName(initialization, name, sourceStart, sourceEnd);
 
1369                 this.assistNode = local;
 
1370                 this.lastCheckPoint = sourceEnd + 1;
 
1375 protected FieldDeclaration createFieldDeclaration(Expression initialization, char[] name, int sourceStart, int sourceEnd) {
 
1376         if (this.indexOfAssistIdentifier() < 0) {
 
1377                 return super.createFieldDeclaration(initialization, name, sourceStart, sourceEnd);
 
1379                 CompletionOnFieldName field = new CompletionOnFieldName(initialization, name, sourceStart, sourceEnd);
 
1380                 this.assistNode = field;
 
1381                 this.lastCheckPoint = sourceEnd + 1;