| /******************************************************************************* |
| * 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.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.jdt.internal.compiler.ast.Block; |
| import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ExportsStatement; |
| import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ImportReference; |
| import org.eclipse.jdt.internal.compiler.ast.Initializer; |
| import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| |
| public class RecoveredUnit extends RecoveredElement { |
| |
| public CompilationUnitDeclaration unitDeclaration; |
| |
| public RecoveredImport[] imports; |
| public int importCount; |
| public RecoveredModule module; |
| public RecoveredType[] types; |
| public int typeCount; |
| |
| int pendingModifiers; |
| int pendingModifersSourceStart = -1; |
| RecoveredAnnotation[] pendingAnnotations; |
| int pendingAnnotationCount; |
| |
| public RecoveredUnit(CompilationUnitDeclaration unitDeclaration, int bracketBalance, Parser parser){ |
| super(null, bracketBalance, parser); |
| this.unitDeclaration = unitDeclaration; |
| } |
| @Override |
| 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; |
| } |
| @Override |
| public void addModifier(int flag, int modifiersSourceStart) { |
| this.pendingModifiers |= flag; |
| |
| if (this.pendingModifersSourceStart < 0) { |
| this.pendingModifersSourceStart = modifiersSourceStart; |
| } |
| } |
| /* |
| * Record a method declaration: should be attached to last type |
| */ |
| @Override |
| public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalanceValue) { |
| |
| /* attach it to last type - if any */ |
| if (this.typeCount > 0){ |
| RecoveredType type = this.types[this.typeCount -1]; |
| int start = type.bodyEnd; |
| int end = type.typeDeclaration.bodyEnd; |
| type.bodyEnd = 0; // reset position |
| type.typeDeclaration.declarationSourceEnd = 0; // reset position |
| type.typeDeclaration.bodyEnd = 0; |
| |
| int kind = TypeDeclaration.kind(type.typeDeclaration.modifiers); |
| if(start > 0 && |
| start < end && |
| kind != TypeDeclaration.INTERFACE_DECL && |
| kind != TypeDeclaration.ANNOTATION_TYPE_DECL) { |
| // the } of the last type can be considered as the end of an initializer |
| Block block = new Block(0); |
| block.sourceStart = block.sourceEnd = end; |
| Initializer initializer = new Initializer(block, 0); |
| initializer.bodyStart = end; |
| initializer.bodyEnd = end; |
| initializer.declarationSourceStart = end; |
| initializer.declarationSourceEnd = end; |
| initializer.sourceStart = end; |
| initializer.sourceEnd = end; |
| type.add(initializer, bracketBalanceValue); |
| } |
| |
| resetPendingModifiers(); |
| |
| return type.add(methodDeclaration, bracketBalanceValue); |
| } |
| return this; // ignore |
| } |
| /* |
| * Record a field declaration: should be attached to last type |
| */ |
| @Override |
| public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) { |
| |
| /* attach it to last type - if any */ |
| if (this.typeCount > 0){ |
| RecoveredType type = this.types[this.typeCount -1]; |
| type.bodyEnd = 0; // reset position |
| type.typeDeclaration.declarationSourceEnd = 0; // reset position |
| type.typeDeclaration.bodyEnd = 0; |
| |
| resetPendingModifiers(); |
| |
| return type.add(fieldDeclaration, bracketBalanceValue); |
| } |
| return this; // ignore |
| } |
| public RecoveredElement add(ExportsStatement exportReference, int bracketBalanceValue) { |
| return this.module != null ? this.module.add(exportReference, bracketBalanceValue) : null; |
| } |
| |
| @Override |
| public RecoveredElement add(ImportReference importReference, int bracketBalanceValue) { |
| resetPendingModifiers(); |
| |
| if (this.imports == null) { |
| this.imports = new RecoveredImport[5]; |
| this.importCount = 0; |
| } else { |
| if (this.importCount == this.imports.length) { |
| System.arraycopy( |
| this.imports, |
| 0, |
| (this.imports = new RecoveredImport[2 * this.importCount]), |
| 0, |
| this.importCount); |
| } |
| } |
| RecoveredImport element = new RecoveredImport(importReference, this, bracketBalanceValue); |
| this.imports[this.importCount++] = element; |
| |
| /* if import not finished, then import becomes current */ |
| if (importReference.declarationSourceEnd == 0) return element; |
| return this; |
| } |
| @Override |
| public RecoveredElement add(ModuleDeclaration moduleDeclaration, int bracketBalanceValue){ |
| this.module = new RecoveredModule(moduleDeclaration, this, bracketBalanceValue); |
| return this.module; |
| } |
| @Override |
| public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) { |
| |
| if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0){ |
| if (this.typeCount > 0) { |
| // add it to the last type |
| RecoveredType lastType = this.types[this.typeCount-1]; |
| lastType.bodyEnd = 0; // reopen type |
| lastType.typeDeclaration.bodyEnd = 0; // reopen type |
| lastType.typeDeclaration.declarationSourceEnd = 0; // reopen type |
| lastType.bracketBalance++; // expect one closing brace |
| |
| resetPendingModifiers(); |
| |
| return lastType.add(typeDeclaration, bracketBalanceValue); |
| } |
| } |
| if (this.types == null) { |
| this.types = new RecoveredType[5]; |
| this.typeCount = 0; |
| } else { |
| if (this.typeCount == this.types.length) { |
| System.arraycopy( |
| this.types, |
| 0, |
| (this.types = new RecoveredType[2 * this.typeCount]), |
| 0, |
| this.typeCount); |
| } |
| } |
| RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalanceValue); |
| this.types[this.typeCount++] = element; |
| |
| if(this.pendingAnnotationCount > 0) { |
| element.attach( |
| this.pendingAnnotations, |
| this.pendingAnnotationCount, |
| this.pendingModifiers, |
| this.pendingModifersSourceStart); |
| } |
| resetPendingModifiers(); |
| |
| /* if type not finished, then type becomes current */ |
| if (typeDeclaration.declarationSourceEnd == 0) return element; |
| return this; |
| } |
| /* |
| * Answer the associated parsed structure |
| */ |
| @Override |
| public ASTNode parseTree(){ |
| return this.unitDeclaration; |
| } |
| @Override |
| public void resetPendingModifiers() { |
| this.pendingAnnotations = null; |
| this.pendingAnnotationCount = 0; |
| this.pendingModifiers = 0; |
| this.pendingModifersSourceStart = -1; |
| } |
| /* |
| * Answer the very source end of the corresponding parse node |
| */ |
| @Override |
| public int sourceEnd(){ |
| return this.unitDeclaration.sourceEnd; |
| } |
| @Override |
| public int getLastStart() { |
| int lastTypeStart = -1; |
| |
| if (this.typeCount > 0) { |
| TypeDeclaration lastType = this.types[this.typeCount - 1].typeDeclaration; |
| if (lastTypeStart < lastType.declarationSourceStart && lastType.declarationSourceStart != 0) { |
| lastTypeStart = lastType.declarationSourceStart; |
| } |
| } |
| return lastTypeStart; |
| } |
| @Override |
| public String toString(int tab) { |
| StringBuffer result = new StringBuffer(tabString(tab)); |
| result.append("Recovered unit: [\n"); //$NON-NLS-1$ |
| this.unitDeclaration.print(tab + 1, result); |
| result.append(tabString(tab + 1)); |
| result.append("]"); //$NON-NLS-1$ |
| if (this.imports != null) { |
| for (int i = 0; i < this.importCount; i++) { |
| result.append("\n"); //$NON-NLS-1$ |
| result.append(this.imports[i].toString(tab + 1)); |
| } |
| } |
| if (this.types != null) { |
| for (int i = 0; i < this.typeCount; i++) { |
| result.append("\n"); //$NON-NLS-1$ |
| result.append(this.types[i].toString(tab + 1)); |
| } |
| } |
| return result.toString(); |
| } |
| public CompilationUnitDeclaration updatedCompilationUnitDeclaration(){ |
| |
| /* update imports */ |
| if (this.importCount > 0){ |
| ImportReference[] importRefences = new ImportReference[this.importCount]; |
| for (int i = 0; i < this.importCount; i++){ |
| importRefences[i] = this.imports[i].updatedImportReference(); |
| } |
| this.unitDeclaration.imports = importRefences; |
| } |
| if (this.module != null) { |
| this.unitDeclaration.moduleDeclaration = this.module.updatedModuleDeclaration(); |
| } |
| /* update types */ |
| if (this.typeCount > 0){ |
| int existingCount = this.unitDeclaration.types == null ? 0 : this.unitDeclaration.types.length; |
| TypeDeclaration[] typeDeclarations = new TypeDeclaration[existingCount + this.typeCount]; |
| if (existingCount > 0){ |
| System.arraycopy(this.unitDeclaration.types, 0, typeDeclarations, 0, existingCount); |
| } |
| // may need to update the declarationSourceEnd of the last type |
| if (this.types[this.typeCount - 1].typeDeclaration.declarationSourceEnd == 0){ |
| this.types[this.typeCount - 1].typeDeclaration.declarationSourceEnd = this.unitDeclaration.sourceEnd; |
| this.types[this.typeCount - 1].typeDeclaration.bodyEnd = this.unitDeclaration.sourceEnd; |
| } |
| |
| Set<TypeDeclaration> knownTypes = new HashSet<>(); |
| int actualCount = existingCount; |
| for (int i = 0; i < this.typeCount; i++){ |
| TypeDeclaration typeDecl = this.types[i].updatedTypeDeclaration(0, knownTypes); |
| // filter out local types (12454) |
| if (typeDecl != null && (typeDecl.bits & ASTNode.IsLocalType) == 0){ |
| typeDeclarations[actualCount++] = typeDecl; |
| } |
| } |
| if (actualCount != this.typeCount){ |
| System.arraycopy( |
| typeDeclarations, |
| 0, |
| typeDeclarations = new TypeDeclaration[existingCount+actualCount], |
| 0, |
| existingCount+actualCount); |
| } |
| this.unitDeclaration.types = typeDeclarations; |
| } |
| return this.unitDeclaration; |
| } |
| @Override |
| public void updateParseTree(){ |
| updatedCompilationUnitDeclaration(); |
| } |
| /* |
| * Update the sourceEnd of the corresponding parse node |
| */ |
| @Override |
| public void updateSourceEndIfNecessary(int bodyStart, int bodyEnd){ |
| if (this.unitDeclaration.sourceEnd == 0) |
| this.unitDeclaration.sourceEnd = bodyEnd; |
| } |
| } |