fixed accelerator problem; slightly improved PHP Perspective
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / codeassist / impl / AssistParser.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.impl;
12
13 /*
14  * Parser extension for code assist task
15  *
16  */
17
18 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
19 import net.sourceforge.phpdt.internal.compiler.ast.AstNode;
20 import net.sourceforge.phpdt.internal.compiler.ast.Block;
21 import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
22 import net.sourceforge.phpdt.internal.compiler.ast.ConstructorDeclaration;
23 import net.sourceforge.phpdt.internal.compiler.ast.ExplicitConstructorCall;
24 import net.sourceforge.phpdt.internal.compiler.ast.Expression;
25 import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
26 import net.sourceforge.phpdt.internal.compiler.ast.ImportReference;
27 import net.sourceforge.phpdt.internal.compiler.ast.Initializer;
28 import net.sourceforge.phpdt.internal.compiler.ast.LocalDeclaration;
29 import net.sourceforge.phpdt.internal.compiler.ast.MessageSend;
30 import net.sourceforge.phpdt.internal.compiler.ast.MethodDeclaration;
31 import net.sourceforge.phpdt.internal.compiler.ast.NameReference;
32 import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
33 import net.sourceforge.phpdt.internal.compiler.ast.TypeReference;
34 import net.sourceforge.phpdt.internal.compiler.parser.Parser;
35 import net.sourceforge.phpdt.internal.compiler.parser.RecoveredElement;
36 import net.sourceforge.phpdt.internal.compiler.parser.RecoveredField;
37 import net.sourceforge.phpdt.internal.compiler.parser.RecoveredInitializer;
38 import net.sourceforge.phpdt.internal.compiler.parser.RecoveredMethod;
39 import net.sourceforge.phpdt.internal.compiler.parser.RecoveredType;
40 import net.sourceforge.phpdt.internal.compiler.parser.RecoveredUnit;
41 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
42 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
43
44 public abstract class AssistParser extends Parser {
45
46         public AstNode assistNode;
47         public boolean isOrphanCompletionNode;
48                 
49         /* recovery */
50         int[] blockStarts = new int[30];
51
52         // the previous token read by the scanner
53         protected int previousToken;
54
55         // the index in the identifier stack of the previous identifier
56         protected int previousIdentifierPtr;
57         
58         // the stacks of selectors for invocations (ie. method invocations, allocation expressions and
59         // explicit constructor invocations)
60         // the selector stack contains pointers to the identifier stack or one of the selector constants below
61         protected int invocationPtr;
62         protected int[] selectorStack = new int[StackIncrement];
63
64         // selector constants
65         protected static final int THIS_CONSTRUCTOR = -1;
66         protected static final int SUPER_CONSTRUCTOR = -2;
67
68         // whether the parser is in a field initializer
69         // (false is pushed each time a new type is entered,
70         //  it is changed to true when the initializer is entered,
71         //  it is changed back to false when the initializer is exited,
72         //  and it is poped when the type is exited)
73         protected int inFieldInitializationPtr;
74         protected boolean[] inFieldInitializationStack = new boolean[StackIncrement];
75
76         // whether the parser is in a method, constructor or initializer
77         // (false is pushed each time a new type is entered,
78         //  it is changed to true when the method is entered,
79         //  it is changed back to false when the method is exited,
80         //  and it is poped when the type is exited)
81         protected int inMethodPtr;
82         protected boolean[] inMethodStack = new boolean[StackIncrement];
83 public AssistParser(ProblemReporter problemReporter, boolean assertMode) {
84         super(problemReporter, true, assertMode);
85 }
86 public abstract char[] assistIdentifier();
87 public int bodyEnd(AbstractMethodDeclaration method){
88         return method.declarationSourceEnd;
89 }
90 public int bodyEnd(Initializer initializer){
91         return initializer.declarationSourceEnd;
92 }
93 /*
94  * Build initial recovery state.
95  * Recovery state is inferred from the current state of the parser (reduced node stack).
96  */
97 public RecoveredElement buildInitialRecoveryState(){
98
99         /* recovery in unit structure */
100         if (referenceContext instanceof CompilationUnitDeclaration){
101                 RecoveredElement element = super.buildInitialRecoveryState();
102                 flushAssistState();
103                 initInMethodAndInFieldInitializationStack(element);
104                 return element;
105         }
106
107         /* recovery in method body */
108         lastCheckPoint = 0;
109
110         RecoveredElement element = null;
111         if (referenceContext instanceof AbstractMethodDeclaration){
112                 element = new RecoveredMethod((AbstractMethodDeclaration) referenceContext, null, 0, this);
113                 lastCheckPoint = ((AbstractMethodDeclaration) referenceContext).bodyStart;
114         } else {
115                 /* Initializer bodies are parsed in the context of the type declaration, we must thus search it inside */
116                 if (referenceContext instanceof TypeDeclaration){
117                         TypeDeclaration type = (TypeDeclaration) referenceContext;
118                         for (int i = 0; i < type.fields.length; i++){
119                                 FieldDeclaration field = type.fields[i];                                        
120                                 if (!field.isField()
121                                                 && field.declarationSourceStart <= scanner.initialPosition
122                                                 && scanner.initialPosition <= field.declarationSourceEnd
123                                                 && scanner.eofPosition <= field.declarationSourceEnd+1){
124                                         element = new RecoveredInitializer((Initializer) field, null, 1, this);
125                                         lastCheckPoint = field.declarationSourceStart;                                  
126                                         break;
127                                 }
128                         }
129                 } 
130         }
131
132         if (element == null) return element;
133
134         /* add initial block */
135         Block block = new Block(0);
136         int lastStart = blockStarts[0];
137         block.sourceStart = lastStart;
138         element = element.add(block, 1);
139         int blockIndex = 1;     // ignore first block start, since manually rebuilt here
140
141         for(int i = 0; i <= astPtr; i++){
142                 AstNode node = astStack[i];
143
144                 /* check for intermediate block creation, so recovery can properly close them afterwards */
145                 int nodeStart = node.sourceStart;
146                 for (int j = blockIndex; j <= realBlockPtr; j++){
147                         if (blockStarts[j] > nodeStart){
148                                 blockIndex = j; // shift the index to the new block
149                                 break;
150                         }
151                         if (blockStarts[j] != lastStart){ // avoid multiple block if at same position
152                                 block = new Block(0);
153                                 block.sourceStart = lastStart = blockStarts[j];
154                                 element = element.add(block, 1);
155                         }
156                         blockIndex = j+1; // shift the index to the new block
157                 }
158                 if (node instanceof LocalDeclaration){
159                         LocalDeclaration local = (LocalDeclaration) node;
160                         if (local.declarationSourceEnd == 0){
161                                 element = element.add(local, 0);
162                                 if (local.initialization == null){
163                                         lastCheckPoint = local.sourceEnd + 1;
164                                 } else {
165                                         lastCheckPoint = local.initialization.sourceEnd + 1;
166                                 }
167                         } else {
168                                 element = element.add(local, 0);
169                                 lastCheckPoint = local.declarationSourceEnd + 1;
170                         }
171                         continue;
172                 }               
173                 if (node instanceof AbstractMethodDeclaration){
174                         AbstractMethodDeclaration method = (AbstractMethodDeclaration) node;
175                         if (method.declarationSourceEnd == 0){
176                                 element = element.add(method, 0);
177                                 lastCheckPoint = method.bodyStart;
178                         } else {
179                                 element = element.add(method, 0);
180                                 lastCheckPoint = method.declarationSourceEnd + 1;
181                         }
182                         continue;
183                 }
184                 if (node instanceof Initializer){
185                         Initializer initializer = (Initializer) node;
186                         if (initializer.declarationSourceEnd == 0){
187                                 element = element.add(initializer, 1);
188                                 lastCheckPoint = initializer.bodyStart;                         
189                         } else {
190                                 element = element.add(initializer, 0);
191                                 lastCheckPoint = initializer.declarationSourceEnd + 1;
192                         }
193                         continue;
194                 }               
195                 if (node instanceof FieldDeclaration){
196                         FieldDeclaration field = (FieldDeclaration) node;
197                         if (field.declarationSourceEnd == 0){
198                                 element = element.add(field, 0);
199                                 if (field.initialization == null){
200                                         lastCheckPoint = field.sourceEnd + 1;
201                                 } else {
202                                         lastCheckPoint = field.initialization.sourceEnd + 1;
203                                 }
204                         } else {
205                                 element = element.add(field, 0);
206                                 lastCheckPoint = field.declarationSourceEnd + 1;
207                         }
208                         continue;
209                 }
210                 if (node instanceof TypeDeclaration){
211                         TypeDeclaration type = (TypeDeclaration) node;
212                         if (type.declarationSourceEnd == 0){
213                                 element = element.add(type, 0); 
214                                 lastCheckPoint = type.bodyStart;
215                         } else {
216                                 element = element.add(type, 0);                         
217                                 lastCheckPoint = type.declarationSourceEnd + 1;
218                         }
219                         continue;
220                 }
221                 if (node instanceof ImportReference){
222                         ImportReference importRef = (ImportReference) node;
223                         element = element.add(importRef, 0);
224                         lastCheckPoint = importRef.declarationSourceEnd + 1;
225                 }
226         }
227         if (this.currentToken == TokenNameRBRACE) {
228                 this.currentToken = 0; // closing brace has already been taken care of
229         }
230
231         /* might need some extra block (after the last reduced node) */
232         int pos = this.assistNode == null ? lastCheckPoint : this.assistNode.sourceStart;
233         for (int j = blockIndex; j <= realBlockPtr; j++){
234                 if ((blockStarts[j] < pos) && (blockStarts[j] != lastStart)){ // avoid multiple block if at same position
235                         block = new Block(0);
236                         block.sourceStart = lastStart = blockStarts[j];
237                         element = element.add(block, 1);
238                 }
239         }
240         
241         initInMethodAndInFieldInitializationStack(element);
242         return element;
243 }
244 protected void consumeClassBodyDeclarationsopt() {
245         super.consumeClassBodyDeclarationsopt();
246         this.inFieldInitializationPtr--;
247         this.inMethodPtr--;
248 }
249 protected void consumeClassBodyopt() {
250         super.consumeClassBodyopt();
251         this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
252 }
253 protected void consumeClassHeader() {
254         super.consumeClassHeader();
255         this.pushNotInInitializer();
256         this.pushNotInMethod();
257 }
258 protected void consumeConstructorBody() {
259         super.consumeConstructorBody();
260         this.inMethodStack[this.inMethodPtr] = false;
261 }
262 protected void consumeConstructorHeader() {
263         super.consumeConstructorHeader();
264         this.inMethodStack[this.inMethodPtr] = true;
265 }
266 protected void consumeEmptyClassBodyDeclarationsopt() {
267         super.consumeEmptyClassBodyDeclarationsopt();
268         this.inFieldInitializationPtr--;
269         this.inMethodPtr--;
270 }
271 protected void consumeEnterAnonymousClassBody() {
272         super.consumeEnterAnonymousClassBody();
273         this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
274         this.pushNotInInitializer();
275         this.pushNotInMethod();
276 }
277 protected void consumeExplicitConstructorInvocation(int flag, int recFlag) {
278         super.consumeExplicitConstructorInvocation(flag, recFlag);
279         this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
280 }
281 protected void consumeForceNoDiet() {
282         super.consumeForceNoDiet();
283         // if we are not in a method (ie. we are not in a local variable initializer)
284         // then we are entering a field initializer
285         if (!this.inMethodStack[this.inMethodPtr]) {
286                 this.inFieldInitializationStack[this.inFieldInitializationPtr] = true;
287         }
288 }
289 protected void consumeInterfaceHeader() {
290         super.consumeInterfaceHeader();
291         this.pushNotInInitializer();
292         this.pushNotInMethod();
293 }
294 protected void consumeInterfaceMemberDeclarationsopt() {
295         super.consumeInterfaceMemberDeclarationsopt();
296         this.inFieldInitializationPtr--;
297         this.inMethodPtr--;
298 }
299 protected void consumeMethodBody() {
300         super.consumeMethodBody();
301         this.inMethodStack[this.inMethodPtr] = false;
302 }
303 protected void consumeMethodHeader() {
304         super.consumeMethodHeader();
305         this.inMethodStack[this.inMethodPtr] = true;
306 }
307 protected void consumeMethodInvocationName() {
308         super.consumeMethodInvocationName();
309         this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
310         MessageSend messageSend = (MessageSend)expressionStack[expressionPtr];
311         if (messageSend == assistNode){
312                 this.lastCheckPoint = messageSend.sourceEnd + 1;
313         }
314 }
315 protected void consumeMethodInvocationPrimary() {
316         super.consumeMethodInvocationPrimary();
317         this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer
318         MessageSend messageSend = (MessageSend)expressionStack[expressionPtr];
319         if (messageSend == assistNode){
320                 this.lastCheckPoint = messageSend.sourceEnd + 1;
321         }
322 }
323 protected void consumeMethodInvocationSuper() {
324         super.consumeMethodInvocationSuper();
325         this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer 
326         MessageSend messageSend = (MessageSend)expressionStack[expressionPtr];
327         if (messageSend == assistNode){
328                 this.lastCheckPoint = messageSend.sourceEnd + 1;
329         }
330 }
331 protected void consumeNestedMethod() {
332         super.consumeNestedMethod();
333         this.inMethodStack[this.inMethodPtr] = true;
334 }
335 protected void consumeOpenBlock() {
336         // OpenBlock ::= $empty
337
338         super.consumeOpenBlock();
339         try {
340                 blockStarts[realBlockPtr] = scanner.startPosition;
341         } catch (IndexOutOfBoundsException e) {
342                 //realBlockPtr is correct 
343                 int oldStackLength = blockStarts.length;
344                 int oldStack[] = blockStarts;
345                 blockStarts = new int[oldStackLength + StackIncrement];
346                 System.arraycopy(oldStack, 0, blockStarts, 0, oldStackLength);
347                 blockStarts[realBlockPtr] = scanner.startPosition;
348         }
349 }
350 protected void consumePackageDeclarationName() {
351         // PackageDeclarationName ::= 'package' Name
352         /* build an ImportRef build from the last name 
353         stored in the identifier stack. */
354
355         int index;
356
357         /* no need to take action if not inside assist identifiers */
358         if ((index = indexOfAssistIdentifier()) < 0) {
359                 super.consumePackageDeclarationName();
360                 return;
361         }
362         /* retrieve identifiers subset and whole positions, the assist node positions
363                 should include the entire replaced source. */
364         int length = identifierLengthStack[identifierLengthPtr];
365         char[][] subset = identifierSubSet(index+1); // include the assistIdentifier
366         identifierLengthPtr--;
367         identifierPtr -= length;
368         long[] positions = new long[length];
369         System.arraycopy(
370                 identifierPositionStack, 
371                 identifierPtr + 1, 
372                 positions, 
373                 0, 
374                 length); 
375
376         /* build specific assist node on package statement */
377         ImportReference reference = this.createAssistPackageReference(subset, positions);
378         assistNode = reference;
379         this.lastCheckPoint = reference.sourceEnd + 1;
380         compilationUnit.currentPackage = reference; 
381
382         if (currentToken == TokenNameSEMICOLON){
383                 reference.declarationSourceEnd = scanner.currentPosition - 1;
384         } else {
385                 reference.declarationSourceEnd = (int) positions[length-1];
386         }
387         //endPosition is just before the ;
388         reference.declarationSourceStart = intStack[intPtr--];
389         // flush annotations defined prior to import statements
390         reference.declarationSourceEnd = this.flushAnnotationsDefinedPriorTo(reference.declarationSourceEnd);
391
392         // recovery
393         if (currentElement != null){
394                 lastCheckPoint = reference.declarationSourceEnd+1;
395                 restartRecovery = true; // used to avoid branching back into the regular automaton              
396         }       
397 }
398 protected void consumeRestoreDiet() {
399         super.consumeRestoreDiet();
400         // if we are not in a method (ie. we were not in a local variable initializer)
401         // then we are exiting a field initializer
402         if (!this.inMethodStack[this.inMethodPtr]) {
403                 this.inFieldInitializationStack[this.inFieldInitializationPtr] = false;
404         }
405 }
406 protected void consumeSingleTypeImportDeclarationName() {
407         // SingleTypeImportDeclarationName ::= 'import' Name
408         /* push an ImportRef build from the last name 
409         stored in the identifier stack. */
410
411         int index;
412
413         /* no need to take action if not inside assist identifiers */
414         if ((index = indexOfAssistIdentifier()) < 0) {
415                 super.consumeSingleTypeImportDeclarationName();
416                 return;
417         }
418         /* retrieve identifiers subset and whole positions, the assist node positions
419                 should include the entire replaced source. */
420         int length = identifierLengthStack[identifierLengthPtr];
421         char[][] subset = identifierSubSet(index+1); // include the assistIdentifier
422         identifierLengthPtr--;
423         identifierPtr -= length;
424         long[] positions = new long[length];
425         System.arraycopy(
426                 identifierPositionStack, 
427                 identifierPtr + 1, 
428                 positions, 
429                 0, 
430                 length); 
431
432         /* build specific assist node on import statement */
433         ImportReference reference = this.createAssistImportReference(subset, positions);
434         assistNode = reference;
435         this.lastCheckPoint = reference.sourceEnd + 1;
436
437         pushOnAstStack(reference);
438
439         if (currentToken == TokenNameSEMICOLON){
440                 reference.declarationSourceEnd = scanner.currentPosition - 1;
441         } else {
442                 reference.declarationSourceEnd = (int) positions[length-1];
443         }
444         //endPosition is just before the ;
445         reference.declarationSourceStart = intStack[intPtr--];
446         // flush annotations defined prior to import statements
447         reference.declarationSourceEnd = this.flushAnnotationsDefinedPriorTo(reference.declarationSourceEnd);
448
449         // recovery
450         if (currentElement != null){
451                 lastCheckPoint = reference.declarationSourceEnd+1;
452                 currentElement = currentElement.add(reference, 0);
453                 lastIgnoredToken = -1;
454                 restartRecovery = true; // used to avoid branching back into the regular automaton              
455         }
456 }
457 protected void consumeStaticInitializer() {
458         super.consumeStaticInitializer();
459         this.inMethodStack[this.inMethodPtr] = false;
460 }
461 protected void consumeStaticOnly() {
462         super.consumeStaticOnly();
463         this.inMethodStack[this.inMethodPtr] = true;
464 }
465 protected void consumeToken(int token) {
466         super.consumeToken(token);
467         // register message send selector only if inside a method or if looking at a field initializer 
468         // and if the current token is an open parenthesis
469         if ((this.inMethodStack[this.inMethodPtr] || this.inFieldInitializationStack[this.inFieldInitializationPtr]) && token == TokenNameLPAREN) {
470                 switch (this.previousToken) {
471                         case TokenNameIdentifier:
472                                 this.pushOnSelectorStack(this.identifierPtr);
473                                 break;
474 //                      case TokenNamethis: // explicit constructor invocation, eg. this(1, 2)
475 //                              this.pushOnSelectorStack(THIS_CONSTRUCTOR);
476 //                              break;
477 //                      case TokenNamesuper: // explicit constructor invocation, eg. super(1, 2)
478 //                              this.pushOnSelectorStack(SUPER_CONSTRUCTOR);
479 //                              break;
480                 }
481         }
482         this.previousToken = token;
483         if (token == TokenNameIdentifier) {
484                 this.previousIdentifierPtr = this.identifierPtr;
485         }
486 }
487 protected void consumeTypeImportOnDemandDeclarationName() {
488         // TypeImportOnDemandDeclarationName ::= 'import' Name '.' '*'
489         /* push an ImportRef build from the last name 
490         stored in the identifier stack. */
491
492         int index;
493
494         /* no need to take action if not inside assist identifiers */
495         if ((index = indexOfAssistIdentifier()) < 0) {
496                 super.consumeTypeImportOnDemandDeclarationName();
497                 return;
498         }
499         /* retrieve identifiers subset and whole positions, the assist node positions
500                 should include the entire replaced source. */
501         int length = identifierLengthStack[identifierLengthPtr];
502         char[][] subset = identifierSubSet(index+1); // include the assistIdentifier
503         identifierLengthPtr--;
504         identifierPtr -= length;
505         long[] positions = new long[length];
506         System.arraycopy(
507                 identifierPositionStack, 
508                 identifierPtr + 1, 
509                 positions, 
510                 0, 
511                 length); 
512
513         /* build specific assist node on import statement */
514         ImportReference reference = this.createAssistImportReference(subset, positions);
515         reference.onDemand = true;
516         assistNode = reference;
517         this.lastCheckPoint = reference.sourceEnd + 1;
518
519         pushOnAstStack(reference);
520
521         if (currentToken == TokenNameSEMICOLON){
522                 reference.declarationSourceEnd = scanner.currentPosition - 1;
523         } else {
524                 reference.declarationSourceEnd = (int) positions[length-1];
525         }
526         //endPosition is just before the ;
527         reference.declarationSourceStart = intStack[intPtr--];
528         // flush annotations defined prior to import statements
529         reference.declarationSourceEnd = this.flushAnnotationsDefinedPriorTo(reference.declarationSourceEnd);
530
531         // recovery
532         if (currentElement != null){
533                 lastCheckPoint = reference.declarationSourceEnd+1;
534                 currentElement = currentElement.add(reference, 0);
535                 lastIgnoredToken = -1;
536                 restartRecovery = true; // used to avoid branching back into the regular automaton              
537         }
538 }
539 public abstract ImportReference createAssistImportReference(char[][] tokens, long[] positions);
540 public abstract ImportReference createAssistPackageReference(char[][] tokens, long[] positions);
541 public abstract NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions);
542 public abstract TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] name, long[] positions);
543 public abstract NameReference createSingleAssistNameReference(char[] name, long position);
544 public abstract TypeReference createSingleAssistTypeReference(char[] name, long position);
545 /*
546  * Flush parser/scanner state regarding to code assist
547  */
548 public void flushAssistState(){
549         this.assistNode = null;
550         this.isOrphanCompletionNode = false;
551         this.setAssistIdentifier(null);
552 }
553 /*
554  * Build specific type reference nodes in case the cursor is located inside the type reference
555  */
556 protected TypeReference getTypeReference(int dim) {
557
558         int index;
559
560         /* no need to take action if not inside completed identifiers */
561         if ((index = indexOfAssistIdentifier()) < 0) {
562                 return super.getTypeReference(dim);
563         }
564
565         /* retrieve identifiers subset and whole positions, the assist node positions
566                 should include the entire replaced source. */
567         int length = identifierLengthStack[identifierLengthPtr];
568         char[][] subset = identifierSubSet(index);
569         identifierLengthPtr--;
570         identifierPtr -= length;
571         long[] positions = new long[length];
572         System.arraycopy(
573                 identifierPositionStack, 
574                 identifierPtr + 1, 
575                 positions, 
576                 0, 
577                 length); 
578
579         /* build specific assist on type reference */
580         TypeReference reference;
581         if (index == 0) {
582                 /* assist inside first identifier */
583                 reference = this.createSingleAssistTypeReference(
584                                                 assistIdentifier(), 
585                                                 positions[0]);
586         } else {
587                 /* assist inside subsequent identifier */
588                 reference =     this.createQualifiedAssistTypeReference(
589                                                 subset,  
590                                                 assistIdentifier(), 
591                                                 positions);
592         }
593         assistNode = reference;
594         this.lastCheckPoint = reference.sourceEnd + 1;
595         return reference;
596 }
597 /*
598  * Copy of code from superclass with the following change:
599  * In the case of qualified name reference if the cursor location is on the 
600  * qualified name reference, then create a CompletionOnQualifiedNameReference 
601  * instead.
602  */
603 protected NameReference getUnspecifiedReferenceOptimized() {
604
605         int completionIndex;
606
607         /* no need to take action if not inside completed identifiers */
608         if ((completionIndex = indexOfAssistIdentifier()) < 0) {
609                 return super.getUnspecifiedReferenceOptimized();
610         }
611
612         /* retrieve identifiers subset and whole positions, the completion node positions
613                 should include the entire replaced source. */
614         int length = identifierLengthStack[identifierLengthPtr];
615         char[][] subset = identifierSubSet(completionIndex);
616         identifierLengthPtr--;
617         identifierPtr -= length;
618         long[] positions = new long[length];
619         System.arraycopy(
620                 identifierPositionStack, 
621                 identifierPtr + 1, 
622                 positions, 
623                 0, 
624                 length);
625
626         /* build specific completion on name reference */
627         NameReference reference;
628         if (completionIndex == 0) {
629                 /* completion inside first identifier */
630                 reference = this.createSingleAssistNameReference(assistIdentifier(), positions[0]);
631         } else {
632                 /* completion inside subsequent identifier */
633                 reference = this.createQualifiedAssistNameReference(subset, assistIdentifier(), positions);
634         };
635         reference.bits &= ~AstNode.RestrictiveFlagMASK;
636         reference.bits |= LOCAL | FIELD;
637         
638         assistNode = reference;
639         lastCheckPoint = reference.sourceEnd + 1;
640         return reference;
641 }
642 public void goForBlockStatementsopt() {
643         //tells the scanner to go for block statements opt parsing
644
645         firstToken = TokenNameTWIDDLE;
646         scanner.recordLineSeparator = false;
647 }
648 public void goForConstructorBlockStatementsopt() {
649         //tells the scanner to go for constructor block statements opt parsing
650
651         firstToken = TokenNameNOT;
652         scanner.recordLineSeparator = false;
653 }
654 /*
655  * Retrieve a partial subset of a qualified name reference up to the completion point.
656  * It does not pop the actual awaiting identifiers, so as to be able to retrieve position
657  * information afterwards.
658  */
659 protected char[][] identifierSubSet(int subsetLength){
660
661         if (subsetLength == 0) return null;
662         
663         char[][] subset;
664         System.arraycopy(
665                 identifierStack,
666                 identifierPtr - identifierLengthStack[identifierLengthPtr] + 1,
667                 (subset = new char[subsetLength][]),
668                 0,
669                 subsetLength);
670         return subset;
671 }
672 /*
673  * Iterate the most recent group of awaiting identifiers (grouped for qualified name reference (eg. aa.bb.cc)
674  * so as to check whether one of them is the assist identifier.
675  * If so, then answer the index of the assist identifier (0 being the first identifier of the set).
676  *      eg. aa(0).bb(1).cc(2)
677  * If no assist identifier was found, answers -1.
678  */
679 protected int indexOfAssistIdentifier(){
680
681         if (identifierLengthPtr < 0){
682                 return -1; // no awaiting identifier
683         }
684
685         char[] assistIdentifier ;
686         if ((assistIdentifier = this.assistIdentifier()) == null){
687                 return -1; // no assist identifier found yet
688         }
689
690         // iterate awaiting identifiers backwards
691         int length = identifierLengthStack[identifierLengthPtr];
692         for (int i = 0; i < length; i++){ 
693                 if (identifierStack[identifierPtr - i] == assistIdentifier){
694                         return length - i - 1;
695                 }
696         }
697         // none of the awaiting identifiers is the completion one
698         return -1;
699 }
700 public void initialize() {
701         super.initialize();
702         this.flushAssistState();
703         this.invocationPtr = -1;
704         this.inMethodStack[this.inMethodPtr = 0] = false;
705         this.inFieldInitializationStack[this.inFieldInitializationPtr = 0] = false;
706         this.previousIdentifierPtr = -1;
707 }
708 public abstract void initializeScanner();
709
710 protected void initInMethodAndInFieldInitializationStack(RecoveredElement currentElement) {
711
712         int length = currentElement.depth() + 1;
713         int ptr = length;
714         boolean[] methodStack = new boolean[length];
715         boolean[] fieldInitializationStack = new boolean[length];
716         boolean inMethod = false;
717         boolean inFieldInitializer = false;
718         
719         RecoveredElement element = currentElement;
720         while(element != null){
721                 if(element instanceof RecoveredMethod ||
722                         element instanceof RecoveredInitializer) {
723                         if(element.parent == null) {
724                                 methodStack[--ptr] = true;
725                                 fieldInitializationStack[ptr] = false;
726                         }
727                         inMethod = true;
728                 } else if(element instanceof RecoveredField){
729                         inFieldInitializer = element.sourceEnd() == 0;
730                 } else if(element instanceof RecoveredType){
731                         methodStack[--ptr] = inMethod;
732                         fieldInitializationStack[ptr] = inFieldInitializer;
733         
734                         inMethod = false;
735                         inFieldInitializer = false;
736                 } else if(element instanceof RecoveredUnit) {
737                         methodStack[--ptr] = false;
738                         fieldInitializationStack[ptr] = false;
739                 }
740                 element = element.parent;
741         }
742         
743         inMethodPtr = length - ptr - 1;
744         inFieldInitializationPtr = inMethodPtr;
745         System.arraycopy(methodStack, ptr, inMethodStack, 0, inMethodPtr + 1);
746         System.arraycopy(fieldInitializationStack, ptr, inFieldInitializationStack, 0, inFieldInitializationPtr + 1);
747         
748 }
749
750 /**
751  * Returns whether we are directly or indirectly inside a field initializer.
752  */
753 protected boolean insideFieldInitialization() {
754         for (int i = this.inFieldInitializationPtr; i >= 0; i--) {
755                 if (this.inFieldInitializationStack[i]) {
756                         return true;
757                 }
758         }
759         return false;
760 }
761 /**
762  * Parse the block statements inside the given method declaration and try to complete at the
763  * cursor location.
764  */
765 public void parseBlockStatements(AbstractMethodDeclaration md, CompilationUnitDeclaration unit) {
766         if (md instanceof MethodDeclaration) {
767                 parseBlockStatements((MethodDeclaration) md, unit);
768         } else if (md instanceof ConstructorDeclaration) {
769                 parseBlockStatements((ConstructorDeclaration) md, unit);
770         }
771 }
772 /**
773  * Parse the block statements inside the given constructor declaration and try to complete at the
774  * cursor location.
775  */
776 public void parseBlockStatements(ConstructorDeclaration cd, CompilationUnitDeclaration unit) {
777         //only parse the method body of cd
778         //fill out its statements
779
780         //convert bugs into parse error
781
782         initialize();
783         
784         // simulate goForConstructorBody except that we don't want to balance brackets because they are not going to be balanced
785         goForConstructorBlockStatementsopt();
786
787         referenceContext = cd;
788         compilationUnit = unit;
789
790         scanner.resetTo(cd.bodyStart, bodyEnd(cd));
791         consumeNestedMethod();
792         try {
793                 parse();
794         } catch (AbortCompilation ex) {
795                 lastAct = ERROR_ACTION;
796         }
797 }
798 /**
799  * Parse the block statements inside the given initializer and try to complete at the
800  * cursor location.
801  */
802 public void parseBlockStatements(
803         Initializer ini,
804         TypeDeclaration type, 
805         CompilationUnitDeclaration unit) {
806
807         initialize();
808
809         // simulate goForInitializer except that we don't want to balance brackets because they are not going to be balanced
810         goForBlockStatementsopt();
811         
812         referenceContext = type;
813         compilationUnit = unit;
814
815         scanner.resetTo(ini.sourceStart, bodyEnd(ini)); // just after the beginning {
816         consumeNestedMethod();
817         try {
818                 parse();
819         } catch (AbortCompilation ex) {
820                 lastAct = ERROR_ACTION;
821         } finally {
822                 nestedMethod[nestedType]--;
823         }
824 }
825 /**
826  * Parse the block statements inside the given method declaration and try to complete at the
827  * cursor location.
828  */
829 public void parseBlockStatements(MethodDeclaration md, CompilationUnitDeclaration unit) {
830         //only parse the method body of md
831         //fill out method statements
832
833         //convert bugs into parse error
834
835         if (md.isAbstract())
836                 return;
837         if (md.isNative())
838                 return;
839         if ((md.modifiers & AccSemicolonBody) != 0)
840                 return;
841
842         initialize();
843
844         // simulate goForMethodBody except that we don't want to balance brackets because they are not going to be balanced
845         goForBlockStatementsopt();
846
847         referenceContext = md;
848         compilationUnit = unit;
849         
850         scanner.resetTo(md.bodyStart, bodyEnd(md)); // reset the scanner to parser from { down to the cursor location
851         consumeNestedMethod();
852         try {
853                 parse();
854         } catch (AbortCompilation ex) {
855                 lastAct = ERROR_ACTION;
856         } finally {
857                 nestedMethod[nestedType]--;             
858         }
859 }
860 /*
861  * Prepares the state of the parser to go for BlockStatements.
862  */
863 protected void prepareForBlockStatements() {
864         this.nestedMethod[this.nestedType = 0] = 1;
865         this.variablesCounter[this.nestedType] = 0;
866         this.realBlockStack[this.realBlockPtr = 1] = 0;
867         this.invocationPtr = -1;
868 }
869 /*
870  * Pushes 'false' on the inInitializerStack.
871  */
872 protected void pushNotInInitializer() {
873         try {
874                 this.inFieldInitializationStack[++this.inFieldInitializationPtr] = false;
875         } catch (IndexOutOfBoundsException e) {
876                 //except in test's cases, it should never raise
877                 int oldStackLength = this.inFieldInitializationStack.length;
878                 System.arraycopy(this.inFieldInitializationStack , 0, (this.inFieldInitializationStack = new boolean[oldStackLength + StackIncrement]), 0, oldStackLength);
879                 this.inFieldInitializationStack[this.inFieldInitializationPtr] = false;
880         }
881 }
882 /*
883  * Pushes 'false' on the inMethodStack.
884  */
885 protected void pushNotInMethod() {
886         try {
887                 this.inMethodStack[++this.inMethodPtr] = false;
888         } catch (IndexOutOfBoundsException e) {
889                 //except in test's cases, it should never raise
890                 int oldStackLength = this.inMethodStack.length;
891                 System.arraycopy(this.inMethodStack , 0, (this.inMethodStack = new boolean[oldStackLength + StackIncrement]), 0, oldStackLength);
892                 this.inMethodStack[this.inMethodPtr] = false;
893         }
894 }
895 /**
896  * Pushes the given the given selector (an identifier pointer to the identifier stack) on the selector stack.
897  */
898 protected void pushOnSelectorStack(int selectorIdPtr) {
899         if (this.invocationPtr < -1) return;
900         try {
901                 this.selectorStack[++this.invocationPtr] = selectorIdPtr;
902         } catch (IndexOutOfBoundsException e) {
903                 int oldStackLength = this.selectorStack.length;
904                 int oldSelectorStack[] = this.selectorStack;
905                 this.selectorStack = new int[oldStackLength + StackIncrement];
906                 System.arraycopy(oldSelectorStack, 0, this.selectorStack, 0, oldStackLength);
907                 this.selectorStack[this.invocationPtr] = selectorIdPtr;
908         }
909 }
910 public void reset(){
911         this.flushAssistState();
912 }
913 /*
914  * Reset context so as to resume to regular parse loop
915  */
916 protected void resetStacks() {
917         super.resetStacks();
918         this.inFieldInitializationStack[this.inFieldInitializationPtr = 0] = false;
919         this.inMethodStack[this.inMethodPtr = 0] = false;
920 }
921 /*
922  * Reset context so as to resume to regular parse loop
923  * If unable to reset for resuming, answers false.
924  *
925  * Move checkpoint location, reset internal stacks and
926  * decide which grammar goal is activated.
927  */
928 protected boolean resumeAfterRecovery() {
929
930         // reset internal stacks 
931         astPtr = -1;
932         astLengthPtr = -1;
933         expressionPtr = -1;
934         expressionLengthPtr = -1;
935         identifierPtr = -1;     
936         identifierLengthPtr     = -1;
937         intPtr = -1;
938         dimensions = 0 ;
939         recoveredStaticInitializerStart = 0;
940
941         // if in diet mode, reset the diet counter because we're going to restart outside an initializer.
942         if (diet) dietInt = 0;
943
944         /* attempt to move checkpoint location */
945         if (!this.moveRecoveryCheckpoint()) return false;
946
947         initInMethodAndInFieldInitializationStack(currentElement);
948
949         // only look for headers
950         if (referenceContext instanceof CompilationUnitDeclaration
951                 || this.assistNode != null){
952                 
953                 if(inMethodStack[inMethodPtr] &&
954                         insideFieldInitialization() &&
955                         this.assistNode == null
956                         ){ 
957                         this.prepareForBlockStatements();
958                         goForBlockStatementsOrMethodHeaders();
959                 } else {
960                         nestedMethod[nestedType = 0] = 0;
961                         variablesCounter[nestedType] = 0;
962                         realBlockStack[realBlockPtr = 0] = 0;
963                         goForHeaders();
964                         diet = true; // passed this point, will not consider method bodies
965                 }
966                 return true;
967         }
968         if (referenceContext instanceof AbstractMethodDeclaration
969                 || referenceContext instanceof TypeDeclaration){
970                         
971                 if (currentElement instanceof RecoveredType){
972                         nestedMethod[nestedType = 0] = 0;
973                         variablesCounter[nestedType] = 0;
974                         realBlockStack[realBlockPtr = 0] = 0;
975                         goForHeaders();
976                 } else {
977                         this.prepareForBlockStatements();
978                         goForBlockStatementsOrMethodHeaders();
979                 }
980                 return true;
981         }
982         // does not know how to restart
983         return false;
984 }
985 public abstract void setAssistIdentifier(char[] assistIdent);
986 /**
987  * If the given ast node is inside an explicit constructor call
988  * then wrap it with a fake constructor call.
989  * Returns the wrapped completion node or the completion node itself.
990  */
991 protected AstNode wrapWithExplicitConstructorCallIfNeeded(AstNode ast) {
992         int selector;
993         if (ast != null && this.invocationPtr >= 0 && ast instanceof Expression &&
994                         (((selector = this.selectorStack[this.invocationPtr]) == THIS_CONSTRUCTOR) ||
995                         (selector == SUPER_CONSTRUCTOR))) {
996                 ExplicitConstructorCall call = new ExplicitConstructorCall(
997                         (selector == THIS_CONSTRUCTOR) ? 
998                                 ExplicitConstructorCall.This : 
999                                 ExplicitConstructorCall.Super
1000                 );
1001                 call.arguments = new Expression[] {(Expression)ast};
1002                 call.sourceStart = ast.sourceStart;
1003                 call.sourceEnd = ast.sourceEnd;
1004                 return call;
1005         } else {
1006                 return ast;
1007         }
1008 }
1009 }