/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.codeassist.impl;
/*
* Parser extension for code assist task
*
*/
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.SuperReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.parser.RecoveredBlock;
import org.eclipse.jdt.internal.compiler.parser.RecoveredElement;
import org.eclipse.jdt.internal.compiler.parser.RecoveredField;
import org.eclipse.jdt.internal.compiler.parser.RecoveredInitializer;
import org.eclipse.jdt.internal.compiler.parser.RecoveredMethod;
import org.eclipse.jdt.internal.compiler.parser.RecoveredType;
import org.eclipse.jdt.internal.compiler.parser.RecoveredUnit;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
public abstract class AssistParser extends Parser {
public ASTNode assistNode;
public boolean isOrphanCompletionNode;
// last modifiers info
protected int lastModifiers= ClassFileConstants.AccDefault;
protected int lastModifiersStart= -1;
/* 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;
// depth of '(', '{' and '[]'
protected int bracketDepth;
// element stack
protected static final int ElementStackIncrement= 100;
protected int elementPtr;
protected int[] elementKindStack= new int[ElementStackIncrement];
protected int[] elementInfoStack= new int[ElementStackIncrement];
protected Object[] elementObjectInfoStack= new Object[ElementStackIncrement];
protected int previousKind;
protected int previousInfo;
protected Object previousObjectInfo;
// OWNER
protected static final int ASSIST_PARSER= 512;
// KIND : all values known by AssistParser are between 513 and 1023
protected static final int K_SELECTOR= ASSIST_PARSER + 1; // whether we are inside a message send
protected static final int K_TYPE_DELIMITER= ASSIST_PARSER + 2; // whether we are inside a type declaration
protected static final int K_METHOD_DELIMITER= ASSIST_PARSER + 3; // whether we are inside a method declaration
protected static final int K_FIELD_INITIALIZER_DELIMITER= ASSIST_PARSER + 4; // whether we are inside a field initializer
protected static final int K_ATTRIBUTE_VALUE_DELIMITER= ASSIST_PARSER + 5; // whether we are inside a annotation attribute valuer
protected static final int K_ENUM_CONSTANT_DELIMITER= ASSIST_PARSER + 6; // whether we are inside a field initializer
// selector constants
protected static final int THIS_CONSTRUCTOR= -1;
protected static final int SUPER_CONSTRUCTOR= -2;
// enum constant constants
protected static final int NO_BODY= 0;
protected static final int WITH_BODY= 1;
protected boolean isFirst= false;
public AssistParser(ProblemReporter problemReporter) {
super(problemReporter, true);
this.javadocParser.checkDocComment= false;
setMethodsFullRecovery(false);
setStatementsRecovery(false);
}
public abstract char[] assistIdentifier();
/**
* The parser become a simple parser which behave like a Parser
*
* @return the state of the assist parser to be able to restore the assist parser state
*/
public Object becomeSimpleParser() {
return null;
}
/**
* Restore the parser as an assist parser
*
* @param parserState
*/
public void restoreAssistParser(Object parserState) {
//Do nothing
}
public int bodyEnd(AbstractMethodDeclaration method) {
return method.bodyEnd;
}
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 (this.referenceContext instanceof CompilationUnitDeclaration) {
RecoveredElement element= super.buildInitialRecoveryState();
flushAssistState();
flushElementStack();
return element;
}
/* recovery in method body */
this.lastCheckPoint= 0;
RecoveredElement element= null;
if (this.referenceContext instanceof AbstractMethodDeclaration) {
element= new RecoveredMethod((AbstractMethodDeclaration)this.referenceContext, null, 0, this);
this.lastCheckPoint= ((AbstractMethodDeclaration)this.referenceContext).bodyStart;
} else {
/* Initializer bodies are parsed in the context of the type declaration, we must thus search it inside */
if (this.referenceContext instanceof TypeDeclaration) {
TypeDeclaration type= (TypeDeclaration)this.referenceContext;
for (int i= 0; i < type.fields.length; i++) {
FieldDeclaration field= type.fields[i];
if (field != null
&& field.getKind() == AbstractVariableDeclaration.INITIALIZER
&& field.declarationSourceStart <= this.scanner.initialPosition
&& this.scanner.initialPosition <= field.declarationSourceEnd
&& this.scanner.eofPosition <= field.declarationSourceEnd + 1) {
element= new RecoveredInitializer(field, null, 1, this);
this.lastCheckPoint= field.declarationSourceStart;
break;
}
}
}
}
if (element == null)
return element;
/* add initial block */
Block block= new Block(0);
int lastStart= this.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 <= this.astPtr; i++) {
ASTNode node= this.astStack[i];
if (node instanceof ForeachStatement && ((ForeachStatement)node).action == null) {
node= ((ForeachStatement)node).elementVariable;
}
/* check for intermediate block creation, so recovery can properly close them afterwards */
int nodeStart= node.sourceStart;
for (int j= blockIndex; j <= this.realBlockPtr; j++) {
if (this.blockStarts[j] >= 0) {
if (this.blockStarts[j] > nodeStart) {
blockIndex= j; // shift the index to the new block
break;
}
if (this.blockStarts[j] != lastStart) { // avoid multiple block if at same position
block= new Block(0);
block.sourceStart= lastStart= this.blockStarts[j];
element= element.add(block, 1);
}
} else {
if (-this.blockStarts[j] > nodeStart) {
blockIndex= j; // shift the index to the new block
break;
}
block= new Block(0);
block.sourceStart= lastStart= -this.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) {
this.lastCheckPoint= local.sourceEnd + 1;
} else {
this.lastCheckPoint= local.initialization.sourceEnd + 1;
}
} else {
element= element.add(local, 0);
this.lastCheckPoint= local.declarationSourceEnd + 1;
}
continue;
}
if (node instanceof AbstractMethodDeclaration) {
AbstractMethodDeclaration method= (AbstractMethodDeclaration)node;
if (method.declarationSourceEnd == 0) {
element= element.add(method, 0);
this.lastCheckPoint= method.bodyStart;
} else {
element= element.add(method, 0);
this.lastCheckPoint= method.declarationSourceEnd + 1;
}
continue;
}
if (node instanceof Initializer) {
Initializer initializer= (Initializer)node;
if (initializer.declarationSourceEnd == 0) {
element= element.add(initializer, 1);
this.lastCheckPoint= initializer.sourceStart;
} else {
element= element.add(initializer, 0);
this.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) {
this.lastCheckPoint= field.sourceEnd + 1;
} else {
this.lastCheckPoint= field.initialization.sourceEnd + 1;
}
} else {
element= element.add(field, 0);
this.lastCheckPoint= field.declarationSourceEnd + 1;
}
continue;
}
if (node instanceof TypeDeclaration) {
TypeDeclaration type= (TypeDeclaration)node;
if (type.declarationSourceEnd == 0) {
element= element.add(type, 0);
this.lastCheckPoint= type.bodyStart;
} else {
element= element.add(type, 0);
this.lastCheckPoint= type.declarationSourceEnd + 1;
}
continue;
}
if (node instanceof ImportReference) {
ImportReference importRef= (ImportReference)node;
element= element.add(importRef, 0);
this.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 ? this.lastCheckPoint : this.assistNode.sourceStart;
for (int j= blockIndex; j <= this.realBlockPtr; j++) {
if (this.blockStarts[j] >= 0) {
if ((this.blockStarts[j] < pos) && (this.blockStarts[j] != lastStart)) { // avoid multiple block if at same position
block= new Block(0);
block.sourceStart= lastStart= this.blockStarts[j];
element= element.add(block, 1);
}
} else {
if ((this.blockStarts[j] < pos)) { // avoid multiple block if at same position
block= new Block(0);
block.sourceStart= lastStart= -this.blockStarts[j];
element= element.add(block, 1);
}
}
}
return element;
}
protected void consumeAnnotationTypeDeclarationHeader() {
super.consumeAnnotationTypeDeclarationHeader();
pushOnElementStack(K_TYPE_DELIMITER);
}
protected void consumeClassBodyDeclaration() {
popElement(K_METHOD_DELIMITER);
super.consumeClassBodyDeclaration();
}
protected void consumeClassBodyopt() {
super.consumeClassBodyopt();
popElement(K_SELECTOR);
}
protected void consumeClassHeader() {
super.consumeClassHeader();
pushOnElementStack(K_TYPE_DELIMITER);
}
protected void consumeConstructorBody() {
super.consumeConstructorBody();
popElement(K_METHOD_DELIMITER);
}
protected void consumeConstructorHeader() {
super.consumeConstructorHeader();
pushOnElementStack(K_METHOD_DELIMITER);
}
protected void consumeEnhancedForStatementHeaderInit(boolean hasModifiers) {
super.consumeEnhancedForStatementHeaderInit(hasModifiers);
if (this.currentElement != null) {
LocalDeclaration localDecl= ((ForeachStatement)this.astStack[this.astPtr]).elementVariable;
this.lastCheckPoint= localDecl.sourceEnd + 1;
this.currentElement= this.currentElement.add(localDecl, 0);
}
}
protected void consumeEnterAnonymousClassBody(boolean qualified) {
super.consumeEnterAnonymousClassBody(qualified);
popElement(K_SELECTOR);
pushOnElementStack(K_TYPE_DELIMITER);
}
protected void consumeEnterMemberValue() {
super.consumeEnterMemberValue();
pushOnElementStack(K_ATTRIBUTE_VALUE_DELIMITER, this.identifierPtr);
}
protected void consumeEnumConstantHeader() {
if (this.currentToken == TokenNameLBRACE) {
popElement(K_ENUM_CONSTANT_DELIMITER);
pushOnElementStack(K_ENUM_CONSTANT_DELIMITER, WITH_BODY);
pushOnElementStack(K_FIELD_INITIALIZER_DELIMITER);
pushOnElementStack(K_TYPE_DELIMITER);
}
super.consumeEnumConstantHeader();
}
protected void consumeEnumConstantHeaderName() {
super.consumeEnumConstantHeaderName();
pushOnElementStack(K_ENUM_CONSTANT_DELIMITER);
}
protected void consumeEnumConstantWithClassBody() {
popElement(K_TYPE_DELIMITER);
popElement(K_FIELD_INITIALIZER_DELIMITER);
popElement(K_ENUM_CONSTANT_DELIMITER);
super.consumeEnumConstantWithClassBody();
}
protected void consumeEnumConstantNoClassBody() {
popElement(K_ENUM_CONSTANT_DELIMITER);
super.consumeEnumConstantNoClassBody();
}
protected void consumeEnumHeader() {
super.consumeEnumHeader();
pushOnElementStack(K_TYPE_DELIMITER);
}
protected void consumeExitMemberValue() {
super.consumeExitMemberValue();
popElement(K_ATTRIBUTE_VALUE_DELIMITER);
}
protected void consumeExplicitConstructorInvocation(int flag, int recFlag) {
super.consumeExplicitConstructorInvocation(flag, recFlag);
popElement(K_SELECTOR);
}
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 (!isInsideMethod()) {
if (topKnownElementKind(ASSIST_PARSER) != K_ENUM_CONSTANT_DELIMITER) {
if (topKnownElementKind(ASSIST_PARSER, 2) != K_ENUM_CONSTANT_DELIMITER) {
pushOnElementStack(K_FIELD_INITIALIZER_DELIMITER);
}
} else {
int info= topKnownElementInfo(ASSIST_PARSER);
if (info != NO_BODY) {
pushOnElementStack(K_FIELD_INITIALIZER_DELIMITER);
}
}
}
}
protected void consumeInterfaceHeader() {
super.consumeInterfaceHeader();
pushOnElementStack(K_TYPE_DELIMITER);
}
protected void consumeMethodBody() {
super.consumeMethodBody();
popElement(K_METHOD_DELIMITER);
}
protected void consumeMethodDeclaration(boolean isNotAbstract) {
if (!isNotAbstract) {
popElement(K_METHOD_DELIMITER);
}
super.consumeMethodDeclaration(isNotAbstract);
}
protected void consumeMethodHeader() {
super.consumeMethodHeader();
pushOnElementStack(K_METHOD_DELIMITER);
}
protected void consumeMethodInvocationName() {
super.consumeMethodInvocationName();
popElement(K_SELECTOR);
MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr];
if (messageSend == this.assistNode) {
this.lastCheckPoint= messageSend.sourceEnd + 1;
}
}
protected void consumeMethodInvocationNameWithTypeArguments() {
super.consumeMethodInvocationNameWithTypeArguments();
popElement(K_SELECTOR);
MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr];
if (messageSend == this.assistNode) {
this.lastCheckPoint= messageSend.sourceEnd + 1;
}
}
protected void consumeMethodInvocationPrimary() {
super.consumeMethodInvocationPrimary();
popElement(K_SELECTOR);
MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr];
if (messageSend == this.assistNode) {
this.lastCheckPoint= messageSend.sourceEnd + 1;
}
}
protected void consumeMethodInvocationPrimaryWithTypeArguments() {
super.consumeMethodInvocationPrimaryWithTypeArguments();
popElement(K_SELECTOR);
MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr];
if (messageSend == this.assistNode) {
this.lastCheckPoint= messageSend.sourceEnd + 1;
}
}
protected void consumeMethodInvocationSuper() {
super.consumeMethodInvocationSuper();
popElement(K_SELECTOR);
MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr];
if (messageSend == this.assistNode) {
this.lastCheckPoint= messageSend.sourceEnd + 1;
}
}
protected void consumeMethodInvocationSuperWithTypeArguments() {
super.consumeMethodInvocationSuperWithTypeArguments();
popElement(K_SELECTOR);
MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr];
if (messageSend == this.assistNode) {
this.lastCheckPoint= messageSend.sourceEnd + 1;
}
}
protected void consumeNestedMethod() {
super.consumeNestedMethod();
if (!isInsideMethod())
pushOnElementStack(K_METHOD_DELIMITER);
}
protected void consumeOpenBlock() {
// OpenBlock ::= $empty
super.consumeOpenBlock();
int stackLength= this.blockStarts.length;
if (this.realBlockPtr >= stackLength) {
System.arraycopy(
this.blockStarts, 0,
this.blockStarts= new int[stackLength + StackIncrement], 0,
stackLength);
}
this.blockStarts[this.realBlockPtr]= this.scanner.startPosition;
}
protected void consumeOpenFakeBlock() {
// OpenBlock ::= $empty
super.consumeOpenBlock();
int stackLength= this.blockStarts.length;
if (this.realBlockPtr >= stackLength) {
System.arraycopy(
this.blockStarts, 0,
this.blockStarts= new int[stackLength + StackIncrement], 0,
stackLength);
}
this.blockStarts[this.realBlockPtr]= -this.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= this.identifierLengthStack[this.identifierLengthPtr];
char[][] subset= identifierSubSet(index + 1); // include the assistIdentifier
this.identifierLengthPtr--;
this.identifierPtr-= length;
long[] positions= new long[length];
System.arraycopy(
this.identifierPositionStack,
this.identifierPtr + 1,
positions,
0,
length);
/* build specific assist node on package statement */
ImportReference reference= createAssistPackageReference(subset, positions);
this.assistNode= reference;
this.lastCheckPoint= reference.sourceEnd + 1;
this.compilationUnit.currentPackage= reference;
if (this.currentToken == TokenNameSEMICOLON) {
reference.declarationSourceEnd= this.scanner.currentPosition - 1;
} else {
reference.declarationSourceEnd= (int)positions[length - 1];
}
//endPosition is just before the ;
reference.declarationSourceStart= this.intStack[this.intPtr--];
// flush comments defined prior to import statements
reference.declarationSourceEnd= flushCommentsDefinedPriorTo(reference.declarationSourceEnd);
// recovery
if (this.currentElement != null) {
this.lastCheckPoint= reference.declarationSourceEnd + 1;
this.restartRecovery= true; // used to avoid branching back into the regular automaton
}
}
protected void consumePackageDeclarationNameWithModifiers() {
// PackageDeclarationName ::= Modifiers 'package' PushRealModifiers 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.consumePackageDeclarationNameWithModifiers();
return;
}
/* retrieve identifiers subset and whole positions, the assist node positions
should include the entire replaced source. */
int length= this.identifierLengthStack[this.identifierLengthPtr];
char[][] subset= identifierSubSet(index + 1); // include the assistIdentifier
this.identifierLengthPtr--;
this.identifierPtr-= length;
long[] positions= new long[length];
System.arraycopy(
this.identifierPositionStack,
this.identifierPtr + 1,
positions,
0,
length);
this.intPtr--; // we don't need the modifiers start
this.intPtr--; // we don't need the package modifiers
ImportReference reference= createAssistPackageReference(subset, positions);
// consume annotations
if ((length= this.expressionLengthStack[this.expressionLengthPtr--]) != 0) {
System.arraycopy(
this.expressionStack,
(this.expressionPtr-= length) + 1,
reference.annotations= new Annotation[length],
0,
length);
}
/* build specific assist node on package statement */
this.assistNode= reference;
this.lastCheckPoint= reference.sourceEnd + 1;
this.compilationUnit.currentPackage= reference;
if (this.currentToken == TokenNameSEMICOLON) {
reference.declarationSourceEnd= this.scanner.currentPosition - 1;
} else {
reference.declarationSourceEnd= (int)positions[length - 1];
}
//endPosition is just before the ;
reference.declarationSourceStart= this.intStack[this.intPtr--];
// flush comments defined prior to import statements
reference.declarationSourceEnd= flushCommentsDefinedPriorTo(reference.declarationSourceEnd);
// recovery
if (this.currentElement != null) {
this.lastCheckPoint= reference.declarationSourceEnd + 1;
this.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 (!isInsideMethod()) {
popElement(K_FIELD_INITIALIZER_DELIMITER);
}
}
protected void consumeSingleStaticImportDeclarationName() {
// SingleTypeImportDeclarationName ::= 'import' 'static' 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.consumeSingleStaticImportDeclarationName();
return;
}
/* retrieve identifiers subset and whole positions, the assist node positions
should include the entire replaced source. */
int length= this.identifierLengthStack[this.identifierLengthPtr];
char[][] subset= identifierSubSet(index + 1); // include the assistIdentifier
this.identifierLengthPtr--;
this.identifierPtr-= length;
long[] positions= new long[length];
System.arraycopy(
this.identifierPositionStack,
this.identifierPtr + 1,
positions,
0,
length);
/* build specific assist node on import statement */
ImportReference reference= createAssistImportReference(subset, positions, ClassFileConstants.AccStatic);
this.assistNode= reference;
this.lastCheckPoint= reference.sourceEnd + 1;
pushOnAstStack(reference);
if (this.currentToken == TokenNameSEMICOLON) {
reference.declarationSourceEnd= this.scanner.currentPosition - 1;
} else {
reference.declarationSourceEnd= (int)positions[length - 1];
}
//endPosition is just before the ;
reference.declarationSourceStart= this.intStack[this.intPtr--];
// flush annotations defined prior to import statements
reference.declarationSourceEnd= flushCommentsDefinedPriorTo(reference.declarationSourceEnd);
// recovery
if (this.currentElement != null) {
this.lastCheckPoint= reference.declarationSourceEnd + 1;
this.currentElement= this.currentElement.add(reference, 0);
this.lastIgnoredToken= -1;
this.restartRecovery= true; // used to avoid branching back into the regular automaton
}
}
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= this.identifierLengthStack[this.identifierLengthPtr];
char[][] subset= identifierSubSet(index + 1); // include the assistIdentifier
this.identifierLengthPtr--;
this.identifierPtr-= length;
long[] positions= new long[length];
System.arraycopy(
this.identifierPositionStack,
this.identifierPtr + 1,
positions,
0,
length);
/* build specific assist node on import statement */
ImportReference reference= createAssistImportReference(subset, positions, ClassFileConstants.AccDefault);
this.assistNode= reference;
this.lastCheckPoint= reference.sourceEnd + 1;
pushOnAstStack(reference);
if (this.currentToken == TokenNameSEMICOLON) {
reference.declarationSourceEnd= this.scanner.currentPosition - 1;
} else {
reference.declarationSourceEnd= (int)positions[length - 1];
}
//endPosition is just before the ;
reference.declarationSourceStart= this.intStack[this.intPtr--];
// flush comments defined prior to import statements
reference.declarationSourceEnd= flushCommentsDefinedPriorTo(reference.declarationSourceEnd);
// recovery
if (this.currentElement != null) {
this.lastCheckPoint= reference.declarationSourceEnd + 1;
this.currentElement= this.currentElement.add(reference, 0);
this.lastIgnoredToken= -1;
this.restartRecovery= true; // used to avoid branching back into the regular automaton
}
}
protected void consumeStaticImportOnDemandDeclarationName() {
// TypeImportOnDemandDeclarationName ::= 'import' 'static' 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.consumeStaticImportOnDemandDeclarationName();
return;
}
/* retrieve identifiers subset and whole positions, the assist node positions
should include the entire replaced source. */
int length= this.identifierLengthStack[this.identifierLengthPtr];
char[][] subset= identifierSubSet(index + 1); // include the assistIdentifier
this.identifierLengthPtr--;
this.identifierPtr-= length;
long[] positions= new long[length];
System.arraycopy(
this.identifierPositionStack,
this.identifierPtr + 1,
positions,
0,
length);
/* build specific assist node on import statement */
ImportReference reference= createAssistImportReference(subset, positions, ClassFileConstants.AccStatic);
reference.bits|= ASTNode.OnDemand;
this.assistNode= reference;
this.lastCheckPoint= reference.sourceEnd + 1;
pushOnAstStack(reference);
if (this.currentToken == TokenNameSEMICOLON) {
reference.declarationSourceEnd= this.scanner.currentPosition - 1;
} else {
reference.declarationSourceEnd= (int)positions[length - 1];
}
//endPosition is just before the ;
reference.declarationSourceStart= this.intStack[this.intPtr--];
// flush annotations defined prior to import statements
reference.declarationSourceEnd= flushCommentsDefinedPriorTo(reference.declarationSourceEnd);
// recovery
if (this.currentElement != null) {
this.lastCheckPoint= reference.declarationSourceEnd + 1;
this.currentElement= this.currentElement.add(reference, 0);
this.lastIgnoredToken= -1;
this.restartRecovery= true; // used to avoid branching back into the regular automaton
}
}
protected void consumeStaticInitializer() {
super.consumeStaticInitializer();
popElement(K_METHOD_DELIMITER);
}
protected void consumeStaticOnly() {
super.consumeStaticOnly();
pushOnElementStack(K_METHOD_DELIMITER);
}
protected void consumeToken(int token) {
super.consumeToken(token);
if (this.isFirst) {
this.isFirst= false;
return;
}
// 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 (isInsideMethod() || isInsideFieldInitialization() || isInsideAttributeValue()) {
switch (token) {
case TokenNameLPAREN:
this.bracketDepth++;
switch (this.previousToken) {
case TokenNameIdentifier:
this.pushOnElementStack(K_SELECTOR, this.identifierPtr);
break;
case TokenNamethis: // explicit constructor invocation, eg. this(1, 2)
this.pushOnElementStack(K_SELECTOR, THIS_CONSTRUCTOR);
break;
case TokenNamesuper: // explicit constructor invocation, eg. super(1, 2)
this.pushOnElementStack(K_SELECTOR, SUPER_CONSTRUCTOR);
break;
case TokenNameGREATER: // explicit constructor invocation, eg. Fred<X>[(]1, 2)
case TokenNameRIGHT_SHIFT: // or fred<X<X>>[(]1, 2)
case TokenNameUNSIGNED_RIGHT_SHIFT: //or Fred<X<X<X>>>[(]1, 2)
if (this.identifierPtr > -1) {
this.pushOnElementStack(K_SELECTOR, this.identifierPtr);
}
break;
}
break;
case TokenNameLBRACE:
this.bracketDepth++;
break;
case TokenNameLBRACKET:
this.bracketDepth++;
break;
case TokenNameRBRACE:
this.bracketDepth--;
break;
case TokenNameRBRACKET:
this.bracketDepth--;
break;
case TokenNameRPAREN:
this.bracketDepth--;
break;
}
} else {
switch (token) {
case TokenNameRBRACE:
if (topKnownElementKind(ASSIST_PARSER) == K_TYPE_DELIMITER) {
popElement(K_TYPE_DELIMITER);
}
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= this.identifierLengthStack[this.identifierLengthPtr];
char[][] subset= identifierSubSet(index + 1); // include the assistIdentifier
this.identifierLengthPtr--;
this.identifierPtr-= length;
long[] positions= new long[length];
System.arraycopy(
this.identifierPositionStack,
this.identifierPtr + 1,
positions,
0,
length);
/* build specific assist node on import statement */
ImportReference reference= createAssistImportReference(subset, positions, ClassFileConstants.AccDefault);
reference.bits|= ASTNode.OnDemand;
this.assistNode= reference;
this.lastCheckPoint= reference.sourceEnd + 1;
pushOnAstStack(reference);
if (this.currentToken == TokenNameSEMICOLON) {
reference.declarationSourceEnd= this.scanner.currentPosition - 1;
} else {
reference.declarationSourceEnd= (int)positions[length - 1];
}
//endPosition is just before the ;
reference.declarationSourceStart= this.intStack[this.intPtr--];
// flush comments defined prior to import statements
reference.declarationSourceEnd= flushCommentsDefinedPriorTo(reference.declarationSourceEnd);
// recovery
if (this.currentElement != null) {
this.lastCheckPoint= reference.declarationSourceEnd + 1;
this.currentElement= this.currentElement.add(reference, 0);
this.lastIgnoredToken= -1;
this.restartRecovery= true; // used to avoid branching back into the regular automaton
}
}
public abstract ImportReference createAssistImportReference(char[][] tokens, long[] positions, int mod);
public abstract ImportReference createAssistPackageReference(char[][] tokens, long[] positions);
public abstract NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] assistName, long[] positions);
public abstract TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] assistName, long[] positions);
public abstract TypeReference createParameterizedQualifiedAssistTypeReference(char[][] previousIdentifiers, TypeReference[][] typeArguments, char[] asistIdentifier,
TypeReference[] assistTypeArguments, long[] positions);
public abstract NameReference createSingleAssistNameReference(char[] assistName, long position);
public abstract TypeReference createSingleAssistTypeReference(char[] assistName, long position);
public abstract TypeReference createParameterizedSingleAssistTypeReference(TypeReference[] typeArguments, char[] assistName, long position);
/*
* Flush parser/scanner state regarding to code assist
*/
public void flushAssistState() {
this.assistNode= null;
this.isOrphanCompletionNode= false;
setAssistIdentifier(null);
}
protected void flushElementStack() {
for (int j= 0; j <= this.elementPtr; j++) {
this.elementObjectInfoStack[j]= null;
}
this.elementPtr= -1;
this.previousKind= 0;
this.previousInfo= 0;
this.previousObjectInfo= 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(true)) < 0) {
return super.getTypeReference(dim);
}
int length= this.identifierLengthStack[this.identifierLengthPtr];
TypeReference reference;
int numberOfIdentifiers= this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr--];
if (length != numberOfIdentifiers || this.genericsLengthStack[this.genericsLengthPtr] != 0) {
this.identifierLengthPtr--;
// generic type
reference= getAssistTypeReferenceForGenericType(dim, length, numberOfIdentifiers);
} else {
/* retrieve identifiers subset and whole positions, the assist node positions
should include the entire replaced source. */
char[][] subset= identifierSubSet(index);
this.identifierLengthPtr--;
this.identifierPtr-= length;
long[] positions= new long[length];
System.arraycopy(
this.identifierPositionStack,
this.identifierPtr + 1,
positions,
0,
length);
/* build specific assist on type reference */
if (index == 0) {
// genericsIdentifiersLengthPtr--;
this.genericsLengthPtr--;
/* assist inside first identifier */
reference= createSingleAssistTypeReference(
assistIdentifier(),
positions[0]);
} else {
// genericsIdentifiersLengthPtr--;
this.genericsLengthPtr--;
/* assist inside subsequent identifier */
reference= createQualifiedAssistTypeReference(
subset,
assistIdentifier(),
positions);
}
this.assistNode= reference;
this.lastCheckPoint= reference.sourceEnd + 1;
}
return reference;
}
protected TypeReference getAssistTypeReferenceForGenericType(int dim, int identifierLength, int numberOfIdentifiers) {
/* no need to take action if not inside completed identifiers */
if (/*(indexOfAssistIdentifier()) < 0 ||*/(identifierLength == 1 && numberOfIdentifiers == 1)) {
int currentTypeArgumentsLength= this.genericsLengthStack[this.genericsLengthPtr--];
TypeReference[] typeArguments= new TypeReference[currentTypeArgumentsLength];
this.genericsPtr-= currentTypeArgumentsLength;
System.arraycopy(this.genericsStack, this.genericsPtr + 1, typeArguments, 0, currentTypeArgumentsLength);
long[] positions= new long[identifierLength];
System.arraycopy(
this.identifierPositionStack,
this.identifierPtr,
positions,
0,
identifierLength);
this.identifierPtr--;
TypeReference reference= createParameterizedSingleAssistTypeReference(
typeArguments,
assistIdentifier(),
positions[0]);
this.assistNode= reference;
this.lastCheckPoint= reference.sourceEnd + 1;
return reference;
}
TypeReference[][] typeArguments= new TypeReference[numberOfIdentifiers][];
char[][] tokens= new char[numberOfIdentifiers][];
long[] positions= new long[numberOfIdentifiers];
int index= numberOfIdentifiers;
int currentIdentifiersLength= identifierLength;
while (index > 0) {
int currentTypeArgumentsLength= this.genericsLengthStack[this.genericsLengthPtr--];
if (currentTypeArgumentsLength != 0) {
this.genericsPtr-= currentTypeArgumentsLength;
System.arraycopy(this.genericsStack, this.genericsPtr + 1, typeArguments[index - 1]= new TypeReference[currentTypeArgumentsLength], 0, currentTypeArgumentsLength);
}
switch (currentIdentifiersLength) {
case 1:
// we are in a case A<B>.C<D> or A<B>.C<D>
tokens[index - 1]= this.identifierStack[this.identifierPtr];
positions[index - 1]= this.identifierPositionStack[this.identifierPtr--];
break;
default:
// we are in a case A.B.C<B>.C<D> or A.B.C<B>...
this.identifierPtr-= currentIdentifiersLength;
System.arraycopy(this.identifierStack, this.identifierPtr + 1, tokens, index - currentIdentifiersLength, currentIdentifiersLength);
System.arraycopy(this.identifierPositionStack, this.identifierPtr + 1, positions, index - currentIdentifiersLength, currentIdentifiersLength);
}
index-= currentIdentifiersLength;
if (index > 0) {
currentIdentifiersLength= this.identifierLengthStack[this.identifierLengthPtr--];
}
}
// remove completion token
int realLength= numberOfIdentifiers;
for (int i= 0; i < numberOfIdentifiers; i++) {
if (tokens[i] == assistIdentifier()) {
realLength= i;
}
}
TypeReference reference;
if (realLength == 0) {
if (typeArguments[0] != null && typeArguments[0].length > 0) {
reference= createParameterizedSingleAssistTypeReference(typeArguments[0], assistIdentifier(), positions[0]);
} else {
reference= createSingleAssistTypeReference(assistIdentifier(), positions[0]);
}
} else {
TypeReference[] assistTypeArguments= typeArguments[realLength];
System.arraycopy(tokens, 0, tokens= new char[realLength][], 0, realLength);
System.arraycopy(typeArguments, 0, typeArguments= new TypeReference[realLength][], 0, realLength);
boolean isParameterized= false;
for (int i= 0; i < typeArguments.length; i++) {
if (typeArguments[i] != null) {
isParameterized= true;
}
}
if (isParameterized || (assistTypeArguments != null && assistTypeArguments.length > 0)) {
reference= createParameterizedQualifiedAssistTypeReference(tokens, typeArguments, assistIdentifier(), assistTypeArguments, positions);
} else {
reference= createQualifiedAssistTypeReference(tokens, assistIdentifier(), positions);
}
}
this.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= this.identifierLengthStack[this.identifierLengthPtr];
char[][] subset= identifierSubSet(completionIndex);
this.identifierLengthPtr--;
this.identifierPtr-= length;
long[] positions= new long[length];
System.arraycopy(
this.identifierPositionStack,
this.identifierPtr + 1,
positions,
0,
length);
/* build specific completion on name reference */
NameReference reference;
if (completionIndex == 0) {
/* completion inside first identifier */
reference= createSingleAssistNameReference(assistIdentifier(), positions[0]);
} else {
/* completion inside subsequent identifier */
reference= createQualifiedAssistNameReference(subset, assistIdentifier(), positions);
}
reference.bits&= ~ASTNode.RestrictiveFlagMASK;
reference.bits|= Binding.LOCAL | Binding.FIELD;
this.assistNode= reference;
this.lastCheckPoint= reference.sourceEnd + 1;
return reference;
}
public void goForBlockStatementsopt() {
super.goForBlockStatementsopt();
this.isFirst= true;
}
public void goForHeaders() {
super.goForHeaders();
this.isFirst= true;
}
public void goForCompilationUnit() {
super.goForCompilationUnit();
this.isFirst= true;
}
public void goForBlockStatementsOrCatchHeader() {
super.goForBlockStatementsOrCatchHeader();
this.isFirst= true;
}
/*
* 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(
this.identifierStack,
this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + 1,
(subset= new char[subsetLength][]),
0,
subsetLength);
return subset;
}
protected int indexOfAssistIdentifier() {
return this.indexOfAssistIdentifier(false);
}
/*
* 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(boolean useGenericsStack) {
if (this.identifierLengthPtr < 0) {
return -1; // no awaiting identifier
}
char[] assistIdentifier;
if ((assistIdentifier= assistIdentifier()) == null) {
return -1; // no assist identifier found yet
}
// iterate awaiting identifiers backwards
int length= this.identifierLengthStack[this.identifierLengthPtr];
if (useGenericsStack && length > 0 && this.genericsIdentifiersLengthPtr > -1) {
length= this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr];
}
for (int i= 0; i < length; i++) {
if (this.identifierStack[this.identifierPtr - i] == assistIdentifier) {
return length - i - 1;
}
}
// none of the awaiting identifiers is the completion one
return -1;
}
public void initialize() {
super.initialize();
flushAssistState();
flushElementStack();
this.previousIdentifierPtr= -1;
this.bracketDepth= 0;
}
public void initialize(boolean initializeNLS) {
super.initialize(initializeNLS);
flushAssistState();
flushElementStack();
this.previousIdentifierPtr= -1;
this.bracketDepth= 0;
}
public abstract void initializeScanner();
protected boolean isIndirectlyInsideFieldInitialization() {
int i= this.elementPtr;
while (i > -1) {
if (this.elementKindStack[i] == K_FIELD_INITIALIZER_DELIMITER)
return true;
i--;
}
return false;
}
protected boolean isIndirectlyInsideMethod() {
int i= this.elementPtr;
while (i > -1) {
if (this.elementKindStack[i] == K_METHOD_DELIMITER)
return true;
i--;
}
return false;
}
protected boolean isIndirectlyInsideType() {
int i= this.elementPtr;
while (i > -1) {
if (this.elementKindStack[i] == K_TYPE_DELIMITER)
return true;
i--;
}
return false;
}
protected boolean isInsideAttributeValue() {
int i= this.elementPtr;
while (i > -1) {
switch (this.elementKindStack[i]) {
case K_TYPE_DELIMITER:
return false;
case K_METHOD_DELIMITER:
return false;
case K_FIELD_INITIALIZER_DELIMITER:
return false;
case K_ATTRIBUTE_VALUE_DELIMITER:
return true;
}
i--;
}
return false;
}
protected boolean isInsideFieldInitialization() {
int i= this.elementPtr;
while (i > -1) {
switch (this.elementKindStack[i]) {
case K_TYPE_DELIMITER:
return false;
case K_METHOD_DELIMITER:
return false;
case K_FIELD_INITIALIZER_DELIMITER:
return true;
}
i--;
}
return false;
}
protected boolean isInsideMethod() {
int i= this.elementPtr;
while (i > -1) {
switch (this.elementKindStack[i]) {
case K_TYPE_DELIMITER:
return false;
case K_METHOD_DELIMITER:
return true;
case K_FIELD_INITIALIZER_DELIMITER:
return false;
}
i--;
}
return false;
}
protected boolean isInsideType() {
int i= this.elementPtr;
while (i > -1) {
switch (this.elementKindStack[i]) {
case K_TYPE_DELIMITER:
return true;
case K_METHOD_DELIMITER:
return false;
case K_FIELD_INITIALIZER_DELIMITER:
return false;
}
i--;
}
return false;
}
protected int lastIndexOfElement(int kind) {
int i= this.elementPtr;
while (i > -1) {
if (this.elementKindStack[i] == kind)
return i;
i--;
}
return -1;
}
/**
* 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();
// set the lastModifiers to reflect the modifiers of the constructor whose
// block statements are being parsed
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=202634
this.lastModifiers= cd.modifiers;
this.lastModifiersStart= cd.modifiersSourceStart;
// simulate goForConstructorBody except that we don't want to balance brackets because they are not going to be balanced
goForBlockStatementsopt();
this.referenceContext= cd;
this.compilationUnit= unit;
this.scanner.resetTo(cd.bodyStart, bodyEnd(cd));
consumeNestedMethod();
try {
parse();
} catch (AbortCompilation ex) {
this.lastAct= ERROR_ACTION;
}
if (this.lastAct == ERROR_ACTION) {
cd.bits|= ASTNode.HasSyntaxErrors;
return;
}
// attach the statements as we might be searching for a reference to a local type
cd.explicitDeclarations= this.realBlockStack[this.realBlockPtr--];
int length;
if ((length= this.astLengthStack[this.astLengthPtr--]) != 0) {
this.astPtr-= length;
if (this.astStack[this.astPtr + 1] instanceof ExplicitConstructorCall)
//avoid a isSomeThing that would only be used here BUT what is faster between two alternatives ?
{
System.arraycopy(
this.astStack,
this.astPtr + 2,
cd.statements= new Statement[length - 1],
0,
length - 1);
cd.constructorCall= (ExplicitConstructorCall)this.astStack[this.astPtr + 1];
} else { //need to add explicitly the super();
System.arraycopy(
this.astStack,
this.astPtr + 1,
cd.statements= new Statement[length],
0,
length);
cd.constructorCall= SuperReference.implicitSuperConstructorCall();
}
} else {
cd.constructorCall= SuperReference.implicitSuperConstructorCall();
if (!containsComment(cd.bodyStart, cd.bodyEnd)) {
cd.bits|= ASTNode.UndocumentedEmptyBlock;
}
}
if (cd.constructorCall.sourceEnd == 0) {
cd.constructorCall.sourceEnd= cd.sourceEnd;
cd.constructorCall.sourceStart= cd.sourceStart;
}
}
/**
* Parse the block statements inside the given initializer and try to complete at the cursor
* location.
*/
public void parseBlockStatements(
Initializer initializer,
TypeDeclaration type,
CompilationUnitDeclaration unit) {
initialize();
// set the lastModifiers to reflect the modifiers of the initializer whose
// block statements are being parsed
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=202634
this.lastModifiers= initializer.modifiers;
this.lastModifiersStart= initializer.modifiersSourceStart;
// simulate goForInitializer except that we don't want to balance brackets because they are not going to be balanced
goForBlockStatementsopt();
this.referenceContext= type;
this.compilationUnit= unit;
this.scanner.resetTo(initializer.sourceStart, bodyEnd(initializer)); // just after the beginning {
consumeNestedMethod();
try {
parse();
} catch (AbortCompilation ex) {
this.lastAct= ERROR_ACTION;
} finally {
this.nestedMethod[this.nestedType]--;
}
if (this.lastAct == ERROR_ACTION) {
initializer.bits|= ASTNode.HasSyntaxErrors;
return;
}
// attach the statements as we might be searching for a reference to a local type
initializer.block.explicitDeclarations= this.realBlockStack[this.realBlockPtr--];
int length;
if ((length= this.astLengthStack[this.astLengthPtr--]) > 0) {
System.arraycopy(this.astStack, (this.astPtr-= length) + 1, initializer.block.statements= new Statement[length], 0, length);
} else {
// check whether this block at least contains some comment in it
if (!containsComment(initializer.block.sourceStart, initializer.block.sourceEnd)) {
initializer.block.bits|= ASTNode.UndocumentedEmptyBlock;
}
}
// mark initializer with local type if one was found during parsing
if ((type.bits & ASTNode.HasLocalType) != 0) {
initializer.bits|= ASTNode.HasLocalType;
}
}
/**
* 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 & ExtraCompilerModifiers.AccSemicolonBody) != 0)
return;
initialize();
// set the lastModifiers to reflect the modifiers of the method whose
// block statements are being parsed
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=202634
this.lastModifiers= md.modifiers;
this.lastModifiersStart= md.modifiersSourceStart;
// simulate goForMethodBody except that we don't want to balance brackets because they are not going to be balanced
goForBlockStatementsopt();
this.referenceContext= md;
this.compilationUnit= unit;
this.scanner.resetTo(md.bodyStart, bodyEnd(md)); // reset the scanner to parser from { down to the cursor location
consumeNestedMethod();
try {
parse();
} catch (AbortCompilation ex) {
this.lastAct= ERROR_ACTION;
} finally {
this.nestedMethod[this.nestedType]--;
}
if (this.lastAct == ERROR_ACTION) {
md.bits|= ASTNode.HasSyntaxErrors;
return;
}
// attach the statements as we might be searching for a reference to a local type
md.explicitDeclarations= this.realBlockStack[this.realBlockPtr--];
int length;
if ((length= this.astLengthStack[this.astLengthPtr--]) != 0) {
System.arraycopy(
this.astStack,
(this.astPtr-= length) + 1,
md.statements= new Statement[length],
0,
length);
} else {
if (!containsComment(md.bodyStart, md.bodyEnd)) {
md.bits|= ASTNode.UndocumentedEmptyBlock;
}
}
}
protected void popElement(int kind) {
if (this.elementPtr < 0 || this.elementKindStack[this.elementPtr] != kind)
return;
this.previousKind= this.elementKindStack[this.elementPtr];
this.previousInfo= this.elementInfoStack[this.elementPtr];
this.previousObjectInfo= this.elementObjectInfoStack[this.elementPtr];
this.elementObjectInfoStack[this.elementPtr]= null;
switch (kind) {
default:
this.elementPtr--;
break;
}
}
protected void popUntilElement(int kind) {
if (this.elementPtr < 0)
return;
int i= this.elementPtr;
while (i >= 0 && this.elementKindStack[i] != kind) {
i--;
}
if (i >= 0) {
if (i < this.elementPtr) {
this.previousKind= this.elementKindStack[i + 1];
this.previousInfo= this.elementInfoStack[i + 1];
this.previousObjectInfo= this.elementObjectInfoStack[i + 1];
for (int j= i + 1; j <= this.elementPtr; j++) {
this.elementObjectInfoStack[j]= null;
}
}
this.elementPtr= i;
}
}
/*
* 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;
// initialize element stack
int fieldInitializerIndex= lastIndexOfElement(K_FIELD_INITIALIZER_DELIMITER);
int methodIndex= lastIndexOfElement(K_METHOD_DELIMITER);
if (methodIndex == fieldInitializerIndex) {
// there is no method and no field initializer
flushElementStack();
} else if (methodIndex > fieldInitializerIndex) {
popUntilElement(K_METHOD_DELIMITER);
} else {
popUntilElement(K_FIELD_INITIALIZER_DELIMITER);
}
}
/*
* Prepares the state of the parser to go for Headers.
*/
protected void prepareForHeaders() {
this.nestedMethod[this.nestedType= 0]= 0;
this.variablesCounter[this.nestedType]= 0;
this.realBlockStack[this.realBlockPtr= 0]= 0;
popUntilElement(K_TYPE_DELIMITER);
if (this.topKnownElementKind(ASSIST_PARSER) != K_TYPE_DELIMITER) {
// is outside a type and inside a compilation unit.
// remove all elements.
flushElementStack();
}
}
protected void pushOnElementStack(int kind) {
this.pushOnElementStack(kind, 0, null);
}
protected void pushOnElementStack(int kind, int info) {
this.pushOnElementStack(kind, info, null);
}
protected void pushOnElementStack(int kind, int info, Object objectInfo) {
if (this.elementPtr < -1)
return;
this.previousKind= 0;
this.previousInfo= 0;
this.previousObjectInfo= null;
int stackLength= this.elementKindStack.length;
if (++this.elementPtr >= stackLength) {
System.arraycopy(
this.elementKindStack, 0,
this.elementKindStack= new int[stackLength + StackIncrement], 0,
stackLength);
System.arraycopy(
this.elementInfoStack, 0,
this.elementInfoStack= new int[stackLength + StackIncrement], 0,
stackLength);
System.arraycopy(
this.elementObjectInfoStack, 0,
this.elementObjectInfoStack= new Object[stackLength + StackIncrement], 0,
stackLength);
}
this.elementKindStack[this.elementPtr]= kind;
this.elementInfoStack[this.elementPtr]= info;
this.elementObjectInfoStack[this.elementPtr]= objectInfo;
}
public void recoveryExitFromVariable() {
if (this.currentElement != null && this.currentElement instanceof RecoveredField
&& !(this.currentElement instanceof RecoveredInitializer)) {
RecoveredElement oldElement= this.currentElement;
super.recoveryExitFromVariable();
if (oldElement != this.currentElement) {
popElement(K_FIELD_INITIALIZER_DELIMITER);
}
} else {
super.recoveryExitFromVariable();
}
}
public void recoveryTokenCheck() {
RecoveredElement oldElement= this.currentElement;
switch (this.currentToken) {
case TokenNameLBRACE:
super.recoveryTokenCheck();
if (this.currentElement instanceof RecoveredInitializer) {
if (oldElement instanceof RecoveredField) {
popUntilElement(K_FIELD_INITIALIZER_DELIMITER);
popElement(K_FIELD_INITIALIZER_DELIMITER);
}
if (this.currentElement != oldElement
&& topKnownElementKind(ASSIST_PARSER) != K_METHOD_DELIMITER) {
pushOnElementStack(K_METHOD_DELIMITER);
}
}
break;
case TokenNameRBRACE:
super.recoveryTokenCheck();
if (this.currentElement != oldElement && !isInsideAttributeValue()) {
if (oldElement instanceof RecoveredInitializer
|| oldElement instanceof RecoveredMethod
|| (oldElement instanceof RecoveredBlock && oldElement.parent instanceof RecoveredInitializer)
|| (oldElement instanceof RecoveredBlock && oldElement.parent instanceof RecoveredMethod)) {
popUntilElement(K_METHOD_DELIMITER);
popElement(K_METHOD_DELIMITER);
} else if (oldElement instanceof RecoveredType) {
popUntilElement(K_TYPE_DELIMITER);
if (!(this.referenceContext instanceof CompilationUnitDeclaration)
|| isIndirectlyInsideFieldInitialization()
|| this.currentElement instanceof RecoveredUnit) {
popElement(K_TYPE_DELIMITER);
}
}
}
break;
default:
super.recoveryTokenCheck();
break;
}
}
public void reset() {
flushAssistState();
}
/*
* 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
this.astPtr= -1;
this.astLengthPtr= -1;
this.expressionPtr= -1;
this.expressionLengthPtr= -1;
this.identifierPtr= -1;
this.identifierLengthPtr= -1;
this.intPtr= -1;
this.dimensions= 0;
this.recoveredStaticInitializerStart= 0;
this.genericsIdentifiersLengthPtr= -1;
this.genericsLengthPtr= -1;
this.genericsPtr= -1;
this.modifiers= ClassFileConstants.AccDefault;
this.modifiersSourceStart= -1;
// if in diet mode, reset the diet counter because we're going to restart outside an initializer.
if (this.diet)
this.dietInt= 0;
/* attempt to move checkpoint location */
if (!moveRecoveryCheckpoint())
return false;
// only look for headers
if (this.referenceContext instanceof CompilationUnitDeclaration
|| this.assistNode != null) {
if (isInsideMethod() &&
isIndirectlyInsideFieldInitialization() &&
this.assistNode == null) {
prepareForBlockStatements();
goForBlockStatementsOrCatchHeader();
} else {
prepareForHeaders();
goForHeaders();
this.diet= true; // passed this point, will not consider method bodies
}
return true;
}
if (this.referenceContext instanceof AbstractMethodDeclaration
|| this.referenceContext instanceof TypeDeclaration) {
if (this.currentElement instanceof RecoveredType) {
prepareForHeaders();
goForHeaders();
} else {
prepareForBlockStatements();
goForBlockStatementsOrCatchHeader();
}
return true;
}
// does not know how to restart
return false;
}
public abstract void setAssistIdentifier(char[] assistIdent);
protected int topKnownElementInfo(int owner) {
return topKnownElementInfo(owner, 0);
}
protected int topKnownElementInfo(int owner, int offSet) {
int i= this.elementPtr;
while (i > -1) {
if ((this.elementKindStack[i] & owner) != 0) {
if (offSet <= 0)
return this.elementInfoStack[i];
offSet--;
}
i--;
}
return 0;
}
protected int topKnownElementKind(int owner) {
return topKnownElementKind(owner, 0);
}
protected int topKnownElementKind(int owner, int offSet) {
int i= this.elementPtr;
while (i > -1) {
if ((this.elementKindStack[i] & owner) != 0) {
if (offSet <= 0)
return this.elementKindStack[i];
offSet--;
}
i--;
}
return 0;
}
protected Object topKnownElementObjectInfo(int owner, int offSet) {
int i= this.elementPtr;
while (i > -1) {
if ((this.elementKindStack[i] & owner) != 0) {
if (offSet <= 0)
return this.elementObjectInfoStack[i];
offSet--;
}
i--;
}
return null;
}
protected Object topKnownElementObjectInfo(int owner) {
return topKnownElementObjectInfo(owner, 0);
}
/**
* 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 && topKnownElementKind(ASSIST_PARSER) == K_SELECTOR && ast instanceof Expression &&
(((selector= topKnownElementInfo(ASSIST_PARSER)) == 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;
}
}
}