/*******************************************************************************
* Copyright (c) 2000, 2009 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.compiler.parser;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
public class RecoveredBlock extends RecoveredStatement implements TerminalTokens {
public Block blockDeclaration;
public RecoveredStatement[] statements;
public int statementCount;
public boolean preserveContent= false;
public RecoveredLocalVariable pendingArgument;
int pendingModifiers;
int pendingModifersSourceStart= -1;
RecoveredAnnotation[] pendingAnnotations;
int pendingAnnotationCount;
public RecoveredBlock(Block block, RecoveredElement parent, int bracketBalance) {
super(block, parent, bracketBalance);
this.blockDeclaration= block;
this.foundOpeningBrace= true;
this.preserveContent= parser().methodRecoveryActivated || parser().statementRecoveryActivated;
}
public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalanceValue) {
if (this.parent != null && this.parent instanceof RecoveredMethod) {
RecoveredMethod enclosingRecoveredMethod= (RecoveredMethod)this.parent;
if (enclosingRecoveredMethod.methodBody == this && enclosingRecoveredMethod.parent == null) {
resetPendingModifiers();
// the element cannot be added because we are in the body of a top level method
return this; // ignore this element
}
}
return super.add(methodDeclaration, bracketBalanceValue);
}
/*
* Record a nested block declaration
*/
public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {
resetPendingModifiers();
/* do not consider a nested block starting passed the block end (if set)
it must be belonging to an enclosing block */
if (this.blockDeclaration.sourceEnd != 0
&& nestedBlockDeclaration.sourceStart > this.blockDeclaration.sourceEnd) {
return this.parent.add(nestedBlockDeclaration, bracketBalanceValue);
}
RecoveredBlock element= new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue);
// if we have a pending Argument, promote it into the new block
if (this.pendingArgument != null) {
element.attach(this.pendingArgument);
this.pendingArgument= null;
}
if (parser().statementRecoveryActivated) {
addBlockStatement(element);
}
attach(element);
if (nestedBlockDeclaration.sourceEnd == 0)
return element;
return this;
}
/*
* Record a local declaration
*/
public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) {
return this.add(localDeclaration, bracketBalanceValue, false);
}
/*
* Record a local declaration
*/
public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue, boolean delegatedByParent) {
/* local variables inside method can only be final and non void */
/*
char[][] localTypeName;
if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
|| (localDeclaration.type == null) // initializer
|| ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
&& CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){
if (delegatedByParent){
return this; //ignore
} else {
this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
return this.parent.add(localDeclaration, bracketBalance);
}
}
*/
/* do not consider a local variable starting passed the block end (if set)
it must be belonging to an enclosing block */
if (this.blockDeclaration.sourceEnd != 0
&& localDeclaration.declarationSourceStart > this.blockDeclaration.sourceEnd) {
resetPendingModifiers();
if (delegatedByParent)
return this; //ignore
return this.parent.add(localDeclaration, bracketBalanceValue);
}
RecoveredLocalVariable element= new RecoveredLocalVariable(localDeclaration, this, bracketBalanceValue);
if (this.pendingAnnotationCount > 0) {
element.attach(
this.pendingAnnotations,
this.pendingAnnotationCount,
this.pendingModifiers,
this.pendingModifersSourceStart);
}
resetPendingModifiers();
if (localDeclaration instanceof Argument) {
this.pendingArgument= element;
return this;
}
attach(element);
if (localDeclaration.declarationSourceEnd == 0)
return element;
return this;
}
/*
* Record a statement declaration
*/
public RecoveredElement add(Statement stmt, int bracketBalanceValue) {
return this.add(stmt, bracketBalanceValue, false);
}
/*
* Record a statement declaration
*/
public RecoveredElement add(Statement stmt, int bracketBalanceValue, boolean delegatedByParent) {
resetPendingModifiers();
/* do not consider a nested block starting passed the block end (if set)
it must be belonging to an enclosing block */
if (this.blockDeclaration.sourceEnd != 0
&& stmt.sourceStart > this.blockDeclaration.sourceEnd) {
if (delegatedByParent)
return this; //ignore
return this.parent.add(stmt, bracketBalanceValue);
}
RecoveredStatement element= new RecoveredStatement(stmt, this, bracketBalanceValue);
attach(element);
if (stmt.sourceEnd == 0)
return element;
return this;
}
/*
* Addition of a type to an initializer (act like inside method body)
*/
public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) {
return this.add(typeDeclaration, bracketBalanceValue, false);
}
/*
* Addition of a type to an initializer (act like inside method body)
*/
public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue, boolean delegatedByParent) {
/* do not consider a type starting passed the block end (if set)
it must be belonging to an enclosing block */
if (this.blockDeclaration.sourceEnd != 0
&& typeDeclaration.declarationSourceStart > this.blockDeclaration.sourceEnd) {
resetPendingModifiers();
if (delegatedByParent)
return this; //ignore
return this.parent.add(typeDeclaration, bracketBalanceValue);
}
RecoveredType element= new RecoveredType(typeDeclaration, this, bracketBalanceValue);
if (this.pendingAnnotationCount > 0) {
element.attach(
this.pendingAnnotations,
this.pendingAnnotationCount,
this.pendingModifiers,
this.pendingModifersSourceStart);
}
resetPendingModifiers();
attach(element);
if (typeDeclaration.declarationSourceEnd == 0)
return element;
return this;
}
public RecoveredElement addAnnotationName(int identifierPtr, int identifierLengthPtr, int annotationStart, int bracketBalanceValue) {
if (this.pendingAnnotations == null) {
this.pendingAnnotations= new RecoveredAnnotation[5];
this.pendingAnnotationCount= 0;
} else {
if (this.pendingAnnotationCount == this.pendingAnnotations.length) {
System.arraycopy(
this.pendingAnnotations,
0,
(this.pendingAnnotations= new RecoveredAnnotation[2 * this.pendingAnnotationCount]),
0,
this.pendingAnnotationCount);
}
}
RecoveredAnnotation element= new RecoveredAnnotation(identifierPtr, identifierLengthPtr, annotationStart, this, bracketBalanceValue);
this.pendingAnnotations[this.pendingAnnotationCount++]= element;
return element;
}
public void addModifier(int flag, int modifiersSourceStart) {
this.pendingModifiers|= flag;
if (this.pendingModifersSourceStart < 0) {
this.pendingModifersSourceStart= modifiersSourceStart;
}
}
/*
* Attach a recovered statement
*/
void attach(RecoveredStatement recoveredStatement) {
if (this.statements == null) {
this.statements= new RecoveredStatement[5];
this.statementCount= 0;
} else {
if (this.statementCount == this.statements.length) {
System.arraycopy(
this.statements,
0,
(this.statements= new RecoveredStatement[2 * this.statementCount]),
0,
this.statementCount);
}
}
this.statements[this.statementCount++]= recoveredStatement;
}
void attachPendingModifiers(RecoveredAnnotation[] pendingAnnots, int pendingAnnotCount, int pendingMods, int pendingModsSourceStart) {
this.pendingAnnotations= pendingAnnots;
this.pendingAnnotationCount= pendingAnnotCount;
this.pendingModifiers= pendingMods;
this.pendingModifersSourceStart= pendingModsSourceStart;
}
/*
* Answer the associated parsed structure
*/
public ASTNode parseTree() {
return this.blockDeclaration;
}
public void resetPendingModifiers() {
this.pendingAnnotations= null;
this.pendingAnnotationCount= 0;
this.pendingModifiers= 0;
this.pendingModifersSourceStart= -1;
}
public String toString(int tab) {
StringBuffer result= new StringBuffer(tabString(tab));
result.append("Recovered block:\n"); //$NON-NLS-1$
this.blockDeclaration.print(tab + 1, result);
if (this.statements != null) {
for (int i= 0; i < this.statementCount; i++) {
result.append("\n"); //$NON-NLS-1$
result.append(this.statements[i].toString(tab + 1));
}
}
return result.toString();
}
/*
* Rebuild a block from the nested structure which is in scope
*/
public Block updatedBlock(int depth, Set knownTypes) {
// if block was not marked to be preserved or empty, then ignore it
if (!this.preserveContent || this.statementCount == 0)
return null;
Statement[] updatedStatements= new Statement[this.statementCount];
int updatedCount= 0;
// may need to update the end of the last statement
RecoveredStatement lastStatement= this.statements[this.statementCount - 1];
RecoveredMethod enclosingMethod= enclosingMethod();
RecoveredInitializer enclosingIntializer= enclosingInitializer();
int bodyEndValue= 0;
if (enclosingMethod != null) {
bodyEndValue= enclosingMethod.methodDeclaration.bodyEnd;
if (enclosingIntializer != null && enclosingMethod.methodDeclaration.sourceStart < enclosingIntializer.fieldDeclaration.sourceStart) {
bodyEndValue= enclosingIntializer.fieldDeclaration.declarationSourceEnd;
}
} else if (enclosingIntializer != null) {
bodyEndValue= enclosingIntializer.fieldDeclaration.declarationSourceEnd;
} else {
bodyEndValue= this.blockDeclaration.sourceEnd - 1;
}
if (lastStatement instanceof RecoveredLocalVariable) {
RecoveredLocalVariable lastLocalVariable= (RecoveredLocalVariable)lastStatement;
if (lastLocalVariable.localDeclaration.declarationSourceEnd == 0) {
lastLocalVariable.localDeclaration.declarationSourceEnd= bodyEndValue;
lastLocalVariable.localDeclaration.declarationEnd= bodyEndValue;
}
} else if (lastStatement instanceof RecoveredBlock) {
RecoveredBlock lastBlock= (RecoveredBlock)lastStatement;
if (lastBlock.blockDeclaration.sourceEnd == 0) {
lastBlock.blockDeclaration.sourceEnd= bodyEndValue;
}
} else if (!(lastStatement instanceof RecoveredType)) {
if (lastStatement.statement.sourceEnd == 0) {
lastStatement.statement.sourceEnd= bodyEndValue;
}
}
int lastEnd= this.blockDeclaration.sourceStart;
// only collect the non-null updated statements
for (int i= 0; i < this.statementCount; i++) {
Statement updatedStatement= this.statements[i].updatedStatement(depth, knownTypes);
if (updatedStatement != null) {
updatedStatements[updatedCount++]= updatedStatement;
if (updatedStatement instanceof LocalDeclaration) {
LocalDeclaration localDeclaration= (LocalDeclaration)updatedStatement;
if (localDeclaration.declarationSourceEnd > lastEnd) {
lastEnd= localDeclaration.declarationSourceEnd;
}
} else if (updatedStatement instanceof TypeDeclaration) {
TypeDeclaration typeDeclaration= (TypeDeclaration)updatedStatement;
if (typeDeclaration.declarationSourceEnd > lastEnd) {
lastEnd= typeDeclaration.declarationSourceEnd;
}
} else {
if (updatedStatement.sourceEnd > lastEnd) {
lastEnd= updatedStatement.sourceEnd;
}
}
}
}
if (updatedCount == 0)
return null; // not interesting block
// resize statement collection if necessary
if (updatedCount != this.statementCount) {
this.blockDeclaration.statements= new Statement[updatedCount];
System.arraycopy(updatedStatements, 0, this.blockDeclaration.statements, 0, updatedCount);
} else {
this.blockDeclaration.statements= updatedStatements;
}
if (this.blockDeclaration.sourceEnd == 0) {
if (lastEnd < bodyEndValue) {
this.blockDeclaration.sourceEnd= bodyEndValue;
} else {
this.blockDeclaration.sourceEnd= lastEnd;
}
}
return this.blockDeclaration;
}
/*
* Rebuild a statement from the nested structure which is in scope
*/
public Statement updatedStatement(int depth, Set knownTypes) {
return updatedBlock(depth, knownTypes);
}
/*
* A closing brace got consumed, might have closed the current element,
* in which case both the currentElement is exited
*/
public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd) {
if ((--this.bracketBalance <= 0) && (this.parent != null)) {
this.updateSourceEndIfNecessary(braceStart, braceEnd);
/* if the block is the method body, then it closes the method too */
RecoveredMethod method= enclosingMethod();
if (method != null && method.methodBody == this) {
return this.parent.updateOnClosingBrace(braceStart, braceEnd);
}
RecoveredInitializer initializer= enclosingInitializer();
if (initializer != null && initializer.initializerBody == this) {
return this.parent.updateOnClosingBrace(braceStart, braceEnd);
}
return this.parent;
}
return this;
}
/*
* An opening brace got consumed, might be the expected opening one of the current element,
* in which case the bodyStart is updated.
*/
public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd) {
// create a nested block
Block block= new Block(0);
block.sourceStart= parser().scanner.startPosition;
return this.add(block, 1);
}
/*
* Final update the corresponding parse node
*/
public void updateParseTree() {
updatedBlock(0, new HashSet());
}
/*
* Rebuild a flattened block from the nested structure which is in scope
*/
public Statement updateStatement(int depth, Set knownTypes) {
// if block was closed or empty, then ignore it
if (this.blockDeclaration.sourceEnd != 0 || this.statementCount == 0)
return null;
Statement[] updatedStatements= new Statement[this.statementCount];
int updatedCount= 0;
// only collect the non-null updated statements
for (int i= 0; i < this.statementCount; i++) {
Statement updatedStatement= this.statements[i].updatedStatement(depth, knownTypes);
if (updatedStatement != null) {
updatedStatements[updatedCount++]= updatedStatement;
}
}
if (updatedCount == 0)
return null; // not interesting block
// resize statement collection if necessary
if (updatedCount != this.statementCount) {
this.blockDeclaration.statements= new Statement[updatedCount];
System.arraycopy(updatedStatements, 0, this.blockDeclaration.statements, 0, updatedCount);
} else {
this.blockDeclaration.statements= updatedStatements;
}
return this.blockDeclaration;
}
/*
* Record a field declaration
*/
public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
resetPendingModifiers();
/* local variables inside method can only be final and non void */
char[][] fieldTypeName;
if ((fieldDeclaration.modifiers & ~ClassFileConstants.AccFinal) != 0 // local var can only be final
|| (fieldDeclaration.type == null) // initializer
|| ((fieldTypeName= fieldDeclaration.type.getTypeName()).length == 1 // non void
&& CharOperation.equals(fieldTypeName[0], TypeBinding.VOID.sourceName()))) {
this.updateSourceEndIfNecessary(previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
return this.parent.add(fieldDeclaration, bracketBalanceValue);
}
/* do not consider a local variable starting passed the block end (if set)
it must be belonging to an enclosing block */
if (this.blockDeclaration.sourceEnd != 0
&& fieldDeclaration.declarationSourceStart > this.blockDeclaration.sourceEnd) {
return this.parent.add(fieldDeclaration, bracketBalanceValue);
}
// ignore the added field, since indicates a local variable behind recovery point
// which thus got parsed as a field reference. This can happen if restarting after
// having reduced an assistNode to get the following context (see 1GEK7SG)
return this;
}
}