org.eclipse.jdt.internal.compiler.parser.RecoveredField Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.parser;
/**
* Internal field structure for parsing recovery
*/
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Expression;
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;
public class RecoveredField extends RecoveredElement {
public FieldDeclaration fieldDeclaration;
boolean alreadyCompletedFieldInitialization;
public RecoveredAnnotation[] annotations;
public int annotationCount;
public int modifiers;
public int modifiersStart;
public RecoveredType[] anonymousTypes;
public int anonymousTypeCount;
public RecoveredField(FieldDeclaration fieldDeclaration, RecoveredElement parent, int bracketBalance){
this(fieldDeclaration, parent, bracketBalance, null);
}
public RecoveredField(FieldDeclaration fieldDeclaration, RecoveredElement parent, int bracketBalance, Parser parser){
super(parent, bracketBalance, parser);
this.fieldDeclaration = fieldDeclaration;
this.alreadyCompletedFieldInitialization = fieldDeclaration.initialization != null;
}
/*
* Record a local declaration
*/
@Override
public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) {
if (this.lambdaNestLevel > 0) // current element is really the lambda which is recovered in full elsewhere.
return this;
return super.add(localDeclaration, bracketBalanceValue);
}
/*
* Record a field declaration
*/
@Override
public RecoveredElement add(FieldDeclaration addedfieldDeclaration, int bracketBalanceValue) {
/* default behavior is to delegate recording to parent if any */
resetPendingModifiers();
if (this.parent == null) return this; // ignore
if (this.fieldDeclaration.declarationSourceStart == addedfieldDeclaration.declarationSourceStart) {
if (this.fieldDeclaration.initialization != null) {
this.updateSourceEndIfNecessary(this.fieldDeclaration.initialization.sourceEnd);
} else {
this.updateSourceEndIfNecessary(this.fieldDeclaration.sourceEnd);
}
} else {
this.updateSourceEndIfNecessary(previousAvailableLineEnd(addedfieldDeclaration.declarationSourceStart - 1));
}
return this.parent.add(addedfieldDeclaration, bracketBalanceValue);
}
/*
* Record an expression statement if field is expecting an initialization expression,
* used for completion inside field initializers.
*/
@Override
public RecoveredElement add(Statement statement, int bracketBalanceValue) {
if (this.alreadyCompletedFieldInitialization || !(statement instanceof Expression)) {
return super.add(statement, bracketBalanceValue);
} else {
if (statement.sourceEnd > 0)
this.alreadyCompletedFieldInitialization = true;
// else we may still be inside the initialization, having parsed only a part of it yet
if (!(statement instanceof AllocationExpression) &&
this.fieldDeclaration.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
AllocationExpression alloc = new AllocationExpression();
alloc.arguments = new Expression[] {(Expression) statement};
this.fieldDeclaration.initialization = alloc;
} else {
this.fieldDeclaration.initialization = (Expression) statement;
this.fieldDeclaration.declarationSourceEnd = statement.sourceEnd;
this.fieldDeclaration.declarationEnd = statement.sourceEnd;
}
return this;
}
}
/*
* Record a type declaration if this field is expecting an initialization expression
* and the type is an anonymous type.
* Used for completion inside field initializers.
*/
@Override
public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) {
if (this.alreadyCompletedFieldInitialization
|| ((typeDeclaration.bits & ASTNode.IsAnonymousType) == 0)
|| (this.fieldDeclaration.declarationSourceEnd != 0 && typeDeclaration.sourceStart > this.fieldDeclaration.declarationSourceEnd)) {
return super.add(typeDeclaration, bracketBalanceValue);
} else {
// Prepare anonymous type list
if (this.anonymousTypes == null) {
this.anonymousTypes = new RecoveredType[5];
this.anonymousTypeCount = 0;
} else {
if (this.anonymousTypeCount == this.anonymousTypes.length) {
System.arraycopy(
this.anonymousTypes,
0,
(this.anonymousTypes = new RecoveredType[2 * this.anonymousTypeCount]),
0,
this.anonymousTypeCount);
}
}
// Store type declaration as an anonymous type
RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalanceValue);
this.anonymousTypes[this.anonymousTypeCount++] = element;
return element;
}
}
public void attach(RecoveredAnnotation[] annots, int annotCount, int mods, int modsSourceStart) {
if (annotCount > 0) {
Annotation[] existingAnnotations = this.fieldDeclaration.annotations;
if (existingAnnotations != null) {
this.annotations = new RecoveredAnnotation[annotCount];
this.annotationCount = 0;
next : for (int i = 0; i < annotCount; i++) {
for (int j = 0; j < existingAnnotations.length; j++) {
if (annots[i].annotation == existingAnnotations[j]) continue next;
}
this.annotations[this.annotationCount++] = annots[i];
}
} else {
this.annotations = annots;
this.annotationCount = annotCount;
}
}
if (mods != 0) {
this.modifiers = mods;
this.modifiersStart = modsSourceStart;
}
}
/*
* Answer the associated parsed structure
*/
@Override
public ASTNode parseTree(){
return this.fieldDeclaration;
}
/*
* Answer the very source end of the corresponding parse node
*/
@Override
public int sourceEnd(){
return this.fieldDeclaration.declarationSourceEnd;
}
@Override
public String toString(int tab){
StringBuffer buffer = new StringBuffer(tabString(tab));
buffer.append("Recovered field:\n"); //$NON-NLS-1$
this.fieldDeclaration.print(tab + 1, buffer);
if (this.annotations != null) {
for (int i = 0; i < this.annotationCount; i++) {
buffer.append("\n"); //$NON-NLS-1$
buffer.append(this.annotations[i].toString(tab + 1));
}
}
if (this.anonymousTypes != null) {
for (int i = 0; i < this.anonymousTypeCount; i++){
buffer.append("\n"); //$NON-NLS-1$
buffer.append(this.anonymousTypes[i].toString(tab + 1));
}
}
return buffer.toString();
}
public FieldDeclaration updatedFieldDeclaration(int depth, Set knownTypes){
/* update annotations */
if (this.modifiers != 0) {
this.fieldDeclaration.modifiers |= this.modifiers;
if (this.modifiersStart < this.fieldDeclaration.declarationSourceStart) {
this.fieldDeclaration.declarationSourceStart = this.modifiersStart;
}
}
/* update annotations */
if (this.annotationCount > 0){
int existingCount = this.fieldDeclaration.annotations == null ? 0 : this.fieldDeclaration.annotations.length;
Annotation[] annotationReferences = new Annotation[existingCount + this.annotationCount];
if (existingCount > 0){
System.arraycopy(this.fieldDeclaration.annotations, 0, annotationReferences, this.annotationCount, existingCount);
}
for (int i = 0; i < this.annotationCount; i++){
annotationReferences[i] = this.annotations[i].updatedAnnotationReference();
}
this.fieldDeclaration.annotations = annotationReferences;
int start = this.annotations[0].annotation.sourceStart;
if (start < this.fieldDeclaration.declarationSourceStart) {
this.fieldDeclaration.declarationSourceStart = start;
}
}
if (this.anonymousTypes != null) {
if(this.fieldDeclaration.initialization == null) {
ArrayInitializer recoveredInitializers = null;
int recoveredInitializersCount = 0;
if (this.anonymousTypeCount > 1) {
recoveredInitializers = new ArrayInitializer();
recoveredInitializers.expressions = new Expression[this.anonymousTypeCount];
}
for (int i = 0; i < this.anonymousTypeCount; i++){
RecoveredType recoveredType = this.anonymousTypes[i];
TypeDeclaration typeDeclaration = recoveredType.typeDeclaration;
if(typeDeclaration.declarationSourceEnd == 0) {
typeDeclaration.declarationSourceEnd = this.fieldDeclaration.declarationSourceEnd;
typeDeclaration.bodyEnd = this.fieldDeclaration.declarationSourceEnd;
}
if (recoveredType.preserveContent){
TypeDeclaration anonymousType = recoveredType.updatedTypeDeclaration(depth + 1, knownTypes);
if (anonymousType != null) {
if (this.anonymousTypeCount > 1) {
if (recoveredInitializersCount == 0) {
this.fieldDeclaration.initialization = recoveredInitializers;
}
recoveredInitializers.expressions[recoveredInitializersCount++] = anonymousType.allocation;
}
else {
this.fieldDeclaration.initialization = anonymousType.allocation;
}
int end = anonymousType.declarationSourceEnd;
if (end > this.fieldDeclaration.declarationSourceEnd) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=307337
this.fieldDeclaration.declarationSourceEnd = end;
this.fieldDeclaration.declarationEnd = end;
}
}
}
}
if (this.anonymousTypeCount > 0) {
this.fieldDeclaration.bits |= ASTNode.HasLocalType;
if (recoveredInitializers != null) {
recoveredInitializers.sourceStart = this.anonymousTypes[0].typeDeclaration.sourceStart;
recoveredInitializers.sourceEnd = this.anonymousTypes[this.anonymousTypeCount-1].
typeDeclaration.sourceEnd;
}
}
}
else if(this.fieldDeclaration.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
// fieldDeclaration is an enum constant
for (int i = 0; i < this.anonymousTypeCount; i++){
RecoveredType recoveredType = this.anonymousTypes[i];
TypeDeclaration typeDeclaration = recoveredType.typeDeclaration;
if(typeDeclaration.declarationSourceEnd == 0) {
typeDeclaration.declarationSourceEnd = this.fieldDeclaration.declarationSourceEnd;
typeDeclaration.bodyEnd = this.fieldDeclaration.declarationSourceEnd;
}
// if the enum is recovered then enum constants must be recovered too.
// depth is considered as the same as the depth of the enum
recoveredType.updatedTypeDeclaration(depth, knownTypes);
}
}
}
return this.fieldDeclaration;
}
/*
* A closing brace got consumed, might have closed the current element,
* in which case both the currentElement is exited.
*
* Fields have no associated braces, thus if matches, then update parent.
*/
@Override
public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
if (this.bracketBalance > 0){ // was an array initializer
this.bracketBalance--;
if (this.bracketBalance == 0) {
if(this.fieldDeclaration.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
updateSourceEndIfNecessary(braceEnd);
return this.parent;
} else {
if (this.fieldDeclaration.declarationSourceEnd > 0)
this.alreadyCompletedFieldInitialization = true;
}
}
return this;
} else if (this.bracketBalance == 0) {
this.alreadyCompletedFieldInitialization = true;
updateSourceEndIfNecessary(braceEnd - 1);
}
if (this.parent != null){
return this.parent.updateOnClosingBrace(braceStart, braceEnd);
}
return this;
}
/*
* An opening brace got consumed, might be the expected opening one of the current element,
* in which case the bodyStart is updated.
*/
@Override
public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){
if (this.fieldDeclaration.declarationSourceEnd == 0) {
if (this.fieldDeclaration.type instanceof ArrayTypeReference || this.fieldDeclaration.type instanceof ArrayQualifiedTypeReference) {
if (!this.alreadyCompletedFieldInitialization) {
this.bracketBalance++;
return null; // no update is necessary (array initializer)
}
} else { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=308980
// in case an initializer bracket is opened in a non-array field
// e.g. int field = {..
this.bracketBalance++;
return null; // no update is necessary (array initializer)
}
}
if (this.fieldDeclaration.declarationSourceEnd == 0
&& this.fieldDeclaration.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT){
this.bracketBalance++;
return null; // no update is necessary (enum constant)
}
// might be an array initializer
this.updateSourceEndIfNecessary(braceStart - 1, braceEnd - 1);
return this.parent.updateOnOpeningBrace(braceStart, braceEnd);
}
@Override
public void updateParseTree(){
updatedFieldDeclaration(0, new HashSet());
}
/*
* Update the declarationSourceEnd of the corresponding parse node
*/
@Override
public void updateSourceEndIfNecessary(int bodyStart, int bodyEnd){
if (this.fieldDeclaration.declarationSourceEnd == 0) {
this.fieldDeclaration.declarationSourceEnd = bodyEnd;
this.fieldDeclaration.declarationEnd = bodyEnd;
}
}
}