| /******************************************************************************* |
| * Copyright (c) 2000, 2012 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; |
| |
| /** |
| * Internal structure for parsing recovery |
| */ |
| import org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Block; |
| import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ImportReference; |
| 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.util.Util; |
| |
| public class RecoveredElement { |
| |
| public RecoveredElement parent; |
| public int bracketBalance; |
| public boolean foundOpeningBrace; |
| protected Parser recoveringParser; |
| |
| // There is no RecoveredLambdaElement, we just keep track of entry and exit of lambdas via a counter. This allows to prevent certain incorrect mutations of current element. |
| // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=430667. |
| public int lambdaNestLevel; |
| |
| public RecoveredElement(RecoveredElement parent, int bracketBalance){ |
| this(parent, bracketBalance, null); |
| } |
| public RecoveredElement(RecoveredElement parent, int bracketBalance, Parser parser){ |
| this.parent = parent; |
| this.bracketBalance = bracketBalance; |
| this.recoveringParser = parser; |
| } |
| public RecoveredElement addAnnotationName(int identifierPtr, int identifierLengthPtr, int annotationStart, int bracketBalanceValue) { |
| /* default behavior is to delegate recording to parent if any */ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| this.updateSourceEndIfNecessary(previousAvailableLineEnd(annotationStart - 1)); |
| return this.parent.addAnnotationName(identifierPtr, identifierLengthPtr, annotationStart, bracketBalanceValue); |
| } |
| /* |
| * Record a method declaration |
| */ |
| public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalanceValue) { |
| |
| /* default behavior is to delegate recording to parent if any */ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| this.updateSourceEndIfNecessary(previousAvailableLineEnd(methodDeclaration.declarationSourceStart - 1)); |
| return this.parent.add(methodDeclaration, bracketBalanceValue); |
| } |
| /* |
| * Record a nested block declaration |
| */ |
| public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) { |
| |
| /* default behavior is to delegate recording to parent if any */ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| this.updateSourceEndIfNecessary(previousAvailableLineEnd(nestedBlockDeclaration.sourceStart - 1)); |
| return this.parent.add(nestedBlockDeclaration, bracketBalanceValue); |
| } |
| /* |
| * Record a field declaration |
| */ |
| public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) { |
| |
| /* default behavior is to delegate recording to parent if any */ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| this.updateSourceEndIfNecessary(previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1)); |
| return this.parent.add(fieldDeclaration, bracketBalanceValue); |
| } |
| /* |
| * Record an import reference |
| */ |
| public RecoveredElement add(ImportReference importReference, int bracketBalanceValue){ |
| |
| /* default behavior is to delegate recording to parent if any */ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| this.updateSourceEndIfNecessary(previousAvailableLineEnd(importReference.declarationSourceStart - 1)); |
| return this.parent.add(importReference, bracketBalanceValue); |
| } |
| /* |
| * Record a local declaration |
| */ |
| public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) { |
| |
| /* default behavior is to delegate recording to parent if any */ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| this.updateSourceEndIfNecessary(previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1)); |
| return this.parent.add(localDeclaration, bracketBalanceValue); |
| } |
| /* |
| * Record a statement |
| */ |
| public RecoveredElement add(Statement statement, int bracketBalanceValue) { |
| |
| /* default behavior is to delegate recording to parent if any */ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| if (this instanceof RecoveredType) { |
| TypeDeclaration typeDeclaration = ((RecoveredType) this).typeDeclaration; |
| if (typeDeclaration != null && (typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=291040, new X(<SelectOnMessageSend:zoo()>) { ??? |
| if (statement.sourceStart > typeDeclaration.sourceStart && statement.sourceEnd < typeDeclaration.sourceEnd) { |
| return this; |
| } |
| } |
| } |
| this.updateSourceEndIfNecessary(previousAvailableLineEnd(statement.sourceStart - 1)); |
| return this.parent.add(statement, bracketBalanceValue); |
| } |
| /* |
| * Record a type declaration |
| */ |
| public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue){ |
| |
| /* default behavior is to delegate recording to parent if any */ |
| resetPendingModifiers(); |
| if (this.parent == null) return this; // ignore |
| this.updateSourceEndIfNecessary(previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1)); |
| return this.parent.add(typeDeclaration, bracketBalanceValue); |
| } |
| protected void addBlockStatement(RecoveredBlock recoveredBlock) { |
| Block block = recoveredBlock.blockDeclaration; |
| if(block.statements != null) { |
| Statement[] statements = block.statements; |
| for (int i = 0; i < statements.length; i++) { |
| recoveredBlock.add(statements[i], 0); |
| } |
| } |
| } |
| public void addModifier(int flag, int modifiersSourceStart) { |
| // default implementation: do nothing |
| } |
| /* |
| * Answer the depth of this element, considering the parent link. |
| */ |
| public int depth(){ |
| int depth = 0; |
| RecoveredElement current = this; |
| while ((current = current.parent) != null) depth++; |
| return depth; |
| } |
| /* |
| * Answer the enclosing method node, or null if none |
| */ |
| public RecoveredInitializer enclosingInitializer(){ |
| RecoveredElement current = this; |
| while (current != null){ |
| if (current instanceof RecoveredInitializer){ |
| return (RecoveredInitializer) current; |
| } |
| current = current.parent; |
| } |
| return null; |
| } |
| /* |
| * Answer the enclosing method node, or null if none |
| */ |
| public RecoveredMethod enclosingMethod(){ |
| RecoveredElement current = this; |
| while (current != null){ |
| if (current instanceof RecoveredMethod){ |
| return (RecoveredMethod) current; |
| } |
| current = current.parent; |
| } |
| return null; |
| } |
| /* |
| * Answer the enclosing type node, or null if none |
| */ |
| public RecoveredType enclosingType(){ |
| RecoveredElement current = this; |
| while (current != null){ |
| if (current instanceof RecoveredType){ |
| return (RecoveredType) current; |
| } |
| current = current.parent; |
| } |
| return null; |
| } |
| /* |
| * Answer the closest specified parser |
| */ |
| public Parser parser(){ |
| RecoveredElement current = this; |
| while (current != null){ |
| if (current.recoveringParser != null){ |
| return current.recoveringParser; |
| } |
| current = current.parent; |
| } |
| return null; |
| } |
| /* |
| * Answer the associated parsed structure |
| */ |
| public ASTNode parseTree(){ |
| return null; |
| } |
| public void resetPendingModifiers() { |
| // default implementation: do nothing |
| // recovered elements which have pending modifiers must override this method |
| } |
| /* |
| * Iterate the enclosing blocks and tag them so as to preserve their content |
| */ |
| public void preserveEnclosingBlocks(){ |
| RecoveredElement current = this; |
| while (current != null){ |
| if (current instanceof RecoveredBlock){ |
| ((RecoveredBlock)current).preserveContent = true; |
| } |
| if (current instanceof RecoveredType){ // for anonymous types |
| ((RecoveredType)current).preserveContent = true; |
| } |
| current = current.parent; |
| } |
| } |
| /* |
| * Answer the position of the previous line end if |
| * there is nothing but spaces in between it and the |
| * line end. Used to trim spaces on unclosed elements. |
| */ |
| public int previousAvailableLineEnd(int position){ |
| |
| Parser parser = parser(); |
| if (parser == null) return position; |
| |
| Scanner scanner = parser.scanner; |
| if (scanner.lineEnds == null) return position; |
| |
| int index = Util.getLineNumber(position, scanner.lineEnds, 0, scanner.linePtr); |
| if (index < 2) return position; |
| int previousLineEnd = scanner.lineEnds[index-2]; |
| |
| char[] source = scanner.source; |
| for (int i = previousLineEnd+1; i < position; i++){ |
| if (!(source[i] == ' ' || source[i] == '\t')) return position; |
| } |
| return previousLineEnd; |
| } |
| /* |
| * Answer the very source end of the corresponding parse node |
| */ |
| public int sourceEnd(){ |
| return 0; |
| } |
| protected String tabString(int tab) { |
| StringBuffer result = new StringBuffer(); |
| for (int i = tab; i > 0; i--) { |
| result.append(" "); //$NON-NLS-1$ |
| } |
| return result.toString(); |
| } |
| /* |
| * Answer the top node |
| */ |
| public RecoveredElement topElement(){ |
| RecoveredElement current = this; |
| while (current.parent != null){ |
| current = current.parent; |
| } |
| return current; |
| } |
| public String toString() { |
| return toString(0); |
| } |
| public String toString(int tab) { |
| return super.toString(); |
| } |
| /* |
| * Answer the enclosing type node, or null if none |
| */ |
| public RecoveredType type(){ |
| RecoveredElement current = this; |
| while (current != null){ |
| if (current instanceof RecoveredType){ |
| return (RecoveredType) current; |
| } |
| current = current.parent; |
| } |
| return null; |
| } |
| /* |
| * Update the bodyStart of the corresponding parse node |
| */ |
| public void updateBodyStart(int bodyStart){ |
| this.foundOpeningBrace = true; |
| } |
| /* |
| * Update the corresponding parse node from parser state which |
| * is about to disappear because of restarting recovery |
| */ |
| public void updateFromParserState(){ |
| // default implementation: do nothing |
| } |
| /* |
| * 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 braceEnd){return null;}*/ |
| public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){ |
| |
| if (this.bracketBalance++ == 0){ |
| updateBodyStart(braceEnd + 1); |
| return this; |
| } |
| return null; // no update is necessary |
| } |
| /* |
| * Final update the corresponding parse node |
| */ |
| public void updateParseTree(){ |
| // default implementation: do nothing |
| } |
| /* |
| * Update the declarationSourceEnd of the corresponding parse node |
| */ |
| public void updateSourceEndIfNecessary(int braceStart, int braceEnd){ |
| // default implementation: do nothing |
| } |
| public void updateSourceEndIfNecessary(int sourceEnd){ |
| this.updateSourceEndIfNecessary(sourceEnd + 1, sourceEnd); |
| } |
| } |