| /******************************************************************************* |
| * Copyright (c) 2000, 2016 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.Set; |
| |
| import org.eclipse.jdt.core.compiler.*; |
| import org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.jdt.internal.compiler.ast.Block; |
| import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Initializer; |
| 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 RecoveredInitializer extends RecoveredField implements TerminalTokens { |
| |
| public RecoveredType[] localTypes; |
| public int localTypeCount; |
| |
| public RecoveredBlock initializerBody; |
| |
| int pendingModifiers; |
| int pendingModifersSourceStart = -1; |
| RecoveredAnnotation[] pendingAnnotations; |
| int pendingAnnotationCount; |
| |
| public RecoveredInitializer(FieldDeclaration fieldDeclaration, RecoveredElement parent, int bracketBalance){ |
| this(fieldDeclaration, parent, bracketBalance, null); |
| } |
| public RecoveredInitializer(FieldDeclaration fieldDeclaration, RecoveredElement parent, int bracketBalance, Parser parser){ |
| super(fieldDeclaration, parent, bracketBalance, parser); |
| this.foundOpeningBrace = true; |
| } |
| /* |
| * Record a nested block declaration |
| */ |
| public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) { |
| |
| /* default behavior is to delegate recording to parent if any, |
| do not consider elements passed the known end (if set) |
| it must be belonging to an enclosing element |
| */ |
| if (this.fieldDeclaration.declarationSourceEnd > 0 |
| && nestedBlockDeclaration.sourceStart > this.fieldDeclaration.declarationSourceEnd){ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| return this.parent.add(nestedBlockDeclaration, bracketBalanceValue); |
| } |
| /* consider that if the opening brace was not found, it is there */ |
| if (!this.foundOpeningBrace){ |
| this.foundOpeningBrace = true; |
| this.bracketBalance++; |
| } |
| if (this.initializerBody == null) { |
| return this.initializerBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue); |
| } |
| if (nestedBlockDeclaration.sourceEnd == 0) return this.initializerBody; |
| return this.initializerBody.add(nestedBlockDeclaration, bracketBalanceValue, true); |
| } |
| /* |
| * Record a field declaration (act like inside method body) |
| */ |
| public RecoveredElement add(FieldDeclaration newFieldDeclaration, int bracketBalanceValue) { |
| resetPendingModifiers(); |
| |
| /* local variables inside initializer can only be final and non void */ |
| char[][] fieldTypeName; |
| if ((newFieldDeclaration.modifiers & ~ClassFileConstants.AccFinal) != 0 /* local var can only be final */ |
| || (newFieldDeclaration.type == null) // initializer |
| || ((fieldTypeName = newFieldDeclaration.type.getTypeName()).length == 1 // non void |
| && CharOperation.equals(fieldTypeName[0], TypeBinding.VOID.sourceName()))){ |
| if (this.parent == null) return this; // ignore |
| this.updateSourceEndIfNecessary(previousAvailableLineEnd(newFieldDeclaration.declarationSourceStart - 1)); |
| return this.parent.add(newFieldDeclaration, bracketBalanceValue); |
| } |
| |
| /* default behavior is to delegate recording to parent if any, |
| do not consider elements passed the known end (if set) |
| it must be belonging to an enclosing element |
| */ |
| if (this.fieldDeclaration.declarationSourceEnd > 0 |
| && newFieldDeclaration.declarationSourceStart > this.fieldDeclaration.declarationSourceEnd){ |
| if (this.parent == null) return this; // ignore |
| return this.parent.add(newFieldDeclaration, bracketBalanceValue); |
| } |
| // still inside initializer, treat as local variable |
| return this; // ignore |
| } |
| /* |
| * Record a local declaration - regular method should have been created a block body |
| */ |
| public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) { |
| |
| /* do not consider a type starting passed the type end (if set) |
| it must be belonging to an enclosing type */ |
| if (this.fieldDeclaration.declarationSourceEnd != 0 |
| && localDeclaration.declarationSourceStart > this.fieldDeclaration.declarationSourceEnd){ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| return this.parent.add(localDeclaration, bracketBalanceValue); |
| } |
| /* method body should have been created */ |
| if (this.initializerBody == null) { |
| Block block = new Block(0); |
| block.sourceStart = ((Initializer)this.fieldDeclaration).sourceStart; |
| RecoveredElement element = this.add(block, 1); |
| if (this.bracketBalance > 0){ |
| for (int i = 0; i < this.bracketBalance - 1; i++){ |
| element = element.add(new Block(0), 1); |
| } |
| this.bracketBalance = 1; |
| } |
| return element.add(localDeclaration, bracketBalanceValue); |
| } |
| this.initializerBody.attachPendingModifiers( |
| this.pendingAnnotations, |
| this.pendingAnnotationCount, |
| this.pendingModifiers, |
| this.pendingModifersSourceStart); |
| resetPendingModifiers(); |
| |
| return this.initializerBody.add(localDeclaration, bracketBalanceValue, true); |
| } |
| /* |
| * Record a statement - regular method should have been created a block body |
| */ |
| public RecoveredElement add(Statement statement, int bracketBalanceValue) { |
| |
| /* do not consider a statement starting passed the initializer end (if set) |
| it must be belonging to an enclosing type */ |
| if (this.fieldDeclaration.declarationSourceEnd != 0 |
| && statement.sourceStart > this.fieldDeclaration.declarationSourceEnd){ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| return this.parent.add(statement, bracketBalanceValue); |
| } |
| /* initializer body should have been created */ |
| Block block = new Block(0); |
| block.sourceStart = ((Initializer)this.fieldDeclaration).sourceStart; |
| RecoveredElement element = this.add(block, 1); |
| |
| if (this.initializerBody != null) { |
| this.initializerBody.attachPendingModifiers( |
| this.pendingAnnotations, |
| this.pendingAnnotationCount, |
| this.pendingModifiers, |
| this.pendingModifersSourceStart); |
| } |
| resetPendingModifiers(); |
| |
| return element.add(statement, bracketBalanceValue); |
| } |
| public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) { |
| |
| /* do not consider a type starting passed the type end (if set) |
| it must be belonging to an enclosing type */ |
| if (this.fieldDeclaration.declarationSourceEnd != 0 |
| && typeDeclaration.declarationSourceStart > this.fieldDeclaration.declarationSourceEnd){ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| return this.parent.add(typeDeclaration, bracketBalanceValue); |
| } |
| if ((typeDeclaration.bits & ASTNode.IsLocalType) != 0 || parser().methodRecoveryActivated || parser().statementRecoveryActivated){ |
| /* method body should have been created */ |
| Block block = new Block(0); |
| block.sourceStart = ((Initializer)this.fieldDeclaration).sourceStart; |
| RecoveredElement element = this.add(block, 1); |
| if (this.initializerBody != null) { |
| this.initializerBody.attachPendingModifiers( |
| this.pendingAnnotations, |
| this.pendingAnnotationCount, |
| this.pendingModifiers, |
| this.pendingModifersSourceStart); |
| } |
| resetPendingModifiers(); |
| return element.add(typeDeclaration, bracketBalanceValue); |
| } |
| if (this.localTypes == null) { |
| this.localTypes = new RecoveredType[5]; |
| this.localTypeCount = 0; |
| } else { |
| if (this.localTypeCount == this.localTypes.length) { |
| System.arraycopy( |
| this.localTypes, |
| 0, |
| (this.localTypes = new RecoveredType[2 * this.localTypeCount]), |
| 0, |
| this.localTypeCount); |
| } |
| } |
| RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalanceValue); |
| this.localTypes[this.localTypeCount++] = element; |
| |
| if(this.pendingAnnotationCount > 0) { |
| element.attach( |
| this.pendingAnnotations, |
| this.pendingAnnotationCount, |
| this.pendingModifiers, |
| this.pendingModifersSourceStart); |
| } |
| resetPendingModifiers(); |
| |
| /* consider that if the opening brace was not found, it is there */ |
| if (!this.foundOpeningBrace){ |
| this.foundOpeningBrace = true; |
| this.bracketBalance++; |
| } |
| return element; |
| } |
| 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; |
| } |
| } |
| 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 initializer:\n"); //$NON-NLS-1$ |
| this.fieldDeclaration.print(tab + 1, result); |
| if (this.annotations != null) { |
| for (int i = 0; i < this.annotationCount; i++) { |
| result.append("\n"); //$NON-NLS-1$ |
| result.append(this.annotations[i].toString(tab + 1)); |
| } |
| } |
| if (this.initializerBody != null) { |
| result.append("\n"); //$NON-NLS-1$ |
| result.append(this.initializerBody.toString(tab + 1)); |
| } |
| return result.toString(); |
| } |
| public FieldDeclaration updatedFieldDeclaration(int depth, Set<TypeDeclaration> knownTypes){ |
| |
| if (this.initializerBody != null){ |
| Block block = this.initializerBody.updatedBlock(depth, knownTypes); |
| if (block != null){ |
| Initializer initializer = (Initializer) this.fieldDeclaration; |
| initializer.block = block; |
| |
| if (initializer.declarationSourceEnd == 0) { |
| initializer.declarationSourceEnd = block.sourceEnd; |
| initializer.bodyEnd = block.sourceEnd; |
| } |
| } |
| if (this.localTypeCount > 0) this.fieldDeclaration.bits |= ASTNode.HasLocalType; |
| |
| } |
| if (this.fieldDeclaration.sourceEnd == 0){ |
| this.fieldDeclaration.sourceEnd = this.fieldDeclaration.declarationSourceEnd; |
| } |
| return this.fieldDeclaration; |
| } |
| /* |
| * 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); |
| 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){ |
| this.bracketBalance++; |
| return this; // request to restart |
| } |
| /* |
| * Update the declarationSourceEnd of the corresponding parse node |
| */ |
| public void updateSourceEndIfNecessary(int braceStart, int braceEnd){ |
| if (this.fieldDeclaration.declarationSourceEnd == 0) { |
| Initializer initializer = (Initializer)this.fieldDeclaration; |
| if(parser().rBraceSuccessorStart >= braceEnd) { |
| if (initializer.bodyStart < parser().rBraceEnd) { |
| initializer.declarationSourceEnd = parser().rBraceEnd; |
| } else { |
| initializer.declarationSourceEnd = initializer.bodyStart; |
| } |
| if (initializer.bodyStart < parser().rBraceStart) { |
| initializer.bodyEnd = parser().rBraceStart; |
| } else { |
| initializer.bodyEnd = initializer.bodyStart; |
| } |
| } else { |
| if (braceEnd < initializer.declarationSourceStart) { |
| initializer.declarationSourceEnd = initializer.declarationSourceStart; |
| initializer.bodyEnd = initializer.declarationSourceEnd; |
| } else { |
| initializer.declarationSourceEnd = braceEnd; |
| initializer.bodyEnd = braceStart - 1; |
| } |
| if (initializer.bodyStart > initializer.declarationSourceEnd) { |
| initializer.bodyStart = initializer.declarationSourceEnd; |
| if(initializer.block != null) { |
| initializer.block.sourceStart = initializer.declarationSourceStart; |
| } |
| } |
| } |
| if(initializer.block != null) { |
| initializer.block.sourceEnd = initializer.declarationSourceEnd; |
| } |
| } |
| } |
| } |