package org.eclipse.jdt.internal.compiler.parser; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
/** | |
* Internal block structure for parsing recovery | |
*/ | |
import org.eclipse.jdt.internal.compiler.ast.*; | |
import org.eclipse.jdt.internal.compiler.lookup.*; | |
import org.eclipse.jdt.internal.compiler.util.CharOperation; | |
public class RecoveredBlock extends RecoveredStatement implements CompilerModifiers, TerminalSymbols, BaseTypes { | |
public Block blockDeclaration; | |
public RecoveredStatement[] statements; | |
public int statementCount; | |
public boolean preserveContent = false; | |
public RecoveredLocalVariable pendingArgument; | |
public RecoveredBlock(Block block, RecoveredElement parent, int bracketBalance){ | |
super(block, parent, bracketBalance); | |
this.blockDeclaration = block; | |
this.foundOpeningBrace = true; | |
} | |
/* | |
* Record a nested block declaration | |
*/ | |
public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalance) { | |
/* do not consider a nested block starting passed the block end (if set) | |
it must be belonging to an enclosing block */ | |
if (blockDeclaration.sourceEnd != 0 | |
&& nestedBlockDeclaration.sourceStart > blockDeclaration.sourceEnd){ | |
return this.parent.add(nestedBlockDeclaration, bracketBalance); | |
} | |
RecoveredBlock element = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalance); | |
// if we have a pending Argument, promote it into the new block | |
if (pendingArgument != null){ | |
element.attach(pendingArgument); | |
pendingArgument = null; | |
} | |
this.attach(element); | |
if (nestedBlockDeclaration.sourceEnd == 0) return element; | |
return this; | |
} | |
/* | |
* Record a local declaration | |
*/ | |
public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalance) { | |
/* do not consider a local variable starting passed the block end (if set) | |
it must be belonging to an enclosing block */ | |
if (blockDeclaration.sourceEnd != 0 | |
&& localDeclaration.declarationSourceStart > blockDeclaration.sourceEnd){ | |
return this.parent.add(localDeclaration, bracketBalance); | |
} | |
RecoveredLocalVariable element = new RecoveredLocalVariable(localDeclaration, this, bracketBalance); | |
if (localDeclaration instanceof Argument){ | |
pendingArgument = element; | |
return this; | |
} | |
this.attach(element); | |
if (localDeclaration.declarationSourceEnd == 0) return element; | |
return this; | |
} | |
/* | |
* Record a statement declaration | |
*/ | |
public RecoveredElement add(Statement statement, int bracketBalance) { | |
/* do not consider a nested block starting passed the block end (if set) | |
it must be belonging to an enclosing block */ | |
if (blockDeclaration.sourceEnd != 0 | |
&& statement.sourceStart > blockDeclaration.sourceEnd){ | |
return this.parent.add(statement, bracketBalance); | |
} | |
RecoveredStatement element = new RecoveredStatement(statement, this, bracketBalance); | |
this.attach(element); | |
if (statement.sourceEnd == 0) return element; | |
return this; | |
} | |
/* | |
* Addition of a type to an initializer (act like inside method body) | |
*/ | |
public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalance) { | |
/* do not consider a type starting passed the block end (if set) | |
it must be belonging to an enclosing block */ | |
if (blockDeclaration.sourceEnd != 0 | |
&& typeDeclaration.declarationSourceStart > blockDeclaration.sourceEnd){ | |
return this.parent.add(typeDeclaration, bracketBalance); | |
} | |
RecoveredStatement element = new RecoveredType(typeDeclaration, this, bracketBalance); | |
this.attach(element); | |
if (typeDeclaration.declarationSourceEnd == 0) return element; | |
return this; | |
} | |
/* | |
* Attach a recovered statement | |
*/ | |
void attach(RecoveredStatement recoveredStatement) { | |
if (statements == null) { | |
statements = new RecoveredStatement[5]; | |
statementCount = 0; | |
} else { | |
if (statementCount == statements.length) { | |
System.arraycopy( | |
statements, | |
0, | |
(statements = new RecoveredStatement[2 * statementCount]), | |
0, | |
statementCount); | |
} | |
} | |
statements[statementCount++] = recoveredStatement; | |
} | |
/* | |
* Answer the associated parsed structure | |
*/ | |
public AstNode parseTree(){ | |
return blockDeclaration; | |
} | |
public String toString(int tab) { | |
StringBuffer result = new StringBuffer(tabString(tab)); | |
result.append("Recovered block:\n"); //$NON-NLS-1$ | |
result.append(blockDeclaration.toString(tab + 1)); | |
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(){ | |
// if block was not marked to be preserved or empty, then ignore it | |
if (!preserveContent || statementCount == 0) return null; | |
Statement[] updatedStatements = new Statement[statementCount]; | |
int updatedCount = 0; | |
// only collect the non-null updated statements | |
for (int i = 0; i < statementCount; i++){ | |
Statement updatedStatement = statements[i].updatedStatement(); | |
if (updatedStatement != null){ | |
updatedStatements[updatedCount++] = updatedStatement; | |
} | |
} | |
if (updatedCount == 0) return null; // not interesting block | |
// resize statement collection if necessary | |
if (updatedCount != statementCount){ | |
blockDeclaration.statements = new Statement[updatedCount]; | |
System.arraycopy(updatedStatements, 0, blockDeclaration.statements, 0, updatedCount); | |
} else { | |
blockDeclaration.statements = updatedStatements; | |
} | |
return blockDeclaration; | |
} | |
/* | |
* Rebuild a statement from the nested structure which is in scope | |
*/ | |
public Statement updatedStatement(){ | |
return this.updatedBlock(); | |
} | |
/* | |
* 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 ((--bracketBalance <= 0) && (parent != null)){ | |
this.updateSourceEndIfNecessary(braceEnd); | |
/* if the block is the method body, then it closes the method too */ | |
RecoveredMethod method = enclosingMethod(); | |
if (method != null && method.methodBody == this){ | |
return parent.updateOnClosingBrace(braceStart, braceEnd); | |
} | |
return 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 currentPosition){ | |
// 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(){ | |
this.updatedBlock(); | |
} | |
/* | |
* Rebuild a flattened block from the nested structure which is in scope | |
*/ | |
public Statement updateStatement(){ | |
// if block was closed or empty, then ignore it | |
if (this.blockDeclaration.sourceEnd != 0 || statementCount == 0) return null; | |
Statement[] updatedStatements = new Statement[statementCount]; | |
int updatedCount = 0; | |
// only collect the non-null updated statements | |
for (int i = 0; i < statementCount; i++){ | |
Statement updatedStatement = statements[i].updatedStatement(); | |
if (updatedStatement != null){ | |
updatedStatements[updatedCount++] = updatedStatement; | |
} | |
} | |
if (updatedCount == 0) return null; // not interesting block | |
// resize statement collection if necessary | |
if (updatedCount != statementCount){ | |
blockDeclaration.statements = new Statement[updatedCount]; | |
System.arraycopy(updatedStatements, 0, blockDeclaration.statements, 0, updatedCount); | |
} else { | |
blockDeclaration.statements = updatedStatements; | |
} | |
return blockDeclaration; | |
} | |
/* | |
* Record a field declaration | |
*/ | |
public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalance) { | |
/* local variables inside method can only be final and non void */ | |
char[][] fieldTypeName; | |
if ((fieldDeclaration.modifiers & ~AccFinal) != 0 /* local var can only be final */ | |
|| (fieldDeclaration.type == null) // initializer | |
|| ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void | |
&& CharOperation.equals(fieldTypeName[0], VoidBinding.sourceName()))){ | |
this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1)); | |
return this.parent.add(fieldDeclaration, bracketBalance); | |
} | |
/* do not consider a local variable starting passed the block end (if set) | |
it must be belonging to an enclosing block */ | |
if (blockDeclaration.sourceEnd != 0 | |
&& fieldDeclaration.declarationSourceStart > blockDeclaration.sourceEnd){ | |
return this.parent.add(fieldDeclaration, bracketBalance); | |
} | |
// 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; | |
} | |
} |