* 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();
this.snapShot = null;
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;
FieldDeclaration[] fields = type.fields;
int length = fields == null ? 0 : fields.length;
for (int i = 0; i < length; i++){
FieldDeclaration field = 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
ASTNode node = null, lastNode = null;
for (int i = 0; i <= this.astPtr; i++, lastNode = node) {
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, lastNode instanceof LambdaExpression);
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 (this.assistNode != null && node instanceof Statement) {
Statement stmt = (Statement) node;
if (!(stmt instanceof Expression) || ((Expression) stmt).statementExpression()) {
if (this.assistNode.sourceStart >= stmt.sourceStart && this.assistNode.sourceEnd <= stmt.sourceEnd) {
element.add(stmt, 0);
this.lastCheckPoint = stmt.sourceEnd + 1;
this.isOrphanCompletionNode = false;
}
}
continue;
}
if (node instanceof ImportReference){
ImportReference importRef = (ImportReference) node;
element = element.add(importRef, 0);
this.lastCheckPoint = importRef.declarationSourceEnd + 1;
}
}
if (this.currentToken == TokenNameRBRACE && !isIndirectlyInsideLambdaExpression()) {
this.currentToken = 0; // closing brace has already been taken care of
}
/* might need some extra block (after the last reduced node) */
/* For block bodied lambdas we should create a block even though the lambda header appears before it, so elements from within don't get misattributed. */
int pos = this.assistNode == null ? this.lastCheckPoint : this.assistNode.sourceStart;
boolean createLambdaBlock = lastNode instanceof LambdaExpression && ((LambdaExpression) node).body() instanceof Block;
for (int j = blockIndex; j <= this.realBlockPtr; j++){
if (this.blockStarts[j] >= 0) {
if ((this.blockStarts[j] < pos || createLambdaBlock) && (this.blockStarts[j] != lastStart)){ // avoid multiple block if at same position
block = new Block(0, createLambdaBlock);
block.sourceStart = lastStart = this.blockStarts[j];
element = element.add(block, 1);
createLambdaBlock = false;
}
} 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;