cee0c25da4d97cf793a8c2fac6806ea8168cde60
[phpeclipse.git] /
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.codeassist.impl.AssistParser;
23 import net.sourceforge.phpdt.internal.compiler.CompilationResult;
24 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
25 import net.sourceforge.phpdt.internal.compiler.ast.AbstractVariableDeclaration;
26 import net.sourceforge.phpdt.internal.compiler.ast.AllocationExpression;
27 import net.sourceforge.phpdt.internal.compiler.ast.Argument;
28 import net.sourceforge.phpdt.internal.compiler.ast.Assignment;
29 import net.sourceforge.phpdt.internal.compiler.ast.AstNode;
30 import net.sourceforge.phpdt.internal.compiler.ast.BinaryExpression;
31 import net.sourceforge.phpdt.internal.compiler.ast.CastExpression;
32 import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
33 import net.sourceforge.phpdt.internal.compiler.ast.ConditionalExpression;
34 import net.sourceforge.phpdt.internal.compiler.ast.ExplicitConstructorCall;
35 import net.sourceforge.phpdt.internal.compiler.ast.Expression;
36 import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
37 import net.sourceforge.phpdt.internal.compiler.ast.ImportReference;
38 import net.sourceforge.phpdt.internal.compiler.ast.Initializer;
39 import net.sourceforge.phpdt.internal.compiler.ast.InstanceOfExpression;
40 import net.sourceforge.phpdt.internal.compiler.ast.LabeledStatement;
41 import net.sourceforge.phpdt.internal.compiler.ast.Literal;
42 import net.sourceforge.phpdt.internal.compiler.ast.LocalDeclaration;
43 import net.sourceforge.phpdt.internal.compiler.ast.NameReference;
44 import net.sourceforge.phpdt.internal.compiler.ast.QualifiedAllocationExpression;
45 import net.sourceforge.phpdt.internal.compiler.ast.QualifiedNameReference;
46 import net.sourceforge.phpdt.internal.compiler.ast.Reference;
47 import net.sourceforge.phpdt.internal.compiler.ast.SingleNameReference;
48 import net.sourceforge.phpdt.internal.compiler.ast.SingleTypeReference;
49 import net.sourceforge.phpdt.internal.compiler.ast.Statement;
50 import net.sourceforge.phpdt.internal.compiler.ast.SuperReference;
51 import net.sourceforge.phpdt.internal.compiler.ast.ThisReference;
52 import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
53 import net.sourceforge.phpdt.internal.compiler.ast.TypeReference;
54 import net.sourceforge.phpdt.internal.compiler.ast.UnaryExpression;
55 import net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit;
56 import net.sourceforge.phpdt.internal.compiler.parser.RecoveredMethod;
57 import net.sourceforge.phpdt.internal.compiler.parser.RecoveredType;
58 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
59
60 public class CompletionParser extends AssistParser {
61
62         /* public fields */
63
64         public int cursorLocation;
65         public char[][] labels; // the visible labels up to the cursor location
66         public AstNode assistNodeParent; // the parent node of assist node
67         /* the following fields are internal flags */
68         
69         boolean betweenNewAndLeftBraket; // whether we are between the keyword 'new' and the following left braket, ie. '[', '(' or '{'
70         boolean betweenCatchAndRightParen; // whether we are between the keyword 'catch' and the following ')'
71         boolean completionBehindDot; // true when completion identifier immediately follows a dot
72         
73         boolean nextTypeReferenceIsClass;
74         boolean nextTypeReferenceIsException;
75         boolean nextTypeReferenceIsInterface;
76         
77         int bracketDepth;
78         int throwBracketDepth;
79
80         // the stacks of types and qualifiers for invocations (ie. method invocations, allocation expressions and
81         // explicit constructor invocations). They use the same stack pointer as the selector stack (ie. invocationPtr)
82         // the invocation type stack contains one of the invocation type constants below
83         // the qualifier stack contains pointers to the expression stack or -1 if there is no qualifier
84         // (a qualifier is the expression that qualifies a 'new', a 'super' constructor or a 'this' constructor
85         //  or it is the receiver of a message send)
86         int[] invocationTypeStack = new int[StackIncrement];
87         int[] qualifierStack = new int[StackIncrement];
88
89         // invocation type constants
90         static final int EXPLICIT_RECEIVER = 0;
91         static final int NO_RECEIVER = -1;
92         static final int SUPER_RECEIVER = -2;
93         static final int NAME_RECEIVER = -3;
94         static final int ALLOCATION = -4;
95         static final int QUALIFIED_ALLOCATION = -5;
96
97         // the type of the current invocation (one of the invocation type constants)
98         int invocationType;
99
100         // a pointer in the expression stack to the qualifier of a invocation
101         int qualifier;
102
103         // a stack of label counters
104         // a new counter is pushed on the stack each time when a method (or a constructor) is entered, 
105         // it is poped when the method (or constructor) is exited,
106         // it is incremented when a new label is defined
107         int labelCounterPtr;
108         int[] labelCounterStack = new int[StackIncrement];
109
110         // a stack of invocationPtr: contains the first invocationPtr of a block
111         // the current invocationPtr+1 is pushed when a block is entered
112         // it is poped when a block is exited 
113         int blockInvocationPtr;
114         int[] blockInvocationStack = new int[StackIncrement];
115         
116         // last modifiers info
117         int lastModifiers = AccDefault;
118         int lastModifiersStart = -1;
119         
120 public CompletionParser(ProblemReporter problemReporter, boolean assertMode) {
121         super(problemReporter, assertMode);
122 }
123 public char[] assistIdentifier(){
124         return ((CompletionScanner)scanner).completionIdentifier;
125 }
126 protected void attachOrphanCompletionNode(){
127         if (this.isOrphanCompletionNode) {
128                 AstNode orphan = this.assistNode;
129                 this.isOrphanCompletionNode = false;
130                 
131                 /* if in context of a type, then persists the identifier into a fake field return type */
132                 if (currentElement instanceof RecoveredType){
133                         RecoveredType recoveredType = (RecoveredType)currentElement;
134                         /* filter out cases where scanner is still inside type header */
135                         if (recoveredType.foundOpeningBrace) {
136                                 /* generate a pseudo field with a completion on type reference */       
137                                 if (orphan instanceof TypeReference){
138                                         CompletionOnFieldType fieldDeclaration = new CompletionOnFieldType((TypeReference)orphan, false);
139
140                                         // retrieve available modifiers if any
141                                         if (intPtr >= 2 && intStack[intPtr-1] == this.lastModifiersStart && intStack[intPtr-2] == this.lastModifiers){
142                                                 fieldDeclaration.modifiersSourceStart = intStack[intPtr-1];
143                                                 fieldDeclaration.modifiers = intStack[intPtr-2];
144                                         }
145
146                                         currentElement = currentElement.add(fieldDeclaration, 0);
147                                         return;
148                                 }
149                         }
150                 }
151                 /* if in context of a method, persists if inside arguments as a type */
152                 if (currentElement instanceof RecoveredMethod){
153                         RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement;
154                         /* only consider if inside method header */
155                         if (!recoveredMethod.foundOpeningBrace) {
156                                 //if (rParenPos < lParenPos){ // inside arguments
157                                 if (orphan instanceof TypeReference){
158                                         currentElement = currentElement.parent.add(
159                                                 new CompletionOnFieldType((TypeReference)orphan, true), 0);
160                                         return;
161                                 }
162                         }
163                 }
164
165                 // add the completion node to the method declaration or constructor declaration
166                 if (orphan instanceof Statement) {
167                         /* check for completion at the beginning of method body
168                                 behind an invalid signature
169                          */
170                         RecoveredMethod method = currentElement.enclosingMethod();
171                         if (method != null){
172                                 AbstractMethodDeclaration methodDecl = method.methodDeclaration;
173                                 if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace
174                                         && (scanner.getLineNumber(orphan.sourceStart) == scanner.getLineNumber(methodDecl.sourceEnd))){
175                                         return;
176                                 }
177                         }
178                         // add the completion node as a statement to the list of block statements
179                         currentElement = currentElement.add((Statement)orphan, 0);
180                         return;
181                 } 
182         }
183         
184         // the following code applies only in methods, constructors or initializers
185         if ((!this.inMethodStack[this.inMethodPtr] && !this.inFieldInitializationStack[this.inFieldInitializationPtr])) { 
186                 return;
187         }
188         
189         // push top expression on ast stack if it contains the completion node
190         Expression expression;
191         if (this.expressionPtr > -1 && containsCompletionNode(expression = this.expressionStack[this.expressionPtr])) {
192                 /* check for completion at the beginning of method body
193                         behind an invalid signature
194                  */
195                 RecoveredMethod method = currentElement.enclosingMethod();
196                 if (method != null){
197                         AbstractMethodDeclaration methodDecl = method.methodDeclaration;
198                         if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace
199                                 && (scanner.getLineNumber(expression.sourceStart) == scanner.getLineNumber(methodDecl.sourceEnd))){
200                                 return;
201                         }
202                 }
203                 if (expression instanceof AllocationExpression) {
204                         // keep the context if it is an allocation expression
205                         Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(expression);
206                         currentElement = currentElement.add(statement, 0);
207                 } else {
208                         Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(this.assistNode);
209                         currentElement = currentElement.add(statement, 0);
210                 }
211         }
212 }
213 public int bodyEnd(AbstractMethodDeclaration method){
214         return cursorLocation;
215 }
216 public int bodyEnd(Initializer initializer){
217         return cursorLocation;
218 }
219 /**
220  * Checks if the completion is on the exception type of a catch clause.
221  * Returns whether we found a completion node.
222  */
223 private boolean checkCatchClause() {
224         if (this.betweenCatchAndRightParen && this.identifierPtr > -1) { 
225                 // NB: if the cursor is on the variable, then it has been reduced (so identifierPtr is -1), 
226                 //     thus this can only be a completion on the type of the catch clause
227                 this.assistNode = getTypeReference(0);
228                 this.lastCheckPoint = this.assistNode.sourceEnd + 1;
229                 this.isOrphanCompletionNode = true;
230                 return true;
231         }
232         return false;
233 }
234 /**
235  * Checks if the completion is on the type following a 'new'.
236  * Returns whether we found a completion node.
237  */
238 private boolean checkClassInstanceCreation() {
239         if (this.betweenNewAndLeftBraket) {
240                 // completion on type inside an allocation expression
241                 
242                 if(this.throwBracketDepth != -1 && this.throwBracketDepth == this.bracketDepth) {
243                         this.nextTypeReferenceIsException = true;       
244                 }
245                 TypeReference type = getTypeReference(0);
246                 this.nextTypeReferenceIsException = false;
247                 this.assistNode = type;
248                 this.lastCheckPoint = type.sourceEnd + 1;
249                 if (this.invocationType == ALLOCATION) {
250                         // non qualified allocation expression
251                         AllocationExpression allocExpr = new AllocationExpression();
252                         allocExpr.type = type;
253                         allocExpr.sourceStart = type.sourceStart;
254                         allocExpr.sourceEnd = type.sourceEnd;
255                         pushOnExpressionStack(allocExpr);
256                         this.isOrphanCompletionNode = false;
257                 } else {
258                         // qualified allocation expression
259                         QualifiedAllocationExpression allocExpr = new QualifiedAllocationExpression();
260                         allocExpr.type = type;
261                         allocExpr.enclosingInstance = this.expressionStack[this.qualifier];
262                         allocExpr.sourceStart = this.intStack[this.intPtr--];
263                         allocExpr.sourceEnd = type.sourceEnd;
264                         this.expressionStack[this.qualifier] = allocExpr; // attach it now (it replaces the qualifier expression)
265                         this.isOrphanCompletionNode = false;
266                 }
267                 return true;
268         }
269         return false;
270 }
271 /**
272  * Checks if the completion is on the dot following an array type,
273  * a primitive type or an primitive array type.
274  * Returns whether we found a completion node.
275  */
276 private boolean checkClassLiteralAccess() {
277         if (this.identifierLengthPtr >= 1 && this.previousToken == TokenNameDOT) { // (NB: the top id length is 1 and it is for the completion identifier)
278                 int length;
279                 // if the penultimate id length is negative, 
280                 // the completion is after a primitive type or a primitive array type
281                 if ((length = this.identifierLengthStack[this.identifierLengthPtr-1]) < 0) {
282                         // build the primitive type node
283                         int dim = this.isAfterArrayType() ? this.intStack[this.intPtr--] : 0;
284                         SingleTypeReference typeRef = (SingleTypeReference)TypeReference.baseTypeReference(-length, dim);
285                         typeRef.sourceStart = this.intStack[this.intPtr--];
286                         if (dim == 0) {
287                                 typeRef.sourceEnd = this.intStack[this.intPtr--];
288                         } else {
289                                 this.intPtr--;
290                                 typeRef.sourceEnd = this.endPosition;
291                         }
292                         //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
293
294                         // find the completion identifier and its source positions
295                         char[] source = identifierStack[identifierPtr];
296                         long pos = this.identifierPositionStack[this.identifierPtr--];
297                         this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one)
298
299                         // build the completion on class literal access node
300                         CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef);
301                         access.completionIdentifier = source;
302                         this.identifierLengthPtr--; // pop the length that was used to say it is a primitive type
303                         this.assistNode = access;
304                         this.isOrphanCompletionNode = true;
305                         return true;
306                 }
307
308                 // if the completion is after a regular array type
309                 if (isAfterArrayType()) {
310                         // find the completion identifier and its source positions
311                         char[] source = identifierStack[identifierPtr];
312                         long pos = this.identifierPositionStack[this.identifierPtr--];
313                         this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one)
314                         
315                         // get the type reference
316                         TypeReference typeRef = getTypeReference(this.intPtr--);
317                         
318                         // build the completion on class literal access node
319                         CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef);
320                         access.completionIdentifier = source;
321                         this.assistNode = access;
322                         this.isOrphanCompletionNode = true;
323                         return true;
324                 }
325
326         }
327         return false;
328 }
329 /**
330  * Checks if the completion is inside a method invocation or a constructor invocation.
331  * Returns whether we found a completion node.
332  */
333 private boolean checkInvocation() {
334         Expression topExpression = this.expressionPtr >= 0 ? 
335                 this.expressionStack[this.expressionPtr] :
336                 null;
337         boolean isEmptyNameCompletion = false;
338         boolean isEmptyAssistIdentifier = false;
339         int startInvocationPtr = this.blockInvocationPtr >= 0 ? this.blockInvocationStack[this.blockInvocationPtr] : 0;
340         if (this.invocationPtr >= startInvocationPtr
341                 && ((isEmptyNameCompletion = topExpression == this.assistNode && this.isEmptyNameCompletion()) // eg. it is something like "this.fred([cursor]" but it is not something like "this.fred(1 + [cursor]"
342                         || (isEmptyAssistIdentifier = this.indexOfAssistIdentifier() >= 0 && this.identifierStack[this.identifierPtr].length == 0))) { // eg. it is something like "this.fred(1 [cursor]"
343                                 
344                 // pop empty name completion
345                 if (isEmptyNameCompletion) {
346                         this.expressionPtr--;
347                         this.expressionLengthStack[this.expressionLengthPtr]--;
348                 } else if (isEmptyAssistIdentifier) {
349                         this.identifierPtr--;
350                         this.identifierLengthPtr--;
351                 }
352
353                 // find receiver and qualifier
354                 int invocationType = this.invocationTypeStack[this.invocationPtr];
355                 int qualifierExprPtr = this.qualifierStack[this.invocationPtr];
356
357                 // find arguments
358                 int numArgs = this.expressionPtr - qualifierExprPtr;
359                 int argStart = qualifierExprPtr + 1;
360                 Expression[] arguments = null;
361                 if (numArgs > 0) {
362                         // remember the arguments
363                         arguments = new Expression[numArgs];
364                         System.arraycopy(this.expressionStack, argStart, arguments, 0, numArgs);
365
366                         // consume the expression arguments
367                         this.expressionPtr -= numArgs;
368                         int count = numArgs;
369                         while (count > 0) {
370                                 count -= this.expressionLengthStack[this.expressionLengthPtr--];
371                         } 
372                 }
373
374                 // build ast node
375                 if (invocationType != ALLOCATION && invocationType != QUALIFIED_ALLOCATION) {
376                         // creates completion on message send   
377                         CompletionOnMessageSend messageSend = new CompletionOnMessageSend();
378                         messageSend.arguments = arguments;
379                         switch (invocationType) {
380                                 case NO_RECEIVER:
381                                         // implicit this
382                                         messageSend.receiver = ThisReference.ThisImplicit;
383                                         break;
384                                 case NAME_RECEIVER:
385                                         // remove special flags for primitive types
386                                         while (this.identifierLengthPtr >= 0 && this.identifierLengthStack[this.identifierLengthPtr] < 0) {
387                                                 this.identifierLengthPtr--;
388                                         }
389                                 
390                                         // remove selector 
391                                         this.identifierPtr--; 
392                                         this.identifierLengthStack[this.identifierLengthPtr]--;
393                                         // consume the receiver
394                                         messageSend.receiver = this.getUnspecifiedReference();
395                                         break;
396                                 case SUPER_RECEIVER:
397                                         messageSend.receiver = SuperReference.Super;
398                                         break;
399                                 case EXPLICIT_RECEIVER:
400                                         messageSend.receiver = this.expressionStack[qualifierExprPtr];
401                         }
402
403                         // set selector
404                         int selectorPtr = this.selectorStack[this.invocationPtr];
405                         messageSend.selector = this.identifierStack[selectorPtr];
406                         // remove selector
407                         if (this.identifierLengthPtr >=0 && this.identifierLengthStack[this.identifierLengthPtr] == 1) {
408                                 this.identifierPtr--; 
409                                 this.identifierLengthPtr--;
410                         }
411                 
412                         // the entire message may be replaced in case qualification is needed
413                         messageSend.sourceStart = (int)(this.identifierPositionStack[selectorPtr] >> 32); //this.cursorLocation + 1;
414                         messageSend.sourceEnd = this.cursorLocation;
415
416                         // remember the message send as an orphan completion node
417                         this.assistNode = messageSend;
418                         this.lastCheckPoint = messageSend.sourceEnd + 1;
419                         this.isOrphanCompletionNode = true;
420                         return true;
421                 } else {
422                         int selectorPtr = this.selectorStack[this.invocationPtr];
423                         if (selectorPtr == THIS_CONSTRUCTOR || selectorPtr == SUPER_CONSTRUCTOR) {
424                                 // creates an explicit constructor call
425                                 CompletionOnExplicitConstructorCall call = new CompletionOnExplicitConstructorCall(
426                                         (selectorPtr == THIS_CONSTRUCTOR) ? ExplicitConstructorCall.This : ExplicitConstructorCall.Super);
427                                 call.arguments = arguments;
428                                 if (invocationType == QUALIFIED_ALLOCATION) {
429                                         call.qualification = this.expressionStack[qualifierExprPtr];
430                                 }
431                 
432                                 // no source is going to be replaced
433                                 call.sourceStart = this.cursorLocation + 1;
434                                 call.sourceEnd = this.cursorLocation;
435
436                                 // remember the explicit constructor call as an orphan completion node
437                                 this.assistNode = call;
438                                 this.lastCheckPoint = call.sourceEnd + 1;
439                                 this.isOrphanCompletionNode = true;
440                                 return true;
441                         } else {
442                                 // creates an allocation expression 
443                                 CompletionOnQualifiedAllocationExpression allocExpr = new CompletionOnQualifiedAllocationExpression();
444                                 allocExpr.arguments = arguments;
445                                 allocExpr.type = super.getTypeReference(0); // we don't want a completion node here, so call super
446                                 if (invocationType == QUALIFIED_ALLOCATION) {
447                                         allocExpr.enclosingInstance = this.expressionStack[qualifierExprPtr];
448                                 }
449                                 // no source is going to be replaced
450                                 allocExpr.sourceStart = this.cursorLocation + 1;
451                                 allocExpr.sourceEnd = this.cursorLocation;
452                                 
453                                 // remember the allocation expression as an orphan completion node
454                                 this.assistNode = allocExpr;
455                                 this.lastCheckPoint = allocExpr.sourceEnd + 1;
456                                 this.isOrphanCompletionNode = true;
457                                 return true;
458                         }
459                 }
460         }
461         return false;
462 }
463 /**
464  * Checks if the completion is on a member access (ie. in an identifier following a dot).
465  * Returns whether we found a completion node.
466  */
467 private boolean checkMemberAccess() {
468         if (this.previousToken == TokenNameDOT && this.qualifier > -1 && this.expressionPtr == this.qualifier) {
469                 // the receiver is an expression
470                 pushCompletionOnMemberAccessOnExpressionStack(false);
471                 return true;
472         }
473         return false;
474 }
475 /**
476  * Checks if the completion is on a name reference.
477  * Returns whether we found a completion node.
478  */
479 private boolean checkNameCompletion() {
480         /* 
481                 We didn't find any other completion, but the completion identifier is on the identifier stack,
482                 so it can only be a completion on name.
483                 Note that we allow the completion on a name even if nothing is expected (eg. foo() b[cursor] would
484                 be a completion on 'b'). This policy gives more to the user than he/she would expect, but this 
485                 simplifies the problem. To fix this, the recovery must be changed to work at a 'statement' granularity
486                 instead of at the 'expression' granularity as it does right now.
487         */ 
488         
489         // NB: at this point the completion identifier is on the identifier stack
490         this.assistNode = getUnspecifiedReferenceOptimized();
491         this.lastCheckPoint = this.assistNode.sourceEnd + 1;
492         this.isOrphanCompletionNode = true;
493         return true;
494 }
495 /**
496  * Checks if the completion is in the context of a method and on the type of one of its arguments
497  * Returns whether we found a completion node.
498  */
499 private boolean checkRecoveredMethod() {
500         if (currentElement instanceof RecoveredMethod){
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                 RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement;
512                 /* only consider if inside method header */
513                 if (!recoveredMethod.foundOpeningBrace
514                         && lastIgnoredToken == -1) {
515                         //if (rParenPos < lParenPos){ // inside arguments
516                         this.assistNode = this.getTypeReference(0);
517                         this.lastCheckPoint = this.assistNode.sourceEnd + 1;
518                         this.isOrphanCompletionNode = true;
519                         return true;
520                 }
521         }
522         return false;
523 }
524 /**
525  * Checks if the completion is in the context of a type and on a type reference in this type.
526  * Persists the identifier into a fake field return type
527  * Returns whether we found a completion node.
528  */
529 private boolean checkRecoveredType() {
530         if (currentElement instanceof RecoveredType){
531                 /* check if current awaiting identifier is the completion identifier */
532                 if (this.indexOfAssistIdentifier() < 0) return false;
533
534                 /* check if on line with an error already - to avoid completing inside 
535                         illegal type names e.g.  int[<cursor> */
536                 if ((lastErrorEndPosition <= cursorLocation+1)
537                         && scanner.getLineNumber(lastErrorEndPosition) 
538                                 == scanner.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart)){
539                         return false;
540                 }
541                 RecoveredType recoveredType = (RecoveredType)currentElement;
542                 /* filter out cases where scanner is still inside type header */
543                 if (recoveredType.foundOpeningBrace) {
544                         this.assistNode = this.getTypeReference(0);
545                         this.lastCheckPoint = this.assistNode.sourceEnd + 1;
546                         this.isOrphanCompletionNode = true;
547                         return true;
548                 }
549         }
550         return false;
551 }
552 /* 
553  * Check whether about to shift beyond the completion token.
554  * If so, depending on the context, a special node might need to be created
555  * and attached to the existing recovered structure so as to be remember in the
556  * resulting parsed structure.
557  */
558 public void completionIdentifierCheck(){
559
560         if (checkRecoveredType()) return;
561         if (checkRecoveredMethod()) return;
562
563         // if not in a method in non diet mode and if not inside a field initializer, only record references attached to types
564         if (!(this.inMethodStack[this.inMethodPtr] && !this.diet)
565                 && !insideFieldInitialization()) return; 
566
567         /*
568                 In some cases, the completion identifier may not have yet been consumed,
569                 e.g.  int.[cursor]
570                 This is because the grammar does not allow any (empty) identifier to follow
571                 a base type. We thus have to manually force the identifier to be consumed
572                 (i.e. pushed).
573          */
574         if (assistIdentifier() == null && this.currentToken == TokenNameIdentifier) { // Test below copied from CompletionScanner.getCurrentIdentifierSource()
575                 if (cursorLocation < this.scanner.startPosition && this.scanner.currentPosition == this.scanner.startPosition){ // fake empty identifier got issued
576                         this.pushIdentifier();                                  
577                 } else if (cursorLocation+1 >= this.scanner.startPosition && cursorLocation < this.scanner.currentPosition){
578                         this.pushIdentifier();
579                 }
580         }
581
582         // check for different scenarii
583         try {
584                 // no need to go further if we found a non empty completion node
585                 // (we still need to store labels though)
586                 if (this.assistNode != null) {
587                         // however inside an invocation, the completion identifier may already have been consumed into an empty name 
588                         // completion, so this check should be before we check that we are at the cursor location
589                         if (!isEmptyNameCompletion() || checkInvocation()) return;
590                 }
591
592                 // no need to check further if we are not at the cursor location
593                 if (this.indexOfAssistIdentifier() < 0) return;
594
595                 if (checkClassInstanceCreation()) return;
596                 if (checkCatchClause()) return;
597                 if (checkMemberAccess()) return;
598                 if (checkClassLiteralAccess()) return;
599
600                 // if the completion was not on an empty name, it can still be inside an invocation (eg. this.fred("abc"[cursor])
601                 // (NB: Put this check before checkNameCompletion() because the selector of the invocation can be on the identifier stack)
602                 if (checkInvocation()) return;
603
604                 if (checkNameCompletion()) return;
605         } finally {
606                 storeLabelsIfNeeded();
607         }
608 }
609 protected void consumeCaseLabel() {
610         Expression caseExpression = this.expressionStack[this.expressionPtr];
611         if (caseExpression instanceof SingleNameReference || caseExpression instanceof QualifiedNameReference) {
612                 // label counter was wrongly incremented in consumeToken
613                 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
614         }
615         super.consumeCaseLabel();
616 }
617 protected void consumeClassHeaderExtends() {
618         this.nextTypeReferenceIsClass = true;
619         super.consumeClassHeaderExtends();
620         this.nextTypeReferenceIsClass = false;
621 }
622 protected void consumeClassTypeElt() {
623         this.nextTypeReferenceIsException = true;
624         super.consumeClassTypeElt();
625         this.nextTypeReferenceIsException = false;
626 }
627 protected void consumeConditionalExpression(int op) {
628         Expression valueIfTrue = this.expressionStack[this.expressionPtr - 1];
629         if (valueIfTrue instanceof SingleNameReference || valueIfTrue instanceof QualifiedNameReference) {
630                 // label counter was wrongly incremented in consumeToken
631                 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
632         }
633         super.consumeConditionalExpression(op);
634 }
635 protected void consumeConstructorBody() {
636         super.consumeConstructorBody();
637         this.labelCounterPtr--;
638         if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
639 }
640 protected void consumeConstructorHeader() {
641         super.consumeConstructorHeader();
642         pushBlockInvocationPtr();
643 }
644 protected void consumeConstructorHeaderName() {
645
646         /* no need to take action if not inside assist identifiers */
647         if (indexOfAssistIdentifier() < 0) {
648                 super.consumeConstructorHeaderName();
649                 return;
650         }
651                 
652         /* force to start recovering in order to get fake field behavior */
653         if (currentElement == null){
654                 this.hasReportedError = true; // do not report any error
655         }
656         this.restartRecovery = true;
657 }
658 protected void consumeEnterVariable() {
659         identifierPtr--;
660         identifierLengthPtr--;
661
662         boolean isLocalDeclaration = nestedMethod[nestedType] != 0;
663         int variableIndex = variablesCounter[nestedType];
664         int extendedDimension = intStack[intPtr + 1];
665         
666         if(isLocalDeclaration || indexOfAssistIdentifier() < 0 || variableIndex != 0 || extendedDimension != 0) {
667                 identifierPtr++;
668                 identifierLengthPtr++;
669                 super.consumeEnterVariable();
670         } else {
671                 restartRecovery = true;
672                 
673                 // recovery
674                 if (currentElement != null) {
675                         int nameSourceStart = (int)(identifierPositionStack[identifierPtr] >>> 32);
676                         intPtr--;
677                         
678                         TypeReference type = getTypeReference(intStack[intPtr--]);
679                         intPtr--;
680                         
681                         if (!(currentElement instanceof RecoveredType)
682                                 && (currentToken == TokenNameDOT
683                                         || (scanner.getLineNumber(type.sourceStart)
684                                                         != scanner.getLineNumber(nameSourceStart)))){
685                                 lastCheckPoint = nameSourceStart;
686                                 restartRecovery = true;
687                                 return;
688                         }
689                         
690                         FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false);
691                         completionFieldDecl.modifiers = intStack[intPtr--];
692                         assistNode = completionFieldDecl;
693                         lastCheckPoint = type.sourceEnd + 1;
694                         currentElement = currentElement.add(completionFieldDecl, 0);
695                         lastIgnoredToken = -1;
696                 }
697         }
698 }
699 protected void consumeExitVariableWithInitialization() {
700         super.consumeExitVariableWithInitialization();
701         
702         // does not keep the initialization if completion is not inside
703         AbstractVariableDeclaration variable = (AbstractVariableDeclaration) astStack[astPtr];
704         if (cursorLocation + 1 < variable.initialization.sourceStart ||
705                 cursorLocation > variable.initialization.sourceEnd) {
706                 variable.initialization = null;
707         }
708 }
709
710 /*
711  * Copy of code from superclass with the following change:
712  * If the cursor location is on the field access, then create a 
713  * CompletionOnMemberAccess instead.
714  */
715 protected void consumeFieldAccess(boolean isSuperAccess) {
716         // FieldAccess ::= Primary '.' 'Identifier'
717         // FieldAccess ::= 'super' '.' 'Identifier'
718
719         // potential receiver is being poped, so reset potential receiver
720         this.invocationType = NO_RECEIVER;
721
722         if (this.indexOfAssistIdentifier() < 0) {
723                 super.consumeFieldAccess(isSuperAccess);
724         } else {
725                 this.pushCompletionOnMemberAccessOnExpressionStack(isSuperAccess);
726         }
727 }
728
729 protected void consumeFormalParameter() {
730         if (this.indexOfAssistIdentifier() < 0) {
731                 super.consumeFormalParameter();
732         } else {
733
734                 identifierLengthPtr--;
735                 char[] name = identifierStack[identifierPtr];
736                 long namePositions = identifierPositionStack[identifierPtr--];
737                 TypeReference type = getTypeReference(intStack[intPtr--] + intStack[intPtr--]);
738                 intPtr -= 2;
739                 Argument arg = 
740                         new CompletionOnArgumentName(
741                                 name, 
742                                 namePositions, 
743                                 type, 
744                                 intStack[intPtr + 1] & ~AccDeprecated); // modifiers
745                 pushOnAstStack(arg);
746                 
747                 assistNode = arg;
748                 this.lastCheckPoint = (int) namePositions;
749                 isOrphanCompletionNode = true;
750
751                 /* if incomplete method header, listLength counter will not have been reset,
752                         indicating that some arguments are available on the stack */
753                 listLength++;
754         }       
755 }
756 protected void consumeInterfaceType() {
757         this.nextTypeReferenceIsInterface = true;
758         super.consumeInterfaceType();
759         this.nextTypeReferenceIsInterface = false;
760 }
761 protected void consumeMethodHeaderName() {
762         if(this.indexOfAssistIdentifier() < 0) {
763                 identifierPtr--;
764                 identifierLengthPtr--;
765                 if(this.indexOfAssistIdentifier() != 0) {
766                         identifierPtr++;
767                         identifierLengthPtr++;
768                         super.consumeMethodHeaderName();
769                 } else {
770                         restartRecovery = true;
771                         
772                         // recovery
773                         if (currentElement != null) {
774                                 //name
775                                 char[] selector = identifierStack[identifierPtr + 1];
776                                 long selectorSource = identifierPositionStack[identifierPtr + 1];
777                                 
778                                 //type
779                                 TypeReference type = getTypeReference(intStack[intPtr--]);
780                                 ((CompletionOnSingleTypeReference)type).isCompletionNode = false;
781                                 //modifiers
782                                 int declarationSourceStart = intStack[intPtr--];
783                                 int modifiers = intStack[intPtr--];
784                                 
785                                 if(scanner.getLineNumber(type.sourceStart) != scanner.getLineNumber((int) (selectorSource >>> 32))) {
786                                         FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false);
787                                         completionFieldDecl.modifiers = modifiers;
788                                         assistNode = completionFieldDecl;
789                                         lastCheckPoint = type.sourceEnd + 1;
790                                         currentElement = currentElement.add(completionFieldDecl, 0);
791                                         lastIgnoredToken = -1;
792                                 } else {
793                                         CompletionOnMethodReturnType md = new CompletionOnMethodReturnType(type, this.compilationUnit.compilationResult);
794                                         md.selector = selector;
795                                         md.declarationSourceStart = declarationSourceStart;
796                                         md.modifiers = modifiers;
797                                         md.bodyStart = lParenPos+1;
798                                         listLength = 0; // initialize listLength before reading parameters/throws
799                                         assistNode = md;
800                                         this.lastCheckPoint = md.bodyStart;
801                                         currentElement = currentElement.add(md, 0);
802                                         lastIgnoredToken = -1;
803                                 }
804                         }
805                 }
806         } else {
807                 // MethodHeaderName ::= Modifiersopt Type 'Identifier' '('
808                 CompletionOnMethodName md = new CompletionOnMethodName(this.compilationUnit.compilationResult);
809         
810                 //name
811                 md.selector = identifierStack[identifierPtr];
812                 long selectorSource = identifierPositionStack[identifierPtr--];
813                 //type
814                 md.returnType = getTypeReference(intStack[intPtr--]);
815                 //modifiers
816                 md.declarationSourceStart = intStack[intPtr--];
817                 md.modifiers = intStack[intPtr--];
818         
819                 //highlight starts at selector start
820                 md.sourceStart = (int) (selectorSource >>> 32);
821                 md.selectorEnd = (int) selectorSource;
822                 pushOnAstStack(md);
823                 md.sourceEnd = lParenPos;
824                 md.bodyStart = lParenPos+1;
825                 listLength = 0; // initialize listLength before reading parameters/throws
826                 
827                 this.assistNode = md;   
828                 this.lastCheckPoint = md.sourceEnd;
829                 // recovery
830                 if (currentElement != null){
831                         if (currentElement instanceof RecoveredType 
832                                 //|| md.modifiers != 0
833                                 || (scanner.getLineNumber(md.returnType.sourceStart)
834                                                 == scanner.getLineNumber(md.sourceStart))){
835                                 lastCheckPoint = md.bodyStart;
836                                 currentElement = currentElement.add(md, 0);
837                                 lastIgnoredToken = -1;
838                         } else {
839                                 lastCheckPoint = md.sourceStart;
840                                 restartRecovery = true;
841                         }
842                 }
843         }
844 }
845
846
847 protected void consumeMethodBody() {
848         super.consumeMethodBody();
849         this.labelCounterPtr--;
850         if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
851 }
852
853 protected void consumeMethodHeader() {
854         super.consumeMethodHeader();
855         pushBlockInvocationPtr();
856 }
857 protected void consumeModifiers() {
858         super.consumeModifiers();
859         // save from stack values
860         this.lastModifiersStart = intStack[intPtr];
861         this.lastModifiers =    intStack[intPtr-1];
862 }
863 protected void consumeNestedMethod() {
864         super.consumeNestedMethod();
865         this.pushNewLabelCounter();
866 }
867 protected void consumeStatementLabel() {
868         super.consumeStatementLabel();
869         if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
870 }
871 protected void consumeToken(int token) {
872         int previous = this.previousToken;
873         int previousIdentifierPtr = this.previousIdentifierPtr;
874         super.consumeToken(token);
875
876         // if in field initializer (directly or not), on the completion identifier and not in recovery mode yet
877         // then position end of file at cursor location (so that we have the same behavior as
878         // in method bodies)
879         if (token == TokenNameIdentifier
880                         && this.identifierStack[this.identifierPtr] == assistIdentifier()
881                         && this.currentElement == null
882                         && this.insideFieldInitialization()) {
883                 this.scanner.eofPosition = cursorLocation < Integer.MAX_VALUE ? cursorLocation+1 : cursorLocation;
884         }
885         
886         // if in a method or if in a field initializer 
887         if (this.inMethodStack[this.inMethodPtr] || this.inFieldInitializationStack[this.inFieldInitializationPtr]) {
888                 switch (token) {
889                         case TokenNameDOT:
890                                 switch (previous) {
891 //                                      case TokenNamethis: // eg. this[.]fred()
892 //                                              this.invocationType = EXPLICIT_RECEIVER;
893 //                                              break;
894 //                                      case TokenNamesuper: // eg. super[.]fred()
895 //                                              this.invocationType = SUPER_RECEIVER;
896 //                                              break;
897                                         case TokenNameIdentifier: // eg. bar[.]fred()
898                                                 if (!this.betweenNewAndLeftBraket) { // eg. not new z.y[.]X()
899                                                         if (this.identifierPtr != previousIdentifierPtr) { // if identifier has been consumed, eg. this.x[.]fred()
900                                                                 this.invocationType = EXPLICIT_RECEIVER;
901                                                         } else {
902                                                                 this.invocationType = NAME_RECEIVER;
903                                                         }
904                                                 }
905                                                 break;
906                                 }
907                                 break;
908                         case TokenNameIdentifier:
909                                 if (previous == TokenNameDOT) { // eg. foo().[fred]()
910                                         // if current identifier is the empty completion one
911                                         if (identifierStack[identifierPtr] == CompletionScanner.EmptyCompletionIdentifier){
912                                                 this.completionBehindDot = true;
913                                         }
914                                         if (this.invocationType != SUPER_RECEIVER // eg. not super.[fred]()
915                                                 && this.invocationType != NAME_RECEIVER // eg. not bar.[fred]()
916                                                 && this.invocationType != ALLOCATION // eg. not new foo.[Bar]()
917                                                 && this.invocationType != QUALIFIED_ALLOCATION) { // eg. not fred().new foo.[Bar]()
918
919                                                 this.invocationType = EXPLICIT_RECEIVER;
920                                                 this.qualifier = this.expressionPtr;
921                                         }
922                                 }
923                                 break;  
924                         case TokenNamenew:
925                                 this.betweenNewAndLeftBraket = true;
926                                 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
927                                 if (previous == TokenNameDOT) { // eg. fred().[new] X()
928                                         this.invocationType = QUALIFIED_ALLOCATION;
929                                 } else { // eg. [new] X()
930                                         this.invocationType = ALLOCATION;
931                                 }
932                                 break;
933 //                      case TokenNamethis:
934 //                              if (previous == TokenNameDOT) { // eg. fred().[this]()
935 //                                      this.invocationType = QUALIFIED_ALLOCATION;
936 //                                      this.qualifier = this.expressionPtr;
937 //                              }
938 //                              break;
939 //                      case TokenNamesuper:
940 //                              if (previous == TokenNameDOT) { // eg. fred().[super]()
941 //                                      this.invocationType = QUALIFIED_ALLOCATION;
942 //                                      this.qualifier = this.expressionPtr;
943 //                              }
944 //                              break;
945 //                      case TokenNamecatch:
946 //                              this.betweenCatchAndRightParen = true;
947 //                              break;
948                         case TokenNameLPAREN:
949                                 this.betweenNewAndLeftBraket = false;
950                                 this.bracketDepth++;
951                                 if (this.invocationType == NO_RECEIVER || this.invocationType == NAME_RECEIVER) {
952                                         this.qualifier = this.expressionPtr; // remenber the last expression so that arguments are correctly computed
953                                 }
954                                 switch (previous) {
955                                         case TokenNameIdentifier: // eg. fred[(]) or foo.fred[(])
956                                                 this.pushOnInvocationStacks(this.invocationType, this.qualifier);
957                                                 this.invocationType = NO_RECEIVER;
958                                                 break;
959 //                                      case TokenNamethis: // explicit constructor invocation, eg. this[(]1, 2)
960 //                                              this.pushOnInvocationStacks(
961 //                                                      (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION, 
962 //                                                      this.qualifier);
963 //                                              this.invocationType = NO_RECEIVER;
964 //                                              break;
965 //                                      case TokenNamesuper: // explicit constructor invocation, eg. super[(]1, 2)
966 //                                              this.pushOnInvocationStacks(
967 //                                                      (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION, 
968 //                                                      this.qualifier);
969 //                                              this.invocationType = NO_RECEIVER;
970 //                                              break;
971                                 }
972                                 break;
973                         case TokenNameLBRACE:
974                                 this.betweenNewAndLeftBraket = false;
975                                 this.bracketDepth++;
976                                 this.pushBlockInvocationPtr();
977                                 break;
978                         case TokenNameLBRACKET:
979                                 this.betweenNewAndLeftBraket = false;
980                                 this.bracketDepth++;
981                                 break; 
982                         case TokenNameRBRACE:
983                                 this.bracketDepth--;
984                                 if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
985                                 break;
986                         case TokenNameRBRACKET:
987                                 this.bracketDepth--;
988                                 break; 
989                         case TokenNameRPAREN:
990                                 this.betweenCatchAndRightParen = false;
991                                 this.bracketDepth--;
992                                 break;
993                         case TokenNameCOLON:
994                                 if (previous == TokenNameIdentifier) {
995                                         if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]++;
996                                 }
997                                 break;
998 //                      case TokenNamethrow:
999 //                              this.throwBracketDepth= bracketDepth;
1000 //                              break;
1001                 }
1002         }
1003 }
1004 /**
1005  * Return whether the given ast node contains the completion node.
1006  */
1007 private boolean containsCompletionNode(AstNode ast) {
1008         if (this.assistNode == null || ast instanceof Literal) {
1009                 return false;
1010         }
1011         if (this.assistNode == ast) {
1012                 return true;
1013         }
1014         if (ast instanceof Reference || ast instanceof TypeReference) {
1015                 return ast == this.assistNode;
1016         }
1017         if (ast instanceof Assignment) {
1018                 Assignment assign = (Assignment)ast;
1019                 return containsCompletionNode(assign.lhs) || containsCompletionNode(assign.expression);
1020         }
1021         if (ast instanceof UnaryExpression) {
1022                 UnaryExpression unary = (UnaryExpression)ast;
1023                 return containsCompletionNode(unary.expression);
1024         }
1025         if (ast instanceof BinaryExpression) {
1026                 BinaryExpression binary = (BinaryExpression)ast;
1027                 return containsCompletionNode(binary.left) || containsCompletionNode(binary.right);
1028         }
1029         if (ast instanceof InstanceOfExpression) {
1030                 InstanceOfExpression instanceOfExpr = (InstanceOfExpression)ast;
1031                 return containsCompletionNode(instanceOfExpr.expression) || containsCompletionNode(instanceOfExpr.type);
1032         }
1033         if (ast instanceof ConditionalExpression) {
1034                 ConditionalExpression conditional = (ConditionalExpression)ast;
1035                 return containsCompletionNode(conditional.condition) || containsCompletionNode(conditional.valueIfTrue) || containsCompletionNode(conditional.valueIfFalse);
1036         }
1037         if (ast instanceof AllocationExpression) {
1038                 AllocationExpression alloc = (AllocationExpression)ast;
1039                 return containsCompletionNode(alloc.type);
1040         }
1041         if (ast instanceof CastExpression) {
1042                 CastExpression cast = (CastExpression)ast;
1043                 return containsCompletionNode(cast.expression) || containsCompletionNode(cast.type);
1044         }
1045         if (ast instanceof ExplicitConstructorCall) {
1046                 ExplicitConstructorCall call = (ExplicitConstructorCall)ast;
1047                 Expression[] arguments = call.arguments;
1048                 if (arguments != null) {
1049                         for (int i = 0; i < arguments.length; i++) {
1050                                 if (containsCompletionNode(arguments[i])) {
1051                                         return true;
1052                                 }
1053                         }
1054                         return false;
1055                 }
1056         }
1057         return false;
1058 }
1059 public ImportReference createAssistImportReference(char[][] tokens, long[] positions){
1060         return new CompletionOnImportReference(tokens, positions);
1061 }
1062 public ImportReference createAssistPackageReference(char[][] tokens, long[] positions){
1063         return new CompletionOnPackageReference(tokens, positions);
1064 }
1065 public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions){
1066         return new CompletionOnQualifiedNameReference(
1067                                         previousIdentifiers, 
1068                                         name, 
1069                                         positions);     
1070 }
1071 public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] name, long[] positions){
1072         return this.betweenCatchAndRightParen || this.nextTypeReferenceIsException // check for exception scenario 
1073                                 ? new CompletionOnQualifiedExceptionReference(
1074                                         previousIdentifiers,  
1075                                         name, 
1076                                         positions)
1077                                 : this.nextTypeReferenceIsInterface
1078                                         ? new CompletionOnQualifiedInterfaceReference(
1079                                                 previousIdentifiers, 
1080                                                 name, 
1081                                                 positions)
1082                                         : this.nextTypeReferenceIsClass
1083                                                 ? new CompletionOnQualifiedClassReference(
1084                                                         previousIdentifiers, 
1085                                                         name, 
1086                                                         positions)
1087                                                 : new CompletionOnQualifiedTypeReference(
1088                                                         previousIdentifiers, 
1089                                                         name, 
1090                                                         positions);     
1091 }
1092 public NameReference createSingleAssistNameReference(char[] name, long position) {
1093         return new CompletionOnSingleNameReference(name, position);
1094 }
1095 public TypeReference createSingleAssistTypeReference(char[] name, long position) {
1096         return this.betweenCatchAndRightParen || this.nextTypeReferenceIsException // check for exception scenario 
1097                 ? new CompletionOnExceptionReference(name, position) 
1098                 : this.nextTypeReferenceIsInterface
1099                         ? new CompletionOnInterfaceReference(name, position) 
1100                         : this.nextTypeReferenceIsClass
1101                                 ? new CompletionOnClassReference(name, position) 
1102                                 : new CompletionOnSingleTypeReference(name, position);
1103 }
1104 public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
1105
1106         this.cursorLocation = cursorLocation;
1107         CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1108         completionScanner.completionIdentifier = null;
1109         completionScanner.cursorLocation = cursorLocation;
1110         return this.dietParse(sourceUnit, compilationResult);
1111 }
1112 /*
1113  * Flush parser/scanner state regarding to code assist
1114  */
1115 public void flushAssistState() {
1116
1117         super.flushAssistState();
1118         this.isOrphanCompletionNode = false;
1119         CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1120         completionScanner.completedIdentifierStart = 0;
1121         completionScanner.completedIdentifierEnd = -1;
1122 }
1123 protected NameReference getUnspecifiedReferenceOptimized() {
1124         if (this.identifierLengthStack[this.identifierLengthPtr] > 1) { // reducing a qualified name
1125                 // potential receiver is being poped, so reset potential receiver
1126                 this.invocationType = NO_RECEIVER;
1127         }
1128         return super.getUnspecifiedReferenceOptimized();
1129 }
1130 /**
1131  * Return whether the given ast node has information interresting for code completion.
1132  */
1133 private boolean hasCompletionInformation(AstNode ast) {
1134         return (
1135                 ast instanceof AbstractMethodDeclaration ||
1136                 ast instanceof AbstractVariableDeclaration ||
1137                 ast instanceof LabeledStatement ||
1138                 ast instanceof TypeDeclaration);
1139 }
1140 public void initialize() {
1141         super.initialize();
1142         this.initializeForBlockStatements();
1143         this.labelCounterPtr = -1;
1144 }
1145 /*
1146  * Initializes the state of the parser that is about to go for BlockStatements.
1147  */
1148 private void initializeForBlockStatements() {
1149         this.previousToken = -1;
1150         this.previousIdentifierPtr = -1;
1151         this.completionBehindDot = false;
1152         this.betweenNewAndLeftBraket = false;
1153         this.betweenCatchAndRightParen = false;
1154         this.bracketDepth = 0;
1155         this.throwBracketDepth = -1;
1156         this.invocationType = NO_RECEIVER;
1157         this.qualifier = -1;
1158         this.blockInvocationPtr = -1;
1159 }
1160 public void initializeScanner(){
1161         this.scanner = new CompletionScanner(this.assertMode);
1162 }
1163 /**
1164  * Returns whether the completion is just after an array type
1165  * eg. String[].[cursor]
1166  */
1167 private boolean isAfterArrayType() {
1168         // TBD: The following relies on the fact that array dimensions are small: it says that if the
1169         //      top of the intStack is less than 11, then it must be a dimension 
1170         //      (smallest position of array type in a compilation unit is 11 as in "class X{Y[]")
1171         if ((this.intPtr > -1) && (this.intStack[this.intPtr] < 11)) {
1172                 return true;
1173         }
1174         return false;
1175 }
1176 private boolean isEmptyNameCompletion() {
1177         return
1178                 this.assistNode != null && 
1179                 this.assistNode instanceof CompletionOnSingleNameReference &&
1180                 (((CompletionOnSingleNameReference)this.assistNode).token.length == 0);
1181 }
1182 public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
1183
1184         this.cursorLocation = cursorLocation;
1185         CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1186         completionScanner.completionIdentifier = null;
1187         completionScanner.cursorLocation = cursorLocation;
1188         return this.parse(sourceUnit, compilationResult);
1189 }
1190 /*
1191  * Prepares the state of the parser to go for BlockStatements.
1192  */
1193 protected void prepareForBlockStatements() {
1194         super.prepareForBlockStatements();
1195         this.initializeForBlockStatements();
1196 }
1197 protected void pushBlockInvocationPtr() {
1198         try {
1199                 this.blockInvocationStack[++this.blockInvocationPtr] = this.invocationPtr+1;
1200         } catch (IndexOutOfBoundsException e) {
1201                 int oldStackLength = this.blockInvocationStack.length;
1202                 int[] oldStack = this.blockInvocationStack;
1203                 this.blockInvocationStack = new int[oldStackLength + StackIncrement];
1204                 System.arraycopy(oldStack, 0, this.blockInvocationStack, 0, oldStackLength);
1205                 this.blockInvocationStack[this.blockInvocationPtr] = this.invocationPtr+1;
1206         }
1207 }
1208 /**
1209  * Creates a completion on member access node and push it
1210  * on the expression stack.
1211  */
1212 private void pushCompletionOnMemberAccessOnExpressionStack(boolean isSuperAccess) {
1213         char[] source = identifierStack[identifierPtr];
1214         long pos = identifierPositionStack[identifierPtr--];
1215         CompletionOnMemberAccess fr = new CompletionOnMemberAccess(source, pos);
1216         this.assistNode = fr;
1217         this.lastCheckPoint = fr.sourceEnd + 1;
1218         identifierLengthPtr--;
1219         if (isSuperAccess) { //considerates the fieldReference beginning at the 'super' ....    
1220                 fr.sourceStart = intStack[intPtr--];
1221                 fr.receiver = new SuperReference(fr.sourceStart, endPosition);
1222                 pushOnExpressionStack(fr);
1223         } else { //optimize push/pop
1224                 if ((fr.receiver = expressionStack[expressionPtr]).isThis()) { //fieldreference begins at the this
1225                         fr.sourceStart = fr.receiver.sourceStart;
1226                 }
1227                 expressionStack[expressionPtr] = fr;
1228         }
1229 }
1230 protected void pushNewLabelCounter() {
1231         try {
1232                 this.labelCounterStack[++this.labelCounterPtr] = 0;
1233         } catch (IndexOutOfBoundsException e) {
1234                 int oldStackLength = this.labelCounterStack.length;
1235                 int[] oldStack = this.labelCounterStack;
1236                 this.labelCounterStack = new int[oldStackLength + StackIncrement];
1237                 System.arraycopy(oldStack, 0, this.labelCounterStack, 0, oldStackLength);
1238                 this.labelCounterStack[this.labelCounterPtr] = 0;
1239         }
1240 }
1241 /**
1242  * Pushes the given invocation type (one of the invocation type constants) on the invocation type stack,
1243  * and the given qualifier (an expression pointer to the expression stack) on the qualifier stack.
1244  */
1245 protected void pushOnInvocationStacks(int invocationType, int qualifierExprPtr) {
1246         // NB: invocationPtr has already been incremented by a call to pushOnSelectorStack()
1247         try {
1248                 this.invocationTypeStack[this.invocationPtr] = invocationType;
1249                 this.qualifierStack[this.invocationPtr] = qualifierExprPtr;
1250         } catch (IndexOutOfBoundsException e) {
1251                 int oldStackLength = this.invocationTypeStack.length;
1252                 int oldInvocationTypeStack[] = this.invocationTypeStack;
1253                 int oldQualifierStack[] = this.qualifierStack;
1254                 this.invocationTypeStack = new int[oldStackLength + StackIncrement];
1255                 this.qualifierStack = new int[oldStackLength + StackIncrement];
1256                 System.arraycopy(oldInvocationTypeStack, 0, this.invocationTypeStack, 0, oldStackLength);
1257                 System.arraycopy(oldQualifierStack, 0, this.qualifierStack, 0, oldStackLength);
1258                 this.invocationTypeStack[this.invocationPtr] = invocationType;
1259                 this.qualifierStack[this.invocationPtr] = qualifierExprPtr;
1260         }
1261 }
1262 public void recordCompletionOnReference(){
1263
1264         if (currentElement instanceof RecoveredType){
1265                 RecoveredType recoveredType = (RecoveredType)currentElement;
1266
1267                 /* filter out cases where scanner is still inside type header */
1268                 if (!recoveredType.foundOpeningBrace) return;
1269                 
1270                 /* generate a pseudo field with a completion on type reference */       
1271                 currentElement.add(
1272                         new CompletionOnFieldType(this.getTypeReference(0), false), 0);
1273                 return;
1274         }
1275         if (!diet) return; // only record references attached to types
1276
1277 }
1278 protected void reportSyntaxError(int act, int currentKind, int stateStackTop) {
1279
1280         /* Intercept error state on EOF inside method bodies, due to 
1281            cursor location being used as an EOF position.
1282         */
1283         if (!diet && currentToken == TokenNameEOF) return;
1284         super.reportSyntaxError(act, currentKind, stateStackTop);
1285 }
1286 /*
1287  * Reset internal state after completion is over
1288  */
1289  
1290 public void reset() {
1291         super.reset();
1292         this.cursorLocation = 0;
1293 }
1294 /*
1295  * Reset internal state after completion is over
1296  */
1297  
1298 public void resetAfterCompletion() {
1299         this.cursorLocation = 0;
1300         this.flushAssistState();
1301 }
1302 /*
1303  * Reset context so as to resume to regular parse loop
1304  * If unable to reset for resuming, answers false.
1305  *
1306  * Move checkpoint location, reset internal stacks and
1307  * decide which grammar goal is activated.
1308  */
1309 protected boolean resumeAfterRecovery() {
1310         if (this.assistNode != null) {
1311                 /* if reached [eof] inside method body, but still inside nested type,
1312                         or inside a field initializer, should continue in diet mode until 
1313                         the end of the method body or compilation unit */
1314                 if ((scanner.eofPosition == cursorLocation+1)
1315                         && (!(referenceContext instanceof CompilationUnitDeclaration) 
1316                                 || insideFieldInitialization())) {
1317
1318                         /*      disabled since does not handle possible field/message refs, i.e. Obj[ASSIST HERE]ect.registerNatives()              
1319                         // consume extra tokens which were part of the qualified reference
1320                         //   so that the replaced source comprises them as well 
1321                         if (this.assistNode instanceof NameReference){
1322                                 int oldEof = scanner.eofPosition;
1323                                 scanner.eofPosition = currentElement.topElement().sourceEnd()+1;
1324                                 scanner.currentPosition = this.cursorLocation+1;
1325                                 int token = -1;
1326                                 try {
1327                                         do {
1328                                                 // first token might not have to be a dot
1329                                                 if (token >= 0 || !this.completionBehindDot){
1330                                                         if ((token = scanner.getNextToken()) != TokenNameDOT) break;
1331                                                 }
1332                                                 if ((token = scanner.getNextToken()) != TokenNameIdentifier) break;
1333                                                 this.assistNode.sourceEnd = scanner.currentPosition - 1;
1334                                         } while (token != TokenNameEOF);
1335                                 } catch (InvalidInputException e){
1336                                 } finally {
1337                                         scanner.eofPosition = oldEof;
1338                                 }
1339                         }
1340                         */                      
1341                         /* restart in diet mode for finding sibling constructs */
1342                         if (currentElement.enclosingType() != null){
1343                                 lastCheckPoint = this.assistNode.sourceEnd+1;
1344                                 int end = currentElement.topElement().sourceEnd();
1345                                 scanner.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end;
1346                         } else {
1347                                 this.resetStacks();
1348                                 return false;   
1349                         }
1350                 }
1351         }
1352         return super.resumeAfterRecovery();
1353 }
1354 public void setAssistIdentifier(char[] assistIdent){
1355         ((CompletionScanner)scanner).completionIdentifier = assistIdent;
1356 }
1357 /**
1358  * Stores the labels left on the identifier stack if they have not been stored yet.
1359  */
1360 private void storeLabelsIfNeeded() {
1361 //      int counter = this.labelCounterPtr >= 0 ? this.labelCounterStack[this.labelCounterPtr] : 0;
1362 //      if (this.labels == null && this.identifierPtr >= 0) {
1363 //              this.labels = new char[counter][];
1364 //              System.arraycopy(this.identifierStack, this.identifierPtr - counter + 1, this.labels, 0, counter);
1365 //      }
1366 //      this.identifierPtr -= counter;
1367 //      this.identifierLengthPtr -= counter; // labels have not been concatenated yet
1368 }
1369 /*
1370  * Update recovery state based on current parser/scanner state
1371  */
1372 protected void updateRecoveryState() {
1373
1374         /* expose parser state to recovery state */
1375         currentElement.updateFromParserState();
1376
1377         /* may be able to retrieve completionNode as an orphan, and then attach it */
1378         this.completionIdentifierCheck();
1379         this.attachOrphanCompletionNode();
1380         
1381         // if an assist node has been found and a recovered element exists,
1382         // mark enclosing blocks as to be preserved
1383         if (this.assistNode != null && this.currentElement != null) {
1384                 currentElement.preserveEnclosingBlocks();
1385         }
1386         
1387         /* check and update recovered state based on current token,
1388                 this action is also performed when shifting token after recovery
1389                 got activated once. 
1390         */
1391         this.recoveryTokenCheck();
1392 }
1393
1394 protected LocalDeclaration createLocalDeclaration(Expression initialization, char[] name, int sourceStart, int sourceEnd) {
1395         if (this.indexOfAssistIdentifier() < 0) {
1396                 return super.createLocalDeclaration(initialization, name, sourceStart, sourceEnd);
1397         } else {
1398                 CompletionOnLocalName local = new CompletionOnLocalName(initialization, name, sourceStart, sourceEnd);
1399                 this.assistNode = local;
1400                 this.lastCheckPoint = sourceEnd + 1;
1401                 return local;
1402         }
1403 }
1404
1405 protected FieldDeclaration createFieldDeclaration(Expression initialization, char[] name, int sourceStart, int sourceEnd) {
1406         if (this.indexOfAssistIdentifier() < 0) {
1407                 return super.createFieldDeclaration(initialization, name, sourceStart, sourceEnd);
1408         } else {
1409                 CompletionOnFieldName field = new CompletionOnFieldName(initialization, name, sourceStart, sourceEnd);
1410                 this.assistNode = field;
1411                 this.lastCheckPoint = sourceEnd + 1;
1412                 return field;
1413         }
1414 }
1415
1416 }