228c98c954082bfa3aa74429b175ecfc9044df01
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / codeassist / complete / CompletionParser.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.codeassist.complete;
12
13 /*
14  * Parser able to build specific completion parse nodes, given a cursorLocation.
15  *
16  * Cursor location denotes the position of the last character behind which completion
17  * got requested:
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
21  */
22 import net.sourceforge.phpdt.internal.compiler.*;
23 import net.sourceforge.phpdt.internal.compiler.env.*;
24
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.*;
29
30 public class CompletionParser extends AssistParser {
31
32         /* public fields */
33
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 */
38         
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
42         
43         boolean nextTypeReferenceIsClass;
44         boolean nextTypeReferenceIsException;
45         boolean nextTypeReferenceIsInterface;
46         
47         int bracketDepth;
48         int throwBracketDepth;
49
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];
58
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;
66
67         // the type of the current invocation (one of the invocation type constants)
68         int invocationType;
69
70         // a pointer in the expression stack to the qualifier of a invocation
71         int qualifier;
72
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
77         int labelCounterPtr;
78         int[] labelCounterStack = new int[StackIncrement];
79
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];
85         
86         // last modifiers info
87         int lastModifiers = AccDefault;
88         int lastModifiersStart = -1;
89         
90 public CompletionParser(ProblemReporter problemReporter, boolean assertMode) {
91         super(problemReporter, assertMode);
92 }
93 public char[] assistIdentifier(){
94         return ((CompletionScanner)scanner).completionIdentifier;
95 }
96 protected void attachOrphanCompletionNode(){
97         if (this.isOrphanCompletionNode) {
98                 AstNode orphan = this.assistNode;
99                 this.isOrphanCompletionNode = false;
100                 
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);
109
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];
114                                         }
115
116                                         currentElement = currentElement.add(fieldDeclaration, 0);
117                                         return;
118                                 }
119                         }
120                 }
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);
130                                         return;
131                                 }
132                         }
133                 }
134
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
139                          */
140                         RecoveredMethod method = currentElement.enclosingMethod();
141                         if (method != null){
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))){
145                                         return;
146                                 }
147                         }
148                         // add the completion node as a statement to the list of block statements
149                         currentElement = currentElement.add((Statement)orphan, 0);
150                         return;
151                 } 
152         }
153         
154         // the following code applies only in methods, constructors or initializers
155         if ((!this.inMethodStack[this.inMethodPtr] && !this.inFieldInitializationStack[this.inFieldInitializationPtr])) { 
156                 return;
157         }
158         
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
164                  */
165                 RecoveredMethod method = currentElement.enclosingMethod();
166                 if (method != null){
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))){
170                                 return;
171                         }
172                 }
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);
177                 } else {
178                         Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(this.assistNode);
179                         currentElement = currentElement.add(statement, 0);
180                 }
181         }
182 }
183 public int bodyEnd(AbstractMethodDeclaration method){
184         return cursorLocation;
185 }
186 public int bodyEnd(Initializer initializer){
187         return cursorLocation;
188 }
189 /**
190  * Checks if the completion is on the exception type of a catch clause.
191  * Returns whether we found a completion node.
192  */
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;
200                 return true;
201         }
202         return false;
203 }
204 /**
205  * Checks if the completion is on the type following a 'new'.
206  * Returns whether we found a completion node.
207  */
208 private boolean checkClassInstanceCreation() {
209         if (this.betweenNewAndLeftBraket) {
210                 // completion on type inside an allocation expression
211                 
212                 if(this.throwBracketDepth != -1 && this.throwBracketDepth == this.bracketDepth) {
213                         this.nextTypeReferenceIsException = true;       
214                 }
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;
227                 } else {
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;
236                 }
237                 return true;
238         }
239         return false;
240 }
241 /**
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.
245  */
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)
248                 int length;
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--];
256                         if (dim == 0) {
257                                 typeRef.sourceEnd = this.intStack[this.intPtr--];
258                         } else {
259                                 this.intPtr--;
260                                 typeRef.sourceEnd = this.endPosition;
261                         }
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
263
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)
268
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;
275                         return true;
276                 }
277
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)
284                         
285                         // get the type reference
286                         TypeReference typeRef = getTypeReference(this.intPtr--);
287                         
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;
293                         return true;
294                 }
295
296         }
297         return false;
298 }
299 /**
300  * Checks if the completion is inside a method invocation or a constructor invocation.
301  * Returns whether we found a completion node.
302  */
303 private boolean checkInvocation() {
304         Expression topExpression = this.expressionPtr >= 0 ? 
305                 this.expressionStack[this.expressionPtr] :
306                 null;
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]"
313                                 
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--;
321                 }
322
323                 // find receiver and qualifier
324                 int invocationType = this.invocationTypeStack[this.invocationPtr];
325                 int qualifierExprPtr = this.qualifierStack[this.invocationPtr];
326
327                 // find arguments
328                 int numArgs = this.expressionPtr - qualifierExprPtr;
329                 int argStart = qualifierExprPtr + 1;
330                 Expression[] arguments = null;
331                 if (numArgs > 0) {
332                         // remember the arguments
333                         arguments = new Expression[numArgs];
334                         System.arraycopy(this.expressionStack, argStart, arguments, 0, numArgs);
335
336                         // consume the expression arguments
337                         this.expressionPtr -= numArgs;
338                         int count = numArgs;
339                         while (count > 0) {
340                                 count -= this.expressionLengthStack[this.expressionLengthPtr--];
341                         } 
342                 }
343
344                 // build ast node
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) {
350                                 case NO_RECEIVER:
351                                         // implicit this
352                                         messageSend.receiver = ThisReference.ThisImplicit;
353                                         break;
354                                 case NAME_RECEIVER:
355                                         // remove special flags for primitive types
356                                         while (this.identifierLengthPtr >= 0 && this.identifierLengthStack[this.identifierLengthPtr] < 0) {
357                                                 this.identifierLengthPtr--;
358                                         }
359                                 
360                                         // remove selector 
361                                         this.identifierPtr--; 
362                                         this.identifierLengthStack[this.identifierLengthPtr]--;
363                                         // consume the receiver
364                                         messageSend.receiver = this.getUnspecifiedReference();
365                                         break;
366                                 case SUPER_RECEIVER:
367                                         messageSend.receiver = SuperReference.Super;
368                                         break;
369                                 case EXPLICIT_RECEIVER:
370                                         messageSend.receiver = this.expressionStack[qualifierExprPtr];
371                         }
372
373                         // set selector
374                         int selectorPtr = this.selectorStack[this.invocationPtr];
375                         messageSend.selector = this.identifierStack[selectorPtr];
376                         // remove selector
377                         if (this.identifierLengthPtr >=0 && this.identifierLengthStack[this.identifierLengthPtr] == 1) {
378                                 this.identifierPtr--; 
379                                 this.identifierLengthPtr--;
380                         }
381                 
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;
385
386                         // remember the message send as an orphan completion node
387                         this.assistNode = messageSend;
388                         this.lastCheckPoint = messageSend.sourceEnd + 1;
389                         this.isOrphanCompletionNode = true;
390                         return true;
391                 } else {
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];
400                                 }
401                 
402                                 // no source is going to be replaced
403                                 call.sourceStart = this.cursorLocation + 1;
404                                 call.sourceEnd = this.cursorLocation;
405
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;
410                                 return true;
411                         } else {
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];
418                                 }
419                                 // no source is going to be replaced
420                                 allocExpr.sourceStart = this.cursorLocation + 1;
421                                 allocExpr.sourceEnd = this.cursorLocation;
422                                 
423                                 // remember the allocation expression as an orphan completion node
424                                 this.assistNode = allocExpr;
425                                 this.lastCheckPoint = allocExpr.sourceEnd + 1;
426                                 this.isOrphanCompletionNode = true;
427                                 return true;
428                         }
429                 }
430         }
431         return false;
432 }
433 /**
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.
436  */
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);
441                 return true;
442         }
443         return false;
444 }
445 /**
446  * Checks if the completion is on a name reference.
447  * Returns whether we found a completion node.
448  */
449 private boolean checkNameCompletion() {
450         /* 
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.
457         */ 
458         
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;
463         return true;
464 }
465 /**
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.
468  */
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;
473
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)){
479                         return false;
480                 }               
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;
489                         return true;
490                 }
491         }
492         return false;
493 }
494 /**
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.
498  */
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;
503
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)){
509                         return false;
510                 }
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;
517                         return true;
518                 }
519         }
520         return false;
521 }
522 /* 
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.
527  */
528 public void completionIdentifierCheck(){
529
530         if (checkRecoveredType()) return;
531         if (checkRecoveredMethod()) return;
532
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; 
536
537         /*
538                 In some cases, the completion identifier may not have yet been consumed,
539                 e.g.  int.[cursor]
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
542                 (i.e. pushed).
543          */
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();
549                 }
550         }
551
552         // check for different scenarii
553         try {
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;
560                 }
561
562                 // no need to check further if we are not at the cursor location
563                 if (this.indexOfAssistIdentifier() < 0) return;
564
565                 if (checkClassInstanceCreation()) return;
566                 if (checkCatchClause()) return;
567                 if (checkMemberAccess()) return;
568                 if (checkClassLiteralAccess()) return;
569
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;
573
574                 if (checkNameCompletion()) return;
575         } finally {
576                 storeLabelsIfNeeded();
577         }
578 }
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]--;
584         }
585         super.consumeCaseLabel();
586 }
587 protected void consumeClassHeaderExtends() {
588         this.nextTypeReferenceIsClass = true;
589         super.consumeClassHeaderExtends();
590         this.nextTypeReferenceIsClass = false;
591 }
592 protected void consumeClassTypeElt() {
593         this.nextTypeReferenceIsException = true;
594         super.consumeClassTypeElt();
595         this.nextTypeReferenceIsException = false;
596 }
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]--;
602         }
603         super.consumeConditionalExpression(op);
604 }
605 protected void consumeConstructorBody() {
606         super.consumeConstructorBody();
607         this.labelCounterPtr--;
608         if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
609 }
610 protected void consumeConstructorHeader() {
611         super.consumeConstructorHeader();
612         pushBlockInvocationPtr();
613 }
614 protected void consumeConstructorHeaderName() {
615
616         /* no need to take action if not inside assist identifiers */
617         if (indexOfAssistIdentifier() < 0) {
618                 super.consumeConstructorHeaderName();
619                 return;
620         }
621                 
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
625         }
626         this.restartRecovery = true;
627 }
628 protected void consumeEnterVariable() {
629         identifierPtr--;
630         identifierLengthPtr--;
631
632         boolean isLocalDeclaration = nestedMethod[nestedType] != 0;
633         int variableIndex = variablesCounter[nestedType];
634         int extendedDimension = intStack[intPtr + 1];
635         
636         if(isLocalDeclaration || indexOfAssistIdentifier() < 0 || variableIndex != 0 || extendedDimension != 0) {
637                 identifierPtr++;
638                 identifierLengthPtr++;
639                 super.consumeEnterVariable();
640         } else {
641                 restartRecovery = true;
642                 
643                 // recovery
644                 if (currentElement != null) {
645                         int nameSourceStart = (int)(identifierPositionStack[identifierPtr] >>> 32);
646                         intPtr--;
647                         
648                         TypeReference type = getTypeReference(intStack[intPtr--]);
649                         intPtr--;
650                         
651                         if (!(currentElement instanceof RecoveredType)
652                                 && (currentToken == TokenNameDOT
653                                         || (scanner.getLineNumber(type.sourceStart)
654                                                         != scanner.getLineNumber(nameSourceStart)))){
655                                 lastCheckPoint = nameSourceStart;
656                                 restartRecovery = true;
657                                 return;
658                         }
659                         
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;
666                 }
667         }
668 }
669 protected void consumeExitVariableWithInitialization() {
670         super.consumeExitVariableWithInitialization();
671         
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;
677         }
678 }
679
680 /*
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.
684  */
685 protected void consumeFieldAccess(boolean isSuperAccess) {
686         // FieldAccess ::= Primary '.' 'Identifier'
687         // FieldAccess ::= 'super' '.' 'Identifier'
688
689         // potential receiver is being poped, so reset potential receiver
690         this.invocationType = NO_RECEIVER;
691
692         if (this.indexOfAssistIdentifier() < 0) {
693                 super.consumeFieldAccess(isSuperAccess);
694         } else {
695                 this.pushCompletionOnMemberAccessOnExpressionStack(isSuperAccess);
696         }
697 }
698
699 protected void consumeFormalParameter() {
700         if (this.indexOfAssistIdentifier() < 0) {
701                 super.consumeFormalParameter();
702         } else {
703
704                 identifierLengthPtr--;
705                 char[] name = identifierStack[identifierPtr];
706                 long namePositions = identifierPositionStack[identifierPtr--];
707                 TypeReference type = getTypeReference(intStack[intPtr--] + intStack[intPtr--]);
708                 intPtr -= 2;
709                 Argument arg = 
710                         new CompletionOnArgumentName(
711                                 name, 
712                                 namePositions, 
713                                 type, 
714                                 intStack[intPtr + 1] & ~AccDeprecated); // modifiers
715                 pushOnAstStack(arg);
716                 
717                 assistNode = arg;
718                 this.lastCheckPoint = (int) namePositions;
719                 isOrphanCompletionNode = true;
720
721                 /* if incomplete method header, listLength counter will not have been reset,
722                         indicating that some arguments are available on the stack */
723                 listLength++;
724         }       
725 }
726 protected void consumeInterfaceType() {
727         this.nextTypeReferenceIsInterface = true;
728         super.consumeInterfaceType();
729         this.nextTypeReferenceIsInterface = false;
730 }
731 protected void consumeMethodHeaderName() {
732         if(this.indexOfAssistIdentifier() < 0) {
733                 identifierPtr--;
734                 identifierLengthPtr--;
735                 if(this.indexOfAssistIdentifier() != 0) {
736                         identifierPtr++;
737                         identifierLengthPtr++;
738                         super.consumeMethodHeaderName();
739                 } else {
740                         restartRecovery = true;
741                         
742                         // recovery
743                         if (currentElement != null) {
744                                 //name
745                                 char[] selector = identifierStack[identifierPtr + 1];
746                                 long selectorSource = identifierPositionStack[identifierPtr + 1];
747                                 
748                                 //type
749                                 TypeReference type = getTypeReference(intStack[intPtr--]);
750                                 ((CompletionOnSingleTypeReference)type).isCompletionNode = false;
751                                 //modifiers
752                                 int declarationSourceStart = intStack[intPtr--];
753                                 int modifiers = intStack[intPtr--];
754                                 
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;
762                                 } else {
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
769                                         assistNode = md;
770                                         this.lastCheckPoint = md.bodyStart;
771                                         currentElement = currentElement.add(md, 0);
772                                         lastIgnoredToken = -1;
773                                 }
774                         }
775                 }
776         } else {
777                 // MethodHeaderName ::= Modifiersopt Type 'Identifier' '('
778                 CompletionOnMethodName md = new CompletionOnMethodName(this.compilationUnit.compilationResult);
779         
780                 //name
781                 md.selector = identifierStack[identifierPtr];
782                 long selectorSource = identifierPositionStack[identifierPtr--];
783                 //type
784                 md.returnType = getTypeReference(intStack[intPtr--]);
785                 //modifiers
786                 md.declarationSourceStart = intStack[intPtr--];
787                 md.modifiers = intStack[intPtr--];
788         
789                 //highlight starts at selector start
790                 md.sourceStart = (int) (selectorSource >>> 32);
791                 md.selectorEnd = (int) selectorSource;
792                 pushOnAstStack(md);
793                 md.sourceEnd = lParenPos;
794                 md.bodyStart = lParenPos+1;
795                 listLength = 0; // initialize listLength before reading parameters/throws
796                 
797                 this.assistNode = md;   
798                 this.lastCheckPoint = md.sourceEnd;
799                 // recovery
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;
808                         } else {
809                                 lastCheckPoint = md.sourceStart;
810                                 restartRecovery = true;
811                         }
812                 }
813         }
814 }
815
816
817 protected void consumeMethodBody() {
818         super.consumeMethodBody();
819         this.labelCounterPtr--;
820         if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
821 }
822
823 protected void consumeMethodHeader() {
824         super.consumeMethodHeader();
825         pushBlockInvocationPtr();
826 }
827 protected void consumeModifiers() {
828         super.consumeModifiers();
829         // save from stack values
830         this.lastModifiersStart = intStack[intPtr];
831         this.lastModifiers =    intStack[intPtr-1];
832 }
833 protected void consumeNestedMethod() {
834         super.consumeNestedMethod();
835         this.pushNewLabelCounter();
836 }
837 protected void consumeStatementLabel() {
838         super.consumeStatementLabel();
839         if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
840 }
841 protected void consumeToken(int token) {
842         int previous = this.previousToken;
843         int previousIdentifierPtr = this.previousIdentifierPtr;
844         super.consumeToken(token);
845
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
848         // in method bodies)
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;
854         }
855         
856         // if in a method or if in a field initializer 
857         if (this.inMethodStack[this.inMethodPtr] || this.inFieldInitializationStack[this.inFieldInitializationPtr]) {
858                 switch (token) {
859                         case TokenNameDOT:
860                                 switch (previous) {
861 //                                      case TokenNamethis: // eg. this[.]fred()
862 //                                              this.invocationType = EXPLICIT_RECEIVER;
863 //                                              break;
864 //                                      case TokenNamesuper: // eg. super[.]fred()
865 //                                              this.invocationType = SUPER_RECEIVER;
866 //                                              break;
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;
871                                                         } else {
872                                                                 this.invocationType = NAME_RECEIVER;
873                                                         }
874                                                 }
875                                                 break;
876                                 }
877                                 break;
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;
883                                         }
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]()
888
889                                                 this.invocationType = EXPLICIT_RECEIVER;
890                                                 this.qualifier = this.expressionPtr;
891                                         }
892                                 }
893                                 break;  
894                         case TokenNamenew:
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;
901                                 }
902                                 break;
903 //                      case TokenNamethis:
904 //                              if (previous == TokenNameDOT) { // eg. fred().[this]()
905 //                                      this.invocationType = QUALIFIED_ALLOCATION;
906 //                                      this.qualifier = this.expressionPtr;
907 //                              }
908 //                              break;
909 //                      case TokenNamesuper:
910 //                              if (previous == TokenNameDOT) { // eg. fred().[super]()
911 //                                      this.invocationType = QUALIFIED_ALLOCATION;
912 //                                      this.qualifier = this.expressionPtr;
913 //                              }
914 //                              break;
915 //                      case TokenNamecatch:
916 //                              this.betweenCatchAndRightParen = true;
917 //                              break;
918                         case TokenNameLPAREN:
919                                 this.betweenNewAndLeftBraket = false;
920                                 this.bracketDepth++;
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
923                                 }
924                                 switch (previous) {
925                                         case TokenNameIdentifier: // eg. fred[(]) or foo.fred[(])
926                                                 this.pushOnInvocationStacks(this.invocationType, this.qualifier);
927                                                 this.invocationType = NO_RECEIVER;
928                                                 break;
929 //                                      case TokenNamethis: // explicit constructor invocation, eg. this[(]1, 2)
930 //                                              this.pushOnInvocationStacks(
931 //                                                      (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION, 
932 //                                                      this.qualifier);
933 //                                              this.invocationType = NO_RECEIVER;
934 //                                              break;
935 //                                      case TokenNamesuper: // explicit constructor invocation, eg. super[(]1, 2)
936 //                                              this.pushOnInvocationStacks(
937 //                                                      (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION, 
938 //                                                      this.qualifier);
939 //                                              this.invocationType = NO_RECEIVER;
940 //                                              break;
941                                 }
942                                 break;
943                         case TokenNameLBRACE:
944                                 this.betweenNewAndLeftBraket = false;
945                                 this.bracketDepth++;
946                                 this.pushBlockInvocationPtr();
947                                 break;
948                         case TokenNameLBRACKET:
949                                 this.betweenNewAndLeftBraket = false;
950                                 this.bracketDepth++;
951                                 break; 
952                         case TokenNameRBRACE:
953                                 this.bracketDepth--;
954                                 if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
955                                 break;
956                         case TokenNameRBRACKET:
957                                 this.bracketDepth--;
958                                 break; 
959                         case TokenNameRPAREN:
960                                 this.betweenCatchAndRightParen = false;
961                                 this.bracketDepth--;
962                                 break;
963                         case TokenNameCOLON:
964                                 if (previous == TokenNameIdentifier) {
965                                         if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]++;
966                                 }
967                                 break;
968 //                      case TokenNamethrow:
969 //                              this.throwBracketDepth= bracketDepth;
970 //                              break;
971                 }
972         }
973 }
974 /**
975  * Return whether the given ast node contains the completion node.
976  */
977 private boolean containsCompletionNode(AstNode ast) {
978         if (this.assistNode == null || ast instanceof Literal) {
979                 return false;
980         }
981         if (this.assistNode == ast) {
982                 return true;
983         }
984         if (ast instanceof Reference || ast instanceof TypeReference) {
985                 return ast == this.assistNode;
986         }
987         if (ast instanceof Assignment) {
988                 Assignment assign = (Assignment)ast;
989                 return containsCompletionNode(assign.lhs) || containsCompletionNode(assign.expression);
990         }
991         if (ast instanceof UnaryExpression) {
992                 UnaryExpression unary = (UnaryExpression)ast;
993                 return containsCompletionNode(unary.expression);
994         }
995         if (ast instanceof BinaryExpression) {
996                 BinaryExpression binary = (BinaryExpression)ast;
997                 return containsCompletionNode(binary.left) || containsCompletionNode(binary.right);
998         }
999         if (ast instanceof InstanceOfExpression) {
1000                 InstanceOfExpression instanceOfExpr = (InstanceOfExpression)ast;
1001                 return containsCompletionNode(instanceOfExpr.expression) || containsCompletionNode(instanceOfExpr.type);
1002         }
1003         if (ast instanceof ConditionalExpression) {
1004                 ConditionalExpression conditional = (ConditionalExpression)ast;
1005                 return containsCompletionNode(conditional.condition) || containsCompletionNode(conditional.valueIfTrue) || containsCompletionNode(conditional.valueIfFalse);
1006         }
1007         if (ast instanceof AllocationExpression) {
1008                 AllocationExpression alloc = (AllocationExpression)ast;
1009                 return containsCompletionNode(alloc.type);
1010         }
1011         if (ast instanceof CastExpression) {
1012                 CastExpression cast = (CastExpression)ast;
1013                 return containsCompletionNode(cast.expression) || containsCompletionNode(cast.type);
1014         }
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])) {
1021                                         return true;
1022                                 }
1023                         }
1024                         return false;
1025                 }
1026         }
1027         return false;
1028 }
1029 public ImportReference createAssistImportReference(char[][] tokens, long[] positions){
1030         return new CompletionOnImportReference(tokens, positions);
1031 }
1032 public ImportReference createAssistPackageReference(char[][] tokens, long[] positions){
1033         return new CompletionOnPackageReference(tokens, positions);
1034 }
1035 public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions){
1036         return new CompletionOnQualifiedNameReference(
1037                                         previousIdentifiers, 
1038                                         name, 
1039                                         positions);     
1040 }
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,  
1045                                         name, 
1046                                         positions)
1047                                 : this.nextTypeReferenceIsInterface
1048                                         ? new CompletionOnQualifiedInterfaceReference(
1049                                                 previousIdentifiers, 
1050                                                 name, 
1051                                                 positions)
1052                                         : this.nextTypeReferenceIsClass
1053                                                 ? new CompletionOnQualifiedClassReference(
1054                                                         previousIdentifiers, 
1055                                                         name, 
1056                                                         positions)
1057                                                 : new CompletionOnQualifiedTypeReference(
1058                                                         previousIdentifiers, 
1059                                                         name, 
1060                                                         positions);     
1061 }
1062 public NameReference createSingleAssistNameReference(char[] name, long position) {
1063         return new CompletionOnSingleNameReference(name, position);
1064 }
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);
1073 }
1074 public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
1075
1076         this.cursorLocation = cursorLocation;
1077         CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1078         completionScanner.completionIdentifier = null;
1079         completionScanner.cursorLocation = cursorLocation;
1080         return this.dietParse(sourceUnit, compilationResult);
1081 }
1082 /*
1083  * Flush parser/scanner state regarding to code assist
1084  */
1085 public void flushAssistState() {
1086
1087         super.flushAssistState();
1088         this.isOrphanCompletionNode = false;
1089         CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1090         completionScanner.completedIdentifierStart = 0;
1091         completionScanner.completedIdentifierEnd = -1;
1092 }
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;
1097         }
1098         return super.getUnspecifiedReferenceOptimized();
1099 }
1100 /**
1101  * Return whether the given ast node has information interresting for code completion.
1102  */
1103 private boolean hasCompletionInformation(AstNode ast) {
1104         return (
1105                 ast instanceof AbstractMethodDeclaration ||
1106                 ast instanceof AbstractVariableDeclaration ||
1107                 ast instanceof LabeledStatement ||
1108                 ast instanceof TypeDeclaration);
1109 }
1110 public void initialize() {
1111         super.initialize();
1112         this.initializeForBlockStatements();
1113         this.labelCounterPtr = -1;
1114 }
1115 /*
1116  * Initializes the state of the parser that is about to go for BlockStatements.
1117  */
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;
1129 }
1130 public void initializeScanner(){
1131         this.scanner = new CompletionScanner(this.assertMode);
1132 }
1133 /**
1134  * Returns whether the completion is just after an array type
1135  * eg. String[].[cursor]
1136  */
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)) {
1142                 return true;
1143         }
1144         return false;
1145 }
1146 private boolean isEmptyNameCompletion() {
1147         return
1148                 this.assistNode != null && 
1149                 this.assistNode instanceof CompletionOnSingleNameReference &&
1150                 (((CompletionOnSingleNameReference)this.assistNode).token.length == 0);
1151 }
1152 public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
1153
1154         this.cursorLocation = cursorLocation;
1155         CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1156         completionScanner.completionIdentifier = null;
1157         completionScanner.cursorLocation = cursorLocation;
1158         return this.parse(sourceUnit, compilationResult);
1159 }
1160 /*
1161  * Prepares the state of the parser to go for BlockStatements.
1162  */
1163 protected void prepareForBlockStatements() {
1164         super.prepareForBlockStatements();
1165         this.initializeForBlockStatements();
1166 }
1167 protected void pushBlockInvocationPtr() {
1168         try {
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;
1176         }
1177 }
1178 /**
1179  * Creates a completion on member access node and push it
1180  * on the expression stack.
1181  */
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;
1196                 }
1197                 expressionStack[expressionPtr] = fr;
1198         }
1199 }
1200 protected void pushNewLabelCounter() {
1201         try {
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;
1209         }
1210 }
1211 /**
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.
1214  */
1215 protected void pushOnInvocationStacks(int invocationType, int qualifierExprPtr) {
1216         // NB: invocationPtr has already been incremented by a call to pushOnSelectorStack()
1217         try {
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;
1230         }
1231 }
1232 public void recordCompletionOnReference(){
1233
1234         if (currentElement instanceof RecoveredType){
1235                 RecoveredType recoveredType = (RecoveredType)currentElement;
1236
1237                 /* filter out cases where scanner is still inside type header */
1238                 if (!recoveredType.foundOpeningBrace) return;
1239                 
1240                 /* generate a pseudo field with a completion on type reference */       
1241                 currentElement.add(
1242                         new CompletionOnFieldType(this.getTypeReference(0), false), 0);
1243                 return;
1244         }
1245         if (!diet) return; // only record references attached to types
1246
1247 }
1248 protected void reportSyntaxError(int act, int currentKind, int stateStackTop) {
1249
1250         /* Intercept error state on EOF inside method bodies, due to 
1251            cursor location being used as an EOF position.
1252         */
1253         if (!diet && currentToken == TokenNameEOF) return;
1254         super.reportSyntaxError(act, currentKind, stateStackTop);
1255 }
1256 /*
1257  * Reset internal state after completion is over
1258  */
1259  
1260 public void reset() {
1261         super.reset();
1262         this.cursorLocation = 0;
1263 }
1264 /*
1265  * Reset internal state after completion is over
1266  */
1267  
1268 public void resetAfterCompletion() {
1269         this.cursorLocation = 0;
1270         this.flushAssistState();
1271 }
1272 /*
1273  * Reset context so as to resume to regular parse loop
1274  * If unable to reset for resuming, answers false.
1275  *
1276  * Move checkpoint location, reset internal stacks and
1277  * decide which grammar goal is activated.
1278  */
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())) {
1287
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;
1295                                 int token = -1;
1296                                 try {
1297                                         do {
1298                                                 // first token might not have to be a dot
1299                                                 if (token >= 0 || !this.completionBehindDot){
1300                                                         if ((token = scanner.getNextToken()) != TokenNameDOT) break;
1301                                                 }
1302                                                 if ((token = scanner.getNextToken()) != TokenNameIdentifier) break;
1303                                                 this.assistNode.sourceEnd = scanner.currentPosition - 1;
1304                                         } while (token != TokenNameEOF);
1305                                 } catch (InvalidInputException e){
1306                                 } finally {
1307                                         scanner.eofPosition = oldEof;
1308                                 }
1309                         }
1310                         */                      
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;
1316                         } else {
1317                                 this.resetStacks();
1318                                 return false;   
1319                         }
1320                 }
1321         }
1322         return super.resumeAfterRecovery();
1323 }
1324 public void setAssistIdentifier(char[] assistIdent){
1325         ((CompletionScanner)scanner).completionIdentifier = assistIdent;
1326 }
1327 /**
1328  * Stores the labels left on the identifier stack if they have not been stored yet.
1329  */
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);
1335 //      }
1336 //      this.identifierPtr -= counter;
1337 //      this.identifierLengthPtr -= counter; // labels have not been concatenated yet
1338 }
1339 /*
1340  * Update recovery state based on current parser/scanner state
1341  */
1342 protected void updateRecoveryState() {
1343
1344         /* expose parser state to recovery state */
1345         currentElement.updateFromParserState();
1346
1347         /* may be able to retrieve completionNode as an orphan, and then attach it */
1348         this.completionIdentifierCheck();
1349         this.attachOrphanCompletionNode();
1350         
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();
1355         }
1356         
1357         /* check and update recovered state based on current token,
1358                 this action is also performed when shifting token after recovery
1359                 got activated once. 
1360         */
1361         this.recoveryTokenCheck();
1362 }
1363
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);
1367         } else {
1368                 CompletionOnLocalName local = new CompletionOnLocalName(initialization, name, sourceStart, sourceEnd);
1369                 this.assistNode = local;
1370                 this.lastCheckPoint = sourceEnd + 1;
1371                 return local;
1372         }
1373 }
1374
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);
1378         } else {
1379                 CompletionOnFieldName field = new CompletionOnFieldName(initialization, name, sourceStart, sourceEnd);
1380                 this.assistNode = field;
1381                 this.lastCheckPoint = sourceEnd + 1;
1382                 return field;
1383         }
1384 }
1385
1386 }