blob: 48bae14d5ae78e55157eaa304b766973f27d3c51 [file] [log] [blame]
package org.eclipse.jdt.internal.compiler.parser;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.util.*;
/**
* Internal type structure for parsing recovery
*/
public class RecoveredType extends RecoveredStatement implements TerminalSymbols, CompilerModifiers {
public TypeDeclaration typeDeclaration;
public RecoveredType[] memberTypes;
public int memberTypeCount;
public RecoveredField[] fields;
public int fieldCount;
public RecoveredMethod[] methods;
public int methodCount;
public boolean preserveContent = false; // only used for anonymous types
public int bodyEnd;
public RecoveredType(TypeDeclaration typeDeclaration, RecoveredElement parent, int bracketBalance){
super(typeDeclaration, parent, bracketBalance);
this.typeDeclaration = typeDeclaration;
this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
if(this.foundOpeningBrace) {
this.bracketBalance++;
}
}
public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalance) {
/* do not consider a method starting passed the type end (if set)
it must be belonging to an enclosing type */
if (typeDeclaration.declarationSourceEnd != 0
&& methodDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd){
return this.parent.add(methodDeclaration, bracketBalance);
}
if (methods == null) {
methods = new RecoveredMethod[5];
methodCount = 0;
} else {
if (methodCount == methods.length) {
System.arraycopy(
methods,
0,
(methods = new RecoveredMethod[2 * methodCount]),
0,
methodCount);
}
}
RecoveredMethod element = new RecoveredMethod(methodDeclaration, this, bracketBalance);
methods[methodCount++] = element;
/* consider that if the opening brace was not found, it is there */
if (!foundOpeningBrace){
foundOpeningBrace = true;
this.bracketBalance++;
}
/* if method not finished, then method becomes current */
if (methodDeclaration.declarationSourceEnd == 0) return element;
return this;
}
public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalance) {
/* do not consider a field starting passed the type end (if set)
it must be belonging to an enclosing type */
if (typeDeclaration.declarationSourceEnd != 0
&& fieldDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd) {
return this.parent.add(fieldDeclaration, bracketBalance);
}
if (fields == null) {
fields = new RecoveredField[5];
fieldCount = 0;
} else {
if (fieldCount == fields.length) {
System.arraycopy(
fields,
0,
(fields = new RecoveredField[2 * fieldCount]),
0,
fieldCount);
}
}
RecoveredField element = fieldDeclaration.isField()
? new RecoveredField(fieldDeclaration, this, bracketBalance)
: new RecoveredInitializer(fieldDeclaration, this, bracketBalance);
fields[fieldCount++] = element;
/* consider that if the opening brace was not found, it is there */
if (!foundOpeningBrace){
foundOpeningBrace = true;
this.bracketBalance++;
}
/* if field not finished, then field becomes current */
if (fieldDeclaration.declarationSourceEnd == 0) return element;
return this;
}
public RecoveredElement add(TypeDeclaration memberTypeDeclaration, int bracketBalance) {
/* do not consider a type starting passed the type end (if set)
it must be belonging to an enclosing type */
if (typeDeclaration.declarationSourceEnd != 0
&& memberTypeDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd){
return this.parent.add(memberTypeDeclaration, bracketBalance);
}
if (memberTypes == null) {
memberTypes = new RecoveredType[5];
memberTypeCount = 0;
} else {
if (memberTypeCount == memberTypes.length) {
System.arraycopy(
memberTypes,
0,
(memberTypes = new RecoveredType[2 * memberTypeCount]),
0,
memberTypeCount);
}
}
RecoveredType element = new RecoveredType(memberTypeDeclaration, this, bracketBalance);
memberTypes[memberTypeCount++] = element;
/* consider that if the opening brace was not found, it is there */
if (!foundOpeningBrace){
foundOpeningBrace = true;
this.bracketBalance++;
}
/* if member type not finished, then member type becomes current */
if (memberTypeDeclaration.declarationSourceEnd == 0) return element;
return this;
}
/*
* Answer the body end of the corresponding parse node
*/
public int bodyEnd(){
if (bodyEnd == 0) return typeDeclaration.declarationSourceEnd;
return bodyEnd;
}
public boolean bodyStartsAtHeaderEnd(){
if (typeDeclaration.superInterfaces == null){
if (typeDeclaration.superclass == null){
return typeDeclaration.bodyStart == typeDeclaration.sourceEnd+1;
} else {
return typeDeclaration.bodyStart == typeDeclaration.superclass.sourceEnd+1;
}
} else {
return typeDeclaration.bodyStart
== typeDeclaration.superInterfaces[typeDeclaration.superInterfaces.length-1].sourceEnd+1;
}
}
/*
* Answer the enclosing type node, or null if none
*/
public RecoveredType enclosingType(){
RecoveredElement current = parent;
while (current != null){
if (current instanceof RecoveredType){
return (RecoveredType) current;
}
current = current.parent;
}
return null;
}
public char[] name(){
return typeDeclaration.name;
}
/*
* Answer the associated parsed structure
*/
public AstNode parseTree(){
return typeDeclaration;
}
/*
* Answer the very source end of the corresponding parse node
*/
public int sourceEnd(){
return this.typeDeclaration.declarationSourceEnd;
}
public String toString(int tab) {
StringBuffer result = new StringBuffer(tabString(tab));
result.append("Recovered type:\n"); //$NON-NLS-1$
if (typeDeclaration instanceof AnonymousLocalTypeDeclaration) {
result.append(tabString(tab));
result.append(" "); //$NON-NLS-1$
}
result.append(typeDeclaration.toString(tab + 1));
if (this.memberTypes != null) {
for (int i = 0; i < this.memberTypeCount; i++) {
result.append("\n"); //$NON-NLS-1$
result.append(this.memberTypes[i].toString(tab + 1));
}
}
if (this.fields != null) {
for (int i = 0; i < this.fieldCount; i++) {
result.append("\n"); //$NON-NLS-1$
result.append(this.fields[i].toString(tab + 1));
}
}
if (this.methods != null) {
for (int i = 0; i < this.methodCount; i++) {
result.append("\n"); //$NON-NLS-1$
result.append(this.methods[i].toString(tab + 1));
}
}
return result.toString();
}
/*
* Update the bodyStart of the corresponding parse node
*/
public void updateBodyStart(int bodyStart){
this.foundOpeningBrace = true;
this.typeDeclaration.bodyStart = bodyStart;
}
public Statement updatedStatement(){
// ignore closed anonymous type
if (typeDeclaration instanceof AnonymousLocalTypeDeclaration
&& !this.preserveContent){
return null;
}
TypeDeclaration updatedType = this.updatedTypeDeclaration();
if (updatedType instanceof AnonymousLocalTypeDeclaration){
/* in presence of an anonymous type, we want the full allocation expression */
return ((AnonymousLocalTypeDeclaration)updatedType).allocation;
}
return updatedType;
}
public TypeDeclaration updatedTypeDeclaration(){
/* update member types */
if (memberTypeCount > 0){
int existingCount = typeDeclaration.memberTypes == null ? 0 : typeDeclaration.memberTypes.length;
MemberTypeDeclaration[] memberTypeDeclarations = new MemberTypeDeclaration[existingCount + memberTypeCount];
if (existingCount > 0){
System.arraycopy(typeDeclaration.memberTypes, 0, memberTypeDeclarations, 0, existingCount);
}
// may need to update the declarationSourceEnd of the last type
if (memberTypes[memberTypeCount - 1].typeDeclaration.declarationSourceEnd == 0){
memberTypes[memberTypeCount - 1].typeDeclaration.declarationSourceEnd = bodyEnd();
}
for (int i = 0; i < memberTypeCount; i++){
memberTypeDeclarations[existingCount + i] = (MemberTypeDeclaration)memberTypes[i].updatedTypeDeclaration();
}
typeDeclaration.memberTypes = memberTypeDeclarations;
}
/* update fields */
if (fieldCount > 0){
int existingCount = typeDeclaration.fields == null ? 0 : typeDeclaration.fields.length;
FieldDeclaration[] fieldDeclarations = new FieldDeclaration[existingCount + fieldCount];
if (existingCount > 0){
System.arraycopy(typeDeclaration.fields, 0, fieldDeclarations, 0, existingCount);
}
// may need to update the declarationSourceEnd of the last field
if (fields[fieldCount - 1].fieldDeclaration.declarationSourceEnd == 0){
fields[fieldCount - 1].fieldDeclaration.declarationSourceEnd = bodyEnd();
}
for (int i = 0; i < fieldCount; i++){
fieldDeclarations[existingCount + i] = fields[i].updatedFieldDeclaration();
}
typeDeclaration.fields = fieldDeclarations;
}
/* update methods */
int existingCount = typeDeclaration.methods == null ? 0 : typeDeclaration.methods.length;
boolean hasConstructor = false, hasRecoveredConstructor = false;
int defaultConstructorIndex = -1;
if (methodCount > 0){
AbstractMethodDeclaration[] methodDeclarations = new AbstractMethodDeclaration[existingCount + methodCount];
for (int i = 0; i < existingCount; i++){
AbstractMethodDeclaration m = typeDeclaration.methods[i];
if (m.isDefaultConstructor()) defaultConstructorIndex = i;
methodDeclarations[i] = m;
}
// may need to update the declarationSourceEnd of the last method
if (methods[methodCount - 1].methodDeclaration.declarationSourceEnd == 0){
methods[methodCount - 1].methodDeclaration.declarationSourceEnd = bodyEnd();
}
for (int i = 0; i < methodCount; i++){
AbstractMethodDeclaration updatedMethod = methods[i].updatedMethodDeclaration();
if (updatedMethod.isConstructor()) hasRecoveredConstructor = true;
methodDeclarations[existingCount + i] = updatedMethod;
}
typeDeclaration.methods = methodDeclarations;
hasConstructor = typeDeclaration.checkConstructors(this.parser());
} else {
for (int i = 0; i < existingCount; i++){
if (typeDeclaration.methods[i].isConstructor()) hasConstructor = true;
}
}
/* add clinit ? */
if (typeDeclaration.needClassInitMethod()){
boolean alreadyHasClinit = false;
for (int i = 0; i < existingCount; i++){
if (typeDeclaration.methods[i].isClinit()){
alreadyHasClinit = true;
break;
}
}
if (!alreadyHasClinit) typeDeclaration.addClinit();
}
/* add default constructor ? */
if (defaultConstructorIndex >= 0 && hasRecoveredConstructor){
/* should discard previous default construtor */
AbstractMethodDeclaration[] methodDeclarations = new AbstractMethodDeclaration[typeDeclaration.methods.length - 1];
if (defaultConstructorIndex != 0){
System.arraycopy(typeDeclaration.methods, 0, methodDeclarations, 0, defaultConstructorIndex);
}
if (defaultConstructorIndex != typeDeclaration.methods.length-1){
System.arraycopy(
typeDeclaration.methods,
defaultConstructorIndex+1,
methodDeclarations,
defaultConstructorIndex,
typeDeclaration.methods.length - defaultConstructorIndex - 1);
}
typeDeclaration.methods = methodDeclarations;
} else {
if (!hasConstructor) {// if was already reduced, then constructor
typeDeclaration.createsInternalConstructor(true, true);
}
}
/* might need to cast itself into a MemberTypeDeclaration or a LocalTypeDeclaration */
TypeDeclaration newTypeDeclaration = null;
if ((typeDeclaration instanceof TypeDeclaration) && (parent instanceof RecoveredType)){
newTypeDeclaration = new MemberTypeDeclaration();
} else {
if ((typeDeclaration instanceof TypeDeclaration) && (parent instanceof RecoveredMethod)){
newTypeDeclaration = new LocalTypeDeclaration();
}
}
/* copy slots into new type */
if (newTypeDeclaration != null){
newTypeDeclaration.modifiers = typeDeclaration.modifiers;
newTypeDeclaration.modifiersSourceStart = typeDeclaration.modifiersSourceStart;
newTypeDeclaration.name = typeDeclaration.name;
newTypeDeclaration.superclass = typeDeclaration.superclass;
newTypeDeclaration.superInterfaces = typeDeclaration.superInterfaces;
newTypeDeclaration.fields = typeDeclaration.fields;
newTypeDeclaration.methods = typeDeclaration.methods;
newTypeDeclaration.memberTypes = typeDeclaration.memberTypes;
newTypeDeclaration.ignoreFurtherInvestigation = typeDeclaration.ignoreFurtherInvestigation;
newTypeDeclaration.maxFieldCount = typeDeclaration.maxFieldCount;
newTypeDeclaration.declarationSourceStart = typeDeclaration.declarationSourceStart;
newTypeDeclaration.declarationSourceEnd = typeDeclaration.declarationSourceEnd;
newTypeDeclaration.bodyStart = typeDeclaration.bodyStart;
typeDeclaration = newTypeDeclaration;
}
return typeDeclaration;
}
/*
* Update the corresponding parse node from parser state which
* is about to disappear because of restarting recovery
*/
public void updateFromParserState(){
if(this.bodyStartsAtHeaderEnd()){
Parser parser = this.parser();
/* might want to recover implemented interfaces */
if (parser.listLength > 0){ // awaiting interface type references
parser.consumeClassHeaderImplements();
// will reset typeListLength to zero
// thus this check will only be performed on first errorCheck after class X implements Y,Z,
}
}
}
/*
* 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);
this.bodyEnd = braceStart - 1;
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 braceEnd){
/* in case the opening brace is not close enough to the signature, ignore it */
if (bracketBalance == 0){
/*
if (parser.scanner.searchLineNumber(typeDeclaration.sourceEnd)
!= parser.scanner.searchLineNumber(braceEnd)){
*/
Parser parser = this.parser();
switch(parser.lastIgnoredToken){
case -1 :
case TokenNameextends :
case TokenNameimplements :
if (parser.recoveredStaticInitializerStart == 0) break;
default:
this.foundOpeningBrace = true;
bracketBalance = 1; // pretend the brace was already there
}
}
// might be an initializer
if (this.bracketBalance == 1){
Block block = new Block(0);
Parser parser = this.parser();
block.sourceStart = parser.scanner.startPosition;
Initializer init;
if (parser.recoveredStaticInitializerStart == 0){
init = new Initializer(block, AccDefault);
} else {
init = new Initializer(block, AccStatic);
init.declarationSourceStart = parser.recoveredStaticInitializerStart;
}
return this.add(init, 1);
}
return super.updateOnOpeningBrace(braceEnd);
}
public void updateParseTree(){
this.updatedTypeDeclaration();
}
/*
* Update the declarationSourceEnd of the corresponding parse node
*/
public void updateSourceEndIfNecessary(int sourceEnd){
if (this.typeDeclaration.declarationSourceEnd == 0){
this.bodyEnd = 0;
this.typeDeclaration.declarationSourceEnd = sourceEnd;
}
}
}