/******************************************************************************* * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v0.5 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v05.html * * Contributors: * IBM Corporation - initial API and implementation ******************************************************************************/ package net.sourceforge.phpdt.internal.codeassist.impl; /* * Parser extension for code assist task * */ import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.AstNode; import net.sourceforge.phpdt.internal.compiler.ast.Block; import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.ConstructorDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.ExplicitConstructorCall; import net.sourceforge.phpdt.internal.compiler.ast.Expression; import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.ImportReference; import net.sourceforge.phpdt.internal.compiler.ast.Initializer; import net.sourceforge.phpdt.internal.compiler.ast.LocalDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.MessageSend; import net.sourceforge.phpdt.internal.compiler.ast.MethodDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.NameReference; import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.TypeReference; import net.sourceforge.phpdt.internal.compiler.parser.Parser; import net.sourceforge.phpdt.internal.compiler.parser.RecoveredElement; import net.sourceforge.phpdt.internal.compiler.parser.RecoveredField; import net.sourceforge.phpdt.internal.compiler.parser.RecoveredInitializer; import net.sourceforge.phpdt.internal.compiler.parser.RecoveredMethod; import net.sourceforge.phpdt.internal.compiler.parser.RecoveredType; import net.sourceforge.phpdt.internal.compiler.parser.RecoveredUnit; import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation; import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter; public abstract class AssistParser extends Parser { public AstNode assistNode; public boolean isOrphanCompletionNode; /* recovery */ int[] blockStarts = new int[30]; // the previous token read by the scanner protected int previousToken; // the index in the identifier stack of the previous identifier protected int previousIdentifierPtr; // the stacks of selectors for invocations (ie. method invocations, allocation expressions and // explicit constructor invocations) // the selector stack contains pointers to the identifier stack or one of the selector constants below protected int invocationPtr; protected int[] selectorStack = new int[StackIncrement]; // selector constants protected static final int THIS_CONSTRUCTOR = -1; protected static final int SUPER_CONSTRUCTOR = -2; // whether the parser is in a field initializer // (false is pushed each time a new type is entered, // it is changed to true when the initializer is entered, // it is changed back to false when the initializer is exited, // and it is poped when the type is exited) protected int inFieldInitializationPtr; protected boolean[] inFieldInitializationStack = new boolean[StackIncrement]; // whether the parser is in a method, constructor or initializer // (false is pushed each time a new type is entered, // it is changed to true when the method is entered, // it is changed back to false when the method is exited, // and it is poped when the type is exited) protected int inMethodPtr; protected boolean[] inMethodStack = new boolean[StackIncrement]; public AssistParser(ProblemReporter problemReporter, boolean assertMode) { super(problemReporter, true, assertMode); } public abstract char[] assistIdentifier(); public int bodyEnd(AbstractMethodDeclaration method){ return method.declarationSourceEnd; } public int bodyEnd(Initializer initializer){ return initializer.declarationSourceEnd; } /* * Build initial recovery state. * Recovery state is inferred from the current state of the parser (reduced node stack). */ public RecoveredElement buildInitialRecoveryState(){ /* recovery in unit structure */ if (referenceContext instanceof CompilationUnitDeclaration){ RecoveredElement element = super.buildInitialRecoveryState(); flushAssistState(); initInMethodAndInFieldInitializationStack(element); return element; } /* recovery in method body */ lastCheckPoint = 0; RecoveredElement element = null; if (referenceContext instanceof AbstractMethodDeclaration){ element = new RecoveredMethod((AbstractMethodDeclaration) referenceContext, null, 0, this); lastCheckPoint = ((AbstractMethodDeclaration) referenceContext).bodyStart; } else { /* Initializer bodies are parsed in the context of the type declaration, we must thus search it inside */ if (referenceContext instanceof TypeDeclaration){ TypeDeclaration type = (TypeDeclaration) referenceContext; for (int i = 0; i < type.fields.length; i++){ FieldDeclaration field = type.fields[i]; if (!field.isField() && field.declarationSourceStart <= scanner.initialPosition && scanner.initialPosition <= field.declarationSourceEnd && scanner.eofPosition <= field.declarationSourceEnd+1){ element = new RecoveredInitializer((Initializer) field, null, 1, this); lastCheckPoint = field.declarationSourceStart; break; } } } } if (element == null) return element; /* add initial block */ Block block = new Block(0); int lastStart = blockStarts[0]; block.sourceStart = lastStart; element = element.add(block, 1); int blockIndex = 1; // ignore first block start, since manually rebuilt here for(int i = 0; i <= astPtr; i++){ AstNode node = astStack[i]; /* check for intermediate block creation, so recovery can properly close them afterwards */ int nodeStart = node.sourceStart; for (int j = blockIndex; j <= realBlockPtr; j++){ if (blockStarts[j] > nodeStart){ blockIndex = j; // shift the index to the new block break; } if (blockStarts[j] != lastStart){ // avoid multiple block if at same position block = new Block(0); block.sourceStart = lastStart = blockStarts[j]; element = element.add(block, 1); } blockIndex = j+1; // shift the index to the new block } if (node instanceof LocalDeclaration){ LocalDeclaration local = (LocalDeclaration) node; if (local.declarationSourceEnd == 0){ element = element.add(local, 0); if (local.initialization == null){ lastCheckPoint = local.sourceEnd + 1; } else { lastCheckPoint = local.initialization.sourceEnd + 1; } } else { element = element.add(local, 0); lastCheckPoint = local.declarationSourceEnd + 1; } continue; } if (node instanceof AbstractMethodDeclaration){ AbstractMethodDeclaration method = (AbstractMethodDeclaration) node; if (method.declarationSourceEnd == 0){ element = element.add(method, 0); lastCheckPoint = method.bodyStart; } else { element = element.add(method, 0); lastCheckPoint = method.declarationSourceEnd + 1; } continue; } if (node instanceof Initializer){ Initializer initializer = (Initializer) node; if (initializer.declarationSourceEnd == 0){ element = element.add(initializer, 1); lastCheckPoint = initializer.bodyStart; } else { element = element.add(initializer, 0); lastCheckPoint = initializer.declarationSourceEnd + 1; } continue; } if (node instanceof FieldDeclaration){ FieldDeclaration field = (FieldDeclaration) node; if (field.declarationSourceEnd == 0){ element = element.add(field, 0); if (field.initialization == null){ lastCheckPoint = field.sourceEnd + 1; } else { lastCheckPoint = field.initialization.sourceEnd + 1; } } else { element = element.add(field, 0); lastCheckPoint = field.declarationSourceEnd + 1; } continue; } if (node instanceof TypeDeclaration){ TypeDeclaration type = (TypeDeclaration) node; if (type.declarationSourceEnd == 0){ element = element.add(type, 0); lastCheckPoint = type.bodyStart; } else { element = element.add(type, 0); lastCheckPoint = type.declarationSourceEnd + 1; } continue; } if (node instanceof ImportReference){ ImportReference importRef = (ImportReference) node; element = element.add(importRef, 0); lastCheckPoint = importRef.declarationSourceEnd + 1; } } if (this.currentToken == TokenNameRBRACE) { this.currentToken = 0; // closing brace has already been taken care of } /* might need some extra block (after the last reduced node) */ int pos = this.assistNode == null ? lastCheckPoint : this.assistNode.sourceStart; for (int j = blockIndex; j <= realBlockPtr; j++){ if ((blockStarts[j] < pos) && (blockStarts[j] != lastStart)){ // avoid multiple block if at same position block = new Block(0); block.sourceStart = lastStart = blockStarts[j]; element = element.add(block, 1); } } initInMethodAndInFieldInitializationStack(element); return element; } protected void consumeClassBodyDeclarationsopt() { super.consumeClassBodyDeclarationsopt(); this.inFieldInitializationPtr--; this.inMethodPtr--; } protected void consumeClassBodyopt() { super.consumeClassBodyopt(); this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer } protected void consumeClassHeader() { super.consumeClassHeader(); this.pushNotInInitializer(); this.pushNotInMethod(); } protected void consumeConstructorBody() { super.consumeConstructorBody(); this.inMethodStack[this.inMethodPtr] = false; } protected void consumeConstructorHeader() { super.consumeConstructorHeader(); this.inMethodStack[this.inMethodPtr] = true; } protected void consumeEmptyClassBodyDeclarationsopt() { super.consumeEmptyClassBodyDeclarationsopt(); this.inFieldInitializationPtr--; this.inMethodPtr--; } protected void consumeEnterAnonymousClassBody() { super.consumeEnterAnonymousClassBody(); this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer this.pushNotInInitializer(); this.pushNotInMethod(); } protected void consumeExplicitConstructorInvocation(int flag, int recFlag) { super.consumeExplicitConstructorInvocation(flag, recFlag); this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer } protected void consumeForceNoDiet() { super.consumeForceNoDiet(); // if we are not in a method (ie. we are not in a local variable initializer) // then we are entering a field initializer if (!this.inMethodStack[this.inMethodPtr]) { this.inFieldInitializationStack[this.inFieldInitializationPtr] = true; } } protected void consumeInterfaceHeader() { super.consumeInterfaceHeader(); this.pushNotInInitializer(); this.pushNotInMethod(); } protected void consumeInterfaceMemberDeclarationsopt() { super.consumeInterfaceMemberDeclarationsopt(); this.inFieldInitializationPtr--; this.inMethodPtr--; } protected void consumeMethodBody() { super.consumeMethodBody(); this.inMethodStack[this.inMethodPtr] = false; } protected void consumeMethodHeader() { super.consumeMethodHeader(); this.inMethodStack[this.inMethodPtr] = true; } protected void consumeMethodInvocationName() { super.consumeMethodInvocationName(); this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer MessageSend messageSend = (MessageSend)expressionStack[expressionPtr]; if (messageSend == assistNode){ this.lastCheckPoint = messageSend.sourceEnd + 1; } } protected void consumeMethodInvocationPrimary() { super.consumeMethodInvocationPrimary(); this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer MessageSend messageSend = (MessageSend)expressionStack[expressionPtr]; if (messageSend == assistNode){ this.lastCheckPoint = messageSend.sourceEnd + 1; } } protected void consumeMethodInvocationSuper() { super.consumeMethodInvocationSuper(); this.invocationPtr--; // NB: This can be decremented below -1 only if in diet mode and not in field initializer MessageSend messageSend = (MessageSend)expressionStack[expressionPtr]; if (messageSend == assistNode){ this.lastCheckPoint = messageSend.sourceEnd + 1; } } protected void consumeNestedMethod() { super.consumeNestedMethod(); this.inMethodStack[this.inMethodPtr] = true; } protected void consumeOpenBlock() { // OpenBlock ::= $empty super.consumeOpenBlock(); try { blockStarts[realBlockPtr] = scanner.startPosition; } catch (IndexOutOfBoundsException e) { //realBlockPtr is correct int oldStackLength = blockStarts.length; int oldStack[] = blockStarts; blockStarts = new int[oldStackLength + StackIncrement]; System.arraycopy(oldStack, 0, blockStarts, 0, oldStackLength); blockStarts[realBlockPtr] = scanner.startPosition; } } protected void consumePackageDeclarationName() { // PackageDeclarationName ::= 'package' Name /* build an ImportRef build from the last name stored in the identifier stack. */ int index; /* no need to take action if not inside assist identifiers */ if ((index = indexOfAssistIdentifier()) < 0) { super.consumePackageDeclarationName(); return; } /* retrieve identifiers subset and whole positions, the assist node positions should include the entire replaced source. */ int length = identifierLengthStack[identifierLengthPtr]; char[][] subset = identifierSubSet(index+1); // include the assistIdentifier identifierLengthPtr--; identifierPtr -= length; long[] positions = new long[length]; System.arraycopy( identifierPositionStack, identifierPtr + 1, positions, 0, length); /* build specific assist node on package statement */ ImportReference reference = this.createAssistPackageReference(subset, positions); assistNode = reference; this.lastCheckPoint = reference.sourceEnd + 1; compilationUnit.currentPackage = reference; if (currentToken == TokenNameSEMICOLON){ reference.declarationSourceEnd = scanner.currentPosition - 1; } else { reference.declarationSourceEnd = (int) positions[length-1]; } //endPosition is just before the ; reference.declarationSourceStart = intStack[intPtr--]; // flush annotations defined prior to import statements reference.declarationSourceEnd = this.flushAnnotationsDefinedPriorTo(reference.declarationSourceEnd); // recovery if (currentElement != null){ lastCheckPoint = reference.declarationSourceEnd+1; restartRecovery = true; // used to avoid branching back into the regular automaton } } protected void consumeRestoreDiet() { super.consumeRestoreDiet(); // if we are not in a method (ie. we were not in a local variable initializer) // then we are exiting a field initializer if (!this.inMethodStack[this.inMethodPtr]) { this.inFieldInitializationStack[this.inFieldInitializationPtr] = false; } } protected void consumeSingleTypeImportDeclarationName() { // SingleTypeImportDeclarationName ::= 'import' Name /* push an ImportRef build from the last name stored in the identifier stack. */ int index; /* no need to take action if not inside assist identifiers */ if ((index = indexOfAssistIdentifier()) < 0) { super.consumeSingleTypeImportDeclarationName(); return; } /* retrieve identifiers subset and whole positions, the assist node positions should include the entire replaced source. */ int length = identifierLengthStack[identifierLengthPtr]; char[][] subset = identifierSubSet(index+1); // include the assistIdentifier identifierLengthPtr--; identifierPtr -= length; long[] positions = new long[length]; System.arraycopy( identifierPositionStack, identifierPtr + 1, positions, 0, length); /* build specific assist node on import statement */ ImportReference reference = this.createAssistImportReference(subset, positions); assistNode = reference; this.lastCheckPoint = reference.sourceEnd + 1; pushOnAstStack(reference); if (currentToken == TokenNameSEMICOLON){ reference.declarationSourceEnd = scanner.currentPosition - 1; } else { reference.declarationSourceEnd = (int) positions[length-1]; } //endPosition is just before the ; reference.declarationSourceStart = intStack[intPtr--]; // flush annotations defined prior to import statements reference.declarationSourceEnd = this.flushAnnotationsDefinedPriorTo(reference.declarationSourceEnd); // recovery if (currentElement != null){ lastCheckPoint = reference.declarationSourceEnd+1; currentElement = currentElement.add(reference, 0); lastIgnoredToken = -1; restartRecovery = true; // used to avoid branching back into the regular automaton } } protected void consumeStaticInitializer() { super.consumeStaticInitializer(); this.inMethodStack[this.inMethodPtr] = false; } protected void consumeStaticOnly() { super.consumeStaticOnly(); this.inMethodStack[this.inMethodPtr] = true; } protected void consumeToken(int token) { super.consumeToken(token); // register message send selector only if inside a method or if looking at a field initializer // and if the current token is an open parenthesis if ((this.inMethodStack[this.inMethodPtr] || this.inFieldInitializationStack[this.inFieldInitializationPtr]) && token == TokenNameLPAREN) { switch (this.previousToken) { case TokenNameIdentifier: this.pushOnSelectorStack(this.identifierPtr); break; // case TokenNamethis: // explicit constructor invocation, eg. this(1, 2) // this.pushOnSelectorStack(THIS_CONSTRUCTOR); // break; // case TokenNamesuper: // explicit constructor invocation, eg. super(1, 2) // this.pushOnSelectorStack(SUPER_CONSTRUCTOR); // break; } } this.previousToken = token; if (token == TokenNameIdentifier) { this.previousIdentifierPtr = this.identifierPtr; } } protected void consumeTypeImportOnDemandDeclarationName() { // TypeImportOnDemandDeclarationName ::= 'import' Name '.' '*' /* push an ImportRef build from the last name stored in the identifier stack. */ int index; /* no need to take action if not inside assist identifiers */ if ((index = indexOfAssistIdentifier()) < 0) { super.consumeTypeImportOnDemandDeclarationName(); return; } /* retrieve identifiers subset and whole positions, the assist node positions should include the entire replaced source. */ int length = identifierLengthStack[identifierLengthPtr]; char[][] subset = identifierSubSet(index+1); // include the assistIdentifier identifierLengthPtr--; identifierPtr -= length; long[] positions = new long[length]; System.arraycopy( identifierPositionStack, identifierPtr + 1, positions, 0, length); /* build specific assist node on import statement */ ImportReference reference = this.createAssistImportReference(subset, positions); reference.onDemand = true; assistNode = reference; this.lastCheckPoint = reference.sourceEnd + 1; pushOnAstStack(reference); if (currentToken == TokenNameSEMICOLON){ reference.declarationSourceEnd = scanner.currentPosition - 1; } else { reference.declarationSourceEnd = (int) positions[length-1]; } //endPosition is just before the ; reference.declarationSourceStart = intStack[intPtr--]; // flush annotations defined prior to import statements reference.declarationSourceEnd = this.flushAnnotationsDefinedPriorTo(reference.declarationSourceEnd); // recovery if (currentElement != null){ lastCheckPoint = reference.declarationSourceEnd+1; currentElement = currentElement.add(reference, 0); lastIgnoredToken = -1; restartRecovery = true; // used to avoid branching back into the regular automaton } } public abstract ImportReference createAssistImportReference(char[][] tokens, long[] positions); public abstract ImportReference createAssistPackageReference(char[][] tokens, long[] positions); public abstract NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions); public abstract TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] name, long[] positions); public abstract NameReference createSingleAssistNameReference(char[] name, long position); public abstract TypeReference createSingleAssistTypeReference(char[] name, long position); /* * Flush parser/scanner state regarding to code assist */ public void flushAssistState(){ this.assistNode = null; this.isOrphanCompletionNode = false; this.setAssistIdentifier(null); } /* * Build specific type reference nodes in case the cursor is located inside the type reference */ protected TypeReference getTypeReference(int dim) { int index; /* no need to take action if not inside completed identifiers */ if ((index = indexOfAssistIdentifier()) < 0) { return super.getTypeReference(dim); } /* retrieve identifiers subset and whole positions, the assist node positions should include the entire replaced source. */ int length = identifierLengthStack[identifierLengthPtr]; char[][] subset = identifierSubSet(index); identifierLengthPtr--; identifierPtr -= length; long[] positions = new long[length]; System.arraycopy( identifierPositionStack, identifierPtr + 1, positions, 0, length); /* build specific assist on type reference */ TypeReference reference; if (index == 0) { /* assist inside first identifier */ reference = this.createSingleAssistTypeReference( assistIdentifier(), positions[0]); } else { /* assist inside subsequent identifier */ reference = this.createQualifiedAssistTypeReference( subset, assistIdentifier(), positions); } assistNode = reference; this.lastCheckPoint = reference.sourceEnd + 1; return reference; } /* * Copy of code from superclass with the following change: * In the case of qualified name reference if the cursor location is on the * qualified name reference, then create a CompletionOnQualifiedNameReference * instead. */ protected NameReference getUnspecifiedReferenceOptimized() { int completionIndex; /* no need to take action if not inside completed identifiers */ if ((completionIndex = indexOfAssistIdentifier()) < 0) { return super.getUnspecifiedReferenceOptimized(); } /* retrieve identifiers subset and whole positions, the completion node positions should include the entire replaced source. */ int length = identifierLengthStack[identifierLengthPtr]; char[][] subset = identifierSubSet(completionIndex); identifierLengthPtr--; identifierPtr -= length; long[] positions = new long[length]; System.arraycopy( identifierPositionStack, identifierPtr + 1, positions, 0, length); /* build specific completion on name reference */ NameReference reference; if (completionIndex == 0) { /* completion inside first identifier */ reference = this.createSingleAssistNameReference(assistIdentifier(), positions[0]); } else { /* completion inside subsequent identifier */ reference = this.createQualifiedAssistNameReference(subset, assistIdentifier(), positions); }; reference.bits &= ~AstNode.RestrictiveFlagMASK; reference.bits |= LOCAL | FIELD; assistNode = reference; lastCheckPoint = reference.sourceEnd + 1; return reference; } public void goForBlockStatementsopt() { //tells the scanner to go for block statements opt parsing firstToken = TokenNameTWIDDLE; scanner.recordLineSeparator = false; } public void goForConstructorBlockStatementsopt() { //tells the scanner to go for constructor block statements opt parsing firstToken = TokenNameNOT; scanner.recordLineSeparator = false; } /* * Retrieve a partial subset of a qualified name reference up to the completion point. * It does not pop the actual awaiting identifiers, so as to be able to retrieve position * information afterwards. */ protected char[][] identifierSubSet(int subsetLength){ if (subsetLength == 0) return null; char[][] subset; System.arraycopy( identifierStack, identifierPtr - identifierLengthStack[identifierLengthPtr] + 1, (subset = new char[subsetLength][]), 0, subsetLength); return subset; } /* * Iterate the most recent group of awaiting identifiers (grouped for qualified name reference (eg. aa.bb.cc) * so as to check whether one of them is the assist identifier. * If so, then answer the index of the assist identifier (0 being the first identifier of the set). * eg. aa(0).bb(1).cc(2) * If no assist identifier was found, answers -1. */ protected int indexOfAssistIdentifier(){ if (identifierLengthPtr < 0){ return -1; // no awaiting identifier } char[] assistIdentifier ; if ((assistIdentifier = this.assistIdentifier()) == null){ return -1; // no assist identifier found yet } // iterate awaiting identifiers backwards int length = identifierLengthStack[identifierLengthPtr]; for (int i = 0; i < length; i++){ if (identifierStack[identifierPtr - i] == assistIdentifier){ return length - i - 1; } } // none of the awaiting identifiers is the completion one return -1; } public void initialize() { super.initialize(); this.flushAssistState(); this.invocationPtr = -1; this.inMethodStack[this.inMethodPtr = 0] = false; this.inFieldInitializationStack[this.inFieldInitializationPtr = 0] = false; this.previousIdentifierPtr = -1; } public abstract void initializeScanner(); protected void initInMethodAndInFieldInitializationStack(RecoveredElement currentElement) { int length = currentElement.depth() + 1; int ptr = length; boolean[] methodStack = new boolean[length]; boolean[] fieldInitializationStack = new boolean[length]; boolean inMethod = false; boolean inFieldInitializer = false; RecoveredElement element = currentElement; while(element != null){ if(element instanceof RecoveredMethod || element instanceof RecoveredInitializer) { if(element.parent == null) { methodStack[--ptr] = true; fieldInitializationStack[ptr] = false; } inMethod = true; } else if(element instanceof RecoveredField){ inFieldInitializer = element.sourceEnd() == 0; } else if(element instanceof RecoveredType){ methodStack[--ptr] = inMethod; fieldInitializationStack[ptr] = inFieldInitializer; inMethod = false; inFieldInitializer = false; } else if(element instanceof RecoveredUnit) { methodStack[--ptr] = false; fieldInitializationStack[ptr] = false; } element = element.parent; } inMethodPtr = length - ptr - 1; inFieldInitializationPtr = inMethodPtr; System.arraycopy(methodStack, ptr, inMethodStack, 0, inMethodPtr + 1); System.arraycopy(fieldInitializationStack, ptr, inFieldInitializationStack, 0, inFieldInitializationPtr + 1); } /** * Returns whether we are directly or indirectly inside a field initializer. */ protected boolean insideFieldInitialization() { for (int i = this.inFieldInitializationPtr; i >= 0; i--) { if (this.inFieldInitializationStack[i]) { return true; } } return false; } /** * Parse the block statements inside the given method declaration and try to complete at the * cursor location. */ public void parseBlockStatements(AbstractMethodDeclaration md, CompilationUnitDeclaration unit) { if (md instanceof MethodDeclaration) { parseBlockStatements((MethodDeclaration) md, unit); } else if (md instanceof ConstructorDeclaration) { parseBlockStatements((ConstructorDeclaration) md, unit); } } /** * Parse the block statements inside the given constructor declaration and try to complete at the * cursor location. */ public void parseBlockStatements(ConstructorDeclaration cd, CompilationUnitDeclaration unit) { //only parse the method body of cd //fill out its statements //convert bugs into parse error initialize(); // simulate goForConstructorBody except that we don't want to balance brackets because they are not going to be balanced goForConstructorBlockStatementsopt(); referenceContext = cd; compilationUnit = unit; scanner.resetTo(cd.bodyStart, bodyEnd(cd)); consumeNestedMethod(); try { parse(); } catch (AbortCompilation ex) { lastAct = ERROR_ACTION; } } /** * Parse the block statements inside the given initializer and try to complete at the * cursor location. */ public void parseBlockStatements( Initializer ini, TypeDeclaration type, CompilationUnitDeclaration unit) { initialize(); // simulate goForInitializer except that we don't want to balance brackets because they are not going to be balanced goForBlockStatementsopt(); referenceContext = type; compilationUnit = unit; scanner.resetTo(ini.sourceStart, bodyEnd(ini)); // just after the beginning { consumeNestedMethod(); try { parse(); } catch (AbortCompilation ex) { lastAct = ERROR_ACTION; } finally { nestedMethod[nestedType]--; } } /** * Parse the block statements inside the given method declaration and try to complete at the * cursor location. */ public void parseBlockStatements(MethodDeclaration md, CompilationUnitDeclaration unit) { //only parse the method body of md //fill out method statements //convert bugs into parse error if (md.isAbstract()) return; if (md.isNative()) return; if ((md.modifiers & AccSemicolonBody) != 0) return; initialize(); // simulate goForMethodBody except that we don't want to balance brackets because they are not going to be balanced goForBlockStatementsopt(); referenceContext = md; compilationUnit = unit; scanner.resetTo(md.bodyStart, bodyEnd(md)); // reset the scanner to parser from { down to the cursor location consumeNestedMethod(); try { parse(); } catch (AbortCompilation ex) { lastAct = ERROR_ACTION; } finally { nestedMethod[nestedType]--; } } /* * Prepares the state of the parser to go for BlockStatements. */ protected void prepareForBlockStatements() { this.nestedMethod[this.nestedType = 0] = 1; this.variablesCounter[this.nestedType] = 0; this.realBlockStack[this.realBlockPtr = 1] = 0; this.invocationPtr = -1; } /* * Pushes 'false' on the inInitializerStack. */ protected void pushNotInInitializer() { try { this.inFieldInitializationStack[++this.inFieldInitializationPtr] = false; } catch (IndexOutOfBoundsException e) { //except in test's cases, it should never raise int oldStackLength = this.inFieldInitializationStack.length; System.arraycopy(this.inFieldInitializationStack , 0, (this.inFieldInitializationStack = new boolean[oldStackLength + StackIncrement]), 0, oldStackLength); this.inFieldInitializationStack[this.inFieldInitializationPtr] = false; } } /* * Pushes 'false' on the inMethodStack. */ protected void pushNotInMethod() { try { this.inMethodStack[++this.inMethodPtr] = false; } catch (IndexOutOfBoundsException e) { //except in test's cases, it should never raise int oldStackLength = this.inMethodStack.length; System.arraycopy(this.inMethodStack , 0, (this.inMethodStack = new boolean[oldStackLength + StackIncrement]), 0, oldStackLength); this.inMethodStack[this.inMethodPtr] = false; } } /** * Pushes the given the given selector (an identifier pointer to the identifier stack) on the selector stack. */ protected void pushOnSelectorStack(int selectorIdPtr) { if (this.invocationPtr < -1) return; try { this.selectorStack[++this.invocationPtr] = selectorIdPtr; } catch (IndexOutOfBoundsException e) { int oldStackLength = this.selectorStack.length; int oldSelectorStack[] = this.selectorStack; this.selectorStack = new int[oldStackLength + StackIncrement]; System.arraycopy(oldSelectorStack, 0, this.selectorStack, 0, oldStackLength); this.selectorStack[this.invocationPtr] = selectorIdPtr; } } public void reset(){ this.flushAssistState(); } /* * Reset context so as to resume to regular parse loop */ protected void resetStacks() { super.resetStacks(); this.inFieldInitializationStack[this.inFieldInitializationPtr = 0] = false; this.inMethodStack[this.inMethodPtr = 0] = false; } /* * Reset context so as to resume to regular parse loop * If unable to reset for resuming, answers false. * * Move checkpoint location, reset internal stacks and * decide which grammar goal is activated. */ protected boolean resumeAfterRecovery() { // reset internal stacks astPtr = -1; astLengthPtr = -1; expressionPtr = -1; expressionLengthPtr = -1; identifierPtr = -1; identifierLengthPtr = -1; intPtr = -1; dimensions = 0 ; recoveredStaticInitializerStart = 0; // if in diet mode, reset the diet counter because we're going to restart outside an initializer. if (diet) dietInt = 0; /* attempt to move checkpoint location */ if (!this.moveRecoveryCheckpoint()) return false; initInMethodAndInFieldInitializationStack(currentElement); // only look for headers if (referenceContext instanceof CompilationUnitDeclaration || this.assistNode != null){ if(inMethodStack[inMethodPtr] && insideFieldInitialization() && this.assistNode == null ){ this.prepareForBlockStatements(); goForBlockStatementsOrMethodHeaders(); } else { nestedMethod[nestedType = 0] = 0; variablesCounter[nestedType] = 0; realBlockStack[realBlockPtr = 0] = 0; goForHeaders(); diet = true; // passed this point, will not consider method bodies } return true; } if (referenceContext instanceof AbstractMethodDeclaration || referenceContext instanceof TypeDeclaration){ if (currentElement instanceof RecoveredType){ nestedMethod[nestedType = 0] = 0; variablesCounter[nestedType] = 0; realBlockStack[realBlockPtr = 0] = 0; goForHeaders(); } else { this.prepareForBlockStatements(); goForBlockStatementsOrMethodHeaders(); } return true; } // does not know how to restart return false; } public abstract void setAssistIdentifier(char[] assistIdent); /** * If the given ast node is inside an explicit constructor call * then wrap it with a fake constructor call. * Returns the wrapped completion node or the completion node itself. */ protected AstNode wrapWithExplicitConstructorCallIfNeeded(AstNode ast) { int selector; if (ast != null && this.invocationPtr >= 0 && ast instanceof Expression && (((selector = this.selectorStack[this.invocationPtr]) == THIS_CONSTRUCTOR) || (selector == SUPER_CONSTRUCTOR))) { ExplicitConstructorCall call = new ExplicitConstructorCall( (selector == THIS_CONSTRUCTOR) ? ExplicitConstructorCall.This : ExplicitConstructorCall.Super ); call.arguments = new Expression[] {(Expression)ast}; call.sourceStart = ast.sourceStart; call.sourceEnd = ast.sourceEnd; return call; } else { return ast; } } }