1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.codeassist.impl;
14 * Parser extension for code assist task
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;
44 public abstract class AssistParser extends Parser {
46 public AstNode assistNode;
47 public boolean isOrphanCompletionNode;
50 int[] blockStarts = new int[30];
52 // the previous token read by the scanner
53 protected int previousToken;
55 // the index in the identifier stack of the previous identifier
56 protected int previousIdentifierPtr;
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];
65 protected static final int THIS_CONSTRUCTOR = -1;
66 protected static final int SUPER_CONSTRUCTOR = -2;
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];
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);
86 public abstract char[] assistIdentifier();
87 public int bodyEnd(AbstractMethodDeclaration method){
88 return method.declarationSourceEnd;
90 public int bodyEnd(Initializer initializer){
91 return initializer.declarationSourceEnd;
94 * Build initial recovery state.
95 * Recovery state is inferred from the current state of the parser (reduced node stack).
97 public RecoveredElement buildInitialRecoveryState(){
99 /* recovery in unit structure */
100 if (referenceContext instanceof CompilationUnitDeclaration){
101 RecoveredElement element = super.buildInitialRecoveryState();
103 initInMethodAndInFieldInitializationStack(element);
107 /* recovery in method body */
110 RecoveredElement element = null;
111 if (referenceContext instanceof AbstractMethodDeclaration){
112 element = new RecoveredMethod((AbstractMethodDeclaration) referenceContext, null, 0, this);
113 lastCheckPoint = ((AbstractMethodDeclaration) referenceContext).bodyStart;
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];
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;
132 if (element == null) return element;
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
141 for(int i = 0; i <= astPtr; i++){
142 AstNode node = astStack[i];
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
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);
156 blockIndex = j+1; // shift the index to the new block
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;
165 lastCheckPoint = local.initialization.sourceEnd + 1;
168 element = element.add(local, 0);
169 lastCheckPoint = local.declarationSourceEnd + 1;
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;
179 element = element.add(method, 0);
180 lastCheckPoint = method.declarationSourceEnd + 1;
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;
190 element = element.add(initializer, 0);
191 lastCheckPoint = initializer.declarationSourceEnd + 1;
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;
202 lastCheckPoint = field.initialization.sourceEnd + 1;
205 element = element.add(field, 0);
206 lastCheckPoint = field.declarationSourceEnd + 1;
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;
216 element = element.add(type, 0);
217 lastCheckPoint = type.declarationSourceEnd + 1;
221 if (node instanceof ImportReference){
222 ImportReference importRef = (ImportReference) node;
223 element = element.add(importRef, 0);
224 lastCheckPoint = importRef.declarationSourceEnd + 1;
227 if (this.currentToken == TokenNameRBRACE) {
228 this.currentToken = 0; // closing brace has already been taken care of
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);
241 initInMethodAndInFieldInitializationStack(element);
244 protected void consumeClassBodyDeclarationsopt() {
245 super.consumeClassBodyDeclarationsopt();
246 this.inFieldInitializationPtr--;
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
253 protected void consumeClassHeader() {
254 super.consumeClassHeader();
255 this.pushNotInInitializer();
256 this.pushNotInMethod();
258 protected void consumeConstructorBody() {
259 super.consumeConstructorBody();
260 this.inMethodStack[this.inMethodPtr] = false;
262 protected void consumeConstructorHeader() {
263 super.consumeConstructorHeader();
264 this.inMethodStack[this.inMethodPtr] = true;
266 protected void consumeEmptyClassBodyDeclarationsopt() {
267 super.consumeEmptyClassBodyDeclarationsopt();
268 this.inFieldInitializationPtr--;
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();
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
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;
289 protected void consumeInterfaceHeader() {
290 super.consumeInterfaceHeader();
291 this.pushNotInInitializer();
292 this.pushNotInMethod();
294 protected void consumeInterfaceMemberDeclarationsopt() {
295 super.consumeInterfaceMemberDeclarationsopt();
296 this.inFieldInitializationPtr--;
299 protected void consumeMethodBody() {
300 super.consumeMethodBody();
301 this.inMethodStack[this.inMethodPtr] = false;
303 protected void consumeMethodHeader() {
304 super.consumeMethodHeader();
305 this.inMethodStack[this.inMethodPtr] = true;
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;
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;
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;
331 protected void consumeNestedMethod() {
332 super.consumeNestedMethod();
333 this.inMethodStack[this.inMethodPtr] = true;
335 protected void consumeOpenBlock() {
336 // OpenBlock ::= $empty
338 super.consumeOpenBlock();
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;
350 protected void consumePackageDeclarationName() {
351 // PackageDeclarationName ::= 'package' Name
352 /* build an ImportRef build from the last name
353 stored in the identifier stack. */
357 /* no need to take action if not inside assist identifiers */
358 if ((index = indexOfAssistIdentifier()) < 0) {
359 super.consumePackageDeclarationName();
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];
370 identifierPositionStack,
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;
382 if (currentToken == TokenNameSEMICOLON){
383 reference.declarationSourceEnd = scanner.currentPosition - 1;
385 reference.declarationSourceEnd = (int) positions[length-1];
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);
393 if (currentElement != null){
394 lastCheckPoint = reference.declarationSourceEnd+1;
395 restartRecovery = true; // used to avoid branching back into the regular automaton
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;
406 protected void consumeSingleTypeImportDeclarationName() {
407 // SingleTypeImportDeclarationName ::= 'import' Name
408 /* push an ImportRef build from the last name
409 stored in the identifier stack. */
413 /* no need to take action if not inside assist identifiers */
414 if ((index = indexOfAssistIdentifier()) < 0) {
415 super.consumeSingleTypeImportDeclarationName();
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];
426 identifierPositionStack,
432 /* build specific assist node on import statement */
433 ImportReference reference = this.createAssistImportReference(subset, positions);
434 assistNode = reference;
435 this.lastCheckPoint = reference.sourceEnd + 1;
437 pushOnAstStack(reference);
439 if (currentToken == TokenNameSEMICOLON){
440 reference.declarationSourceEnd = scanner.currentPosition - 1;
442 reference.declarationSourceEnd = (int) positions[length-1];
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);
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
457 protected void consumeStaticInitializer() {
458 super.consumeStaticInitializer();
459 this.inMethodStack[this.inMethodPtr] = false;
461 protected void consumeStaticOnly() {
462 super.consumeStaticOnly();
463 this.inMethodStack[this.inMethodPtr] = true;
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);
474 // case TokenNamethis: // explicit constructor invocation, eg. this(1, 2)
475 // this.pushOnSelectorStack(THIS_CONSTRUCTOR);
477 // case TokenNamesuper: // explicit constructor invocation, eg. super(1, 2)
478 // this.pushOnSelectorStack(SUPER_CONSTRUCTOR);
482 this.previousToken = token;
483 if (token == TokenNameIdentifier) {
484 this.previousIdentifierPtr = this.identifierPtr;
487 protected void consumeTypeImportOnDemandDeclarationName() {
488 // TypeImportOnDemandDeclarationName ::= 'import' Name '.' '*'
489 /* push an ImportRef build from the last name
490 stored in the identifier stack. */
494 /* no need to take action if not inside assist identifiers */
495 if ((index = indexOfAssistIdentifier()) < 0) {
496 super.consumeTypeImportOnDemandDeclarationName();
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];
507 identifierPositionStack,
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;
519 pushOnAstStack(reference);
521 if (currentToken == TokenNameSEMICOLON){
522 reference.declarationSourceEnd = scanner.currentPosition - 1;
524 reference.declarationSourceEnd = (int) positions[length-1];
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);
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
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);
546 * Flush parser/scanner state regarding to code assist
548 public void flushAssistState(){
549 this.assistNode = null;
550 this.isOrphanCompletionNode = false;
551 this.setAssistIdentifier(null);
554 * Build specific type reference nodes in case the cursor is located inside the type reference
556 protected TypeReference getTypeReference(int dim) {
560 /* no need to take action if not inside completed identifiers */
561 if ((index = indexOfAssistIdentifier()) < 0) {
562 return super.getTypeReference(dim);
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];
573 identifierPositionStack,
579 /* build specific assist on type reference */
580 TypeReference reference;
582 /* assist inside first identifier */
583 reference = this.createSingleAssistTypeReference(
587 /* assist inside subsequent identifier */
588 reference = this.createQualifiedAssistTypeReference(
593 assistNode = reference;
594 this.lastCheckPoint = reference.sourceEnd + 1;
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
603 protected NameReference getUnspecifiedReferenceOptimized() {
607 /* no need to take action if not inside completed identifiers */
608 if ((completionIndex = indexOfAssistIdentifier()) < 0) {
609 return super.getUnspecifiedReferenceOptimized();
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];
620 identifierPositionStack,
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]);
632 /* completion inside subsequent identifier */
633 reference = this.createQualifiedAssistNameReference(subset, assistIdentifier(), positions);
635 reference.bits &= ~AstNode.RestrictiveFlagMASK;
636 reference.bits |= LOCAL | FIELD;
638 assistNode = reference;
639 lastCheckPoint = reference.sourceEnd + 1;
642 public void goForBlockStatementsopt() {
643 //tells the scanner to go for block statements opt parsing
645 firstToken = TokenNameTWIDDLE;
646 scanner.recordLineSeparator = false;
648 public void goForConstructorBlockStatementsopt() {
649 //tells the scanner to go for constructor block statements opt parsing
651 firstToken = TokenNameNOT;
652 scanner.recordLineSeparator = false;
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.
659 protected char[][] identifierSubSet(int subsetLength){
661 if (subsetLength == 0) return null;
666 identifierPtr - identifierLengthStack[identifierLengthPtr] + 1,
667 (subset = new char[subsetLength][]),
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.
679 protected int indexOfAssistIdentifier(){
681 if (identifierLengthPtr < 0){
682 return -1; // no awaiting identifier
685 char[] assistIdentifier ;
686 if ((assistIdentifier = this.assistIdentifier()) == null){
687 return -1; // no assist identifier found yet
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;
697 // none of the awaiting identifiers is the completion one
700 public void 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;
708 public abstract void initializeScanner();
710 protected void initInMethodAndInFieldInitializationStack(RecoveredElement currentElement) {
712 int length = currentElement.depth() + 1;
714 boolean[] methodStack = new boolean[length];
715 boolean[] fieldInitializationStack = new boolean[length];
716 boolean inMethod = false;
717 boolean inFieldInitializer = false;
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;
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;
735 inFieldInitializer = false;
736 } else if(element instanceof RecoveredUnit) {
737 methodStack[--ptr] = false;
738 fieldInitializationStack[ptr] = false;
740 element = element.parent;
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);
751 * Returns whether we are directly or indirectly inside a field initializer.
753 protected boolean insideFieldInitialization() {
754 for (int i = this.inFieldInitializationPtr; i >= 0; i--) {
755 if (this.inFieldInitializationStack[i]) {
762 * Parse the block statements inside the given method declaration and try to complete at the
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);
773 * Parse the block statements inside the given constructor declaration and try to complete at the
776 public void parseBlockStatements(ConstructorDeclaration cd, CompilationUnitDeclaration unit) {
777 //only parse the method body of cd
778 //fill out its statements
780 //convert bugs into parse error
784 // simulate goForConstructorBody except that we don't want to balance brackets because they are not going to be balanced
785 goForConstructorBlockStatementsopt();
787 referenceContext = cd;
788 compilationUnit = unit;
790 scanner.resetTo(cd.bodyStart, bodyEnd(cd));
791 consumeNestedMethod();
794 } catch (AbortCompilation ex) {
795 lastAct = ERROR_ACTION;
799 * Parse the block statements inside the given initializer and try to complete at the
802 public void parseBlockStatements(
804 TypeDeclaration type,
805 CompilationUnitDeclaration unit) {
809 // simulate goForInitializer except that we don't want to balance brackets because they are not going to be balanced
810 goForBlockStatementsopt();
812 referenceContext = type;
813 compilationUnit = unit;
815 scanner.resetTo(ini.sourceStart, bodyEnd(ini)); // just after the beginning {
816 consumeNestedMethod();
819 } catch (AbortCompilation ex) {
820 lastAct = ERROR_ACTION;
822 nestedMethod[nestedType]--;
826 * Parse the block statements inside the given method declaration and try to complete at the
829 public void parseBlockStatements(MethodDeclaration md, CompilationUnitDeclaration unit) {
830 //only parse the method body of md
831 //fill out method statements
833 //convert bugs into parse error
839 if ((md.modifiers & AccSemicolonBody) != 0)
844 // simulate goForMethodBody except that we don't want to balance brackets because they are not going to be balanced
845 goForBlockStatementsopt();
847 referenceContext = md;
848 compilationUnit = unit;
850 scanner.resetTo(md.bodyStart, bodyEnd(md)); // reset the scanner to parser from { down to the cursor location
851 consumeNestedMethod();
854 } catch (AbortCompilation ex) {
855 lastAct = ERROR_ACTION;
857 nestedMethod[nestedType]--;
861 * Prepares the state of the parser to go for BlockStatements.
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;
870 * Pushes 'false' on the inInitializerStack.
872 protected void pushNotInInitializer() {
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;
883 * Pushes 'false' on the inMethodStack.
885 protected void pushNotInMethod() {
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;
896 * Pushes the given the given selector (an identifier pointer to the identifier stack) on the selector stack.
898 protected void pushOnSelectorStack(int selectorIdPtr) {
899 if (this.invocationPtr < -1) return;
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;
911 this.flushAssistState();
914 * Reset context so as to resume to regular parse loop
916 protected void resetStacks() {
918 this.inFieldInitializationStack[this.inFieldInitializationPtr = 0] = false;
919 this.inMethodStack[this.inMethodPtr = 0] = false;
922 * Reset context so as to resume to regular parse loop
923 * If unable to reset for resuming, answers false.
925 * Move checkpoint location, reset internal stacks and
926 * decide which grammar goal is activated.
928 protected boolean resumeAfterRecovery() {
930 // reset internal stacks
934 expressionLengthPtr = -1;
936 identifierLengthPtr = -1;
939 recoveredStaticInitializerStart = 0;
941 // if in diet mode, reset the diet counter because we're going to restart outside an initializer.
942 if (diet) dietInt = 0;
944 /* attempt to move checkpoint location */
945 if (!this.moveRecoveryCheckpoint()) return false;
947 initInMethodAndInFieldInitializationStack(currentElement);
949 // only look for headers
950 if (referenceContext instanceof CompilationUnitDeclaration
951 || this.assistNode != null){
953 if(inMethodStack[inMethodPtr] &&
954 insideFieldInitialization() &&
955 this.assistNode == null
957 this.prepareForBlockStatements();
958 goForBlockStatementsOrMethodHeaders();
960 nestedMethod[nestedType = 0] = 0;
961 variablesCounter[nestedType] = 0;
962 realBlockStack[realBlockPtr = 0] = 0;
964 diet = true; // passed this point, will not consider method bodies
968 if (referenceContext instanceof AbstractMethodDeclaration
969 || referenceContext instanceof TypeDeclaration){
971 if (currentElement instanceof RecoveredType){
972 nestedMethod[nestedType = 0] = 0;
973 variablesCounter[nestedType] = 0;
974 realBlockStack[realBlockPtr = 0] = 0;
977 this.prepareForBlockStatements();
978 goForBlockStatementsOrMethodHeaders();
982 // does not know how to restart
985 public abstract void setAssistIdentifier(char[] assistIdent);
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.
991 protected AstNode wrapWithExplicitConstructorCallIfNeeded(AstNode ast) {
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
1001 call.arguments = new Expression[] {(Expression)ast};
1002 call.sourceStart = ast.sourceStart;
1003 call.sourceEnd = ast.sourceEnd;