/*******************************************************************************
* Copyright (c) 2006, 2008 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.core.dom;
import java.util.List;
import java.util.Vector;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.parser.RecoveryScanner;
import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToIntArray;
/**
* Internal AST visitor for propagating syntax errors.
*/
class ASTRecoveryPropagator extends DefaultASTVisitor {
private static final int NOTHING= -1;
HashtableOfObjectToIntArray endingTokens= new HashtableOfObjectToIntArray();
{
this.endingTokens.put(AnonymousClassDeclaration.class, new int[] { TerminalTokens.TokenNameRBRACE });
this.endingTokens.put(ArrayAccess.class, new int[] { TerminalTokens.TokenNameRBRACKET });
this.endingTokens.put(ArrayCreation.class, new int[] { NOTHING, TerminalTokens.TokenNameRBRACKET });
this.endingTokens.put(ArrayInitializer.class, new int[] { TerminalTokens.TokenNameRBRACE });
this.endingTokens.put(ArrayType.class, new int[] { TerminalTokens.TokenNameRBRACKET });
this.endingTokens.put(AssertStatement.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(Block.class, new int[] { TerminalTokens.TokenNameRBRACE });
this.endingTokens.put(BooleanLiteral.class, new int[] { TerminalTokens.TokenNamefalse, TerminalTokens.TokenNametrue });
this.endingTokens.put(BreakStatement.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(CharacterLiteral.class, new int[] { TerminalTokens.TokenNameCharacterLiteral });
this.endingTokens.put(ClassInstanceCreation.class, new int[] { TerminalTokens.TokenNameRBRACE, TerminalTokens.TokenNameRPAREN });
this.endingTokens.put(ConstructorInvocation.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(ContinueStatement.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(DoStatement.class, new int[] { TerminalTokens.TokenNameRPAREN });
this.endingTokens.put(EmptyStatement.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(ExpressionStatement.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(FieldDeclaration.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(ImportDeclaration.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(Initializer.class, new int[] { TerminalTokens.TokenNameRBRACE });
this.endingTokens.put(MethodDeclaration.class, new int[] { NOTHING, TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(MethodInvocation.class, new int[] { TerminalTokens.TokenNameRPAREN });
this.endingTokens.put(NullLiteral.class, new int[] { TerminalTokens.TokenNamenull });
this.endingTokens.put(NumberLiteral.class, new int[] { TerminalTokens.TokenNameIntegerLiteral, TerminalTokens.TokenNameLongLiteral, TerminalTokens.TokenNameFloatingPointLiteral,
TerminalTokens.TokenNameDoubleLiteral });
this.endingTokens.put(PackageDeclaration.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(ParenthesizedExpression.class, new int[] { TerminalTokens.TokenNameRPAREN });
this.endingTokens.put(PostfixExpression.class, new int[] { TerminalTokens.TokenNamePLUS_PLUS, TerminalTokens.TokenNameMINUS_MINUS });
this.endingTokens.put(PrimitiveType.class, new int[] { TerminalTokens.TokenNamebyte, TerminalTokens.TokenNameshort, TerminalTokens.TokenNamechar, TerminalTokens.TokenNameint,
TerminalTokens.TokenNamelong, TerminalTokens.TokenNamefloat, TerminalTokens.TokenNameboolean, TerminalTokens.TokenNamedouble, TerminalTokens.TokenNamevoid });
this.endingTokens.put(ReturnStatement.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(SimpleName.class, new int[] { TerminalTokens.TokenNameIdentifier });
this.endingTokens.put(SingleVariableDeclaration.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(StringLiteral.class, new int[] { TerminalTokens.TokenNameStringLiteral });
this.endingTokens.put(SuperConstructorInvocation.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(SuperMethodInvocation.class, new int[] { TerminalTokens.TokenNameRPAREN });
this.endingTokens.put(SwitchCase.class, new int[] { TerminalTokens.TokenNameCOLON });
this.endingTokens.put(SwitchStatement.class, new int[] { TerminalTokens.TokenNameRBRACE });
this.endingTokens.put(SynchronizedStatement.class, new int[] { TerminalTokens.TokenNameRBRACE });
this.endingTokens.put(ThisExpression.class, new int[] { TerminalTokens.TokenNamethis });
this.endingTokens.put(ThrowStatement.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
this.endingTokens.put(TypeDeclaration.class, new int[] { TerminalTokens.TokenNameRBRACE });
this.endingTokens.put(TypeLiteral.class, new int[] { TerminalTokens.TokenNameclass });
this.endingTokens.put(VariableDeclarationStatement.class, new int[] { TerminalTokens.TokenNameSEMICOLON });
}
private CategorizedProblem[] problems;
private boolean[] usedOrIrrelevantProblems;
private RecoveryScannerData data;
private int blockDepth= 0;
private int lastEnd;
private int[] insertedTokensKind;
private int[] insertedTokensPosition;
private boolean[] insertedTokensFlagged;
private boolean[] removedTokensFlagged;
private boolean[] replacedTokensFlagged;
private Vector stack= new Vector();
/**
* @noreference This method is not intended to be referenced by clients.
*/
ASTRecoveryPropagator(CategorizedProblem[] problems, RecoveryScannerData data) {
// visit Javadoc.tags() as well
this.problems= problems;
this.usedOrIrrelevantProblems= new boolean[problems.length];
this.data= data;
if (this.data != null) {
int length= 0;
for (int i= 0; i < data.insertedTokensPtr + 1; i++) {
length+= data.insertedTokens[i].length;
}
this.insertedTokensKind= new int[length];
this.insertedTokensPosition= new int[length];
this.insertedTokensFlagged= new boolean[length];
int tokenCount= 0;
for (int i= 0; i < data.insertedTokensPtr + 1; i++) {
for (int j= 0; j < data.insertedTokens[i].length; j++) {
this.insertedTokensKind[tokenCount]= data.insertedTokens[i][j];
this.insertedTokensPosition[tokenCount]= data.insertedTokensPosition[i];
tokenCount++;
}
}
if (data.removedTokensPtr != -1) {
this.removedTokensFlagged= new boolean[data.removedTokensPtr + 1];
}
if (data.replacedTokensPtr != -1) {
this.replacedTokensFlagged= new boolean[data.replacedTokensPtr + 1];
}
}
}
public void endVisit(Block node) {
this.blockDepth--;
if (this.blockDepth <= 0) {
flagNodeWithInsertedTokens();
}
super.endVisit(node);
}
public boolean visit(Block node) {
boolean visitChildren= super.visit(node);
this.blockDepth++;
return visitChildren;
}
protected boolean visitNode(ASTNode node) {
if (this.blockDepth > 0) {
int start= node.getStartPosition();
int end= start + node.getLength() - 1;
// continue to visit the node only if it contains tokens modifications
if (this.insertedTokensFlagged != null) {
for (int i= 0; i < this.insertedTokensFlagged.length; i++) {
if (this.insertedTokensPosition[i] >= start &&
this.insertedTokensPosition[i] <= end) {
return true;
}
}
}
if (this.removedTokensFlagged != null) {
for (int i= 0; i <= this.data.removedTokensPtr; i++) {
if (this.data.removedTokensStart[i] >= start &&
this.data.removedTokensEnd[i] <= end) {
return true;
}
}
}
if (this.replacedTokensFlagged != null) {
for (int i= 0; i <= this.data.replacedTokensPtr; i++) {
if (this.data.replacedTokensStart[i] >= start &&
this.data.replacedTokensEnd[i] <= end) {
return true;
}
}
}
return false;
}
return true;
}
protected void endVisitNode(ASTNode node) {
int start= node.getStartPosition();
int end= start + node.getLength() - 1;
// is inside diet part of the ast
if (this.blockDepth < 1) {
switch (node.getNodeType()) {
case ASTNode.ANNOTATION_TYPE_DECLARATION:
case ASTNode.COMPILATION_UNIT:
case ASTNode.ENUM_DECLARATION:
case ASTNode.FIELD_DECLARATION:
case ASTNode.IMPORT_DECLARATION:
case ASTNode.INITIALIZER:
case ASTNode.METHOD_DECLARATION:
case ASTNode.PACKAGE_DECLARATION:
case ASTNode.TYPE_DECLARATION:
case ASTNode.MARKER_ANNOTATION:
case ASTNode.NORMAL_ANNOTATION:
case ASTNode.SINGLE_MEMBER_ANNOTATION:
case ASTNode.BLOCK:
if (markIncludedProblems(start, end)) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
}
break;
}
} else {
markIncludedProblems(start, end);
if (this.insertedTokensFlagged != null) {
if (this.lastEnd != end) {
flagNodeWithInsertedTokens();
}
this.stack.add(node);
}
if (this.removedTokensFlagged != null) {
for (int i= 0; i <= this.data.removedTokensPtr; i++) {
if (!this.removedTokensFlagged[i] &&
this.data.removedTokensStart[i] >= start &&
this.data.removedTokensEnd[i] <= end) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
this.removedTokensFlagged[i]= true;
}
}
}
if (this.replacedTokensFlagged != null) {
for (int i= 0; i <= this.data.replacedTokensPtr; i++) {
if (!this.replacedTokensFlagged[i] &&
this.data.replacedTokensStart[i] >= start &&
this.data.replacedTokensEnd[i] <= end) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
this.replacedTokensFlagged[i]= true;
}
}
}
}
this.lastEnd= end;
}
private void flagNodeWithInsertedTokens() {
if (this.insertedTokensKind != null && this.insertedTokensKind.length > 0) {
int s= this.stack.size();
for (int i= s - 1; i > -1; i--) {
flagNodesWithInsertedTokensAtEnd((ASTNode)this.stack.get(i));
}
for (int i= 0; i < s; i++) {
flagNodesWithInsertedTokensInside((ASTNode)this.stack.get(i));
}
this.stack= new Vector();
}
}
private boolean flagNodesWithInsertedTokensAtEnd(ASTNode node) {
int[] expectedEndingToken= this.endingTokens.get(node.getClass());
if (expectedEndingToken != null) {
int start= node.getStartPosition();
int end= start + node.getLength() - 1;
boolean flagParent= false;
done: for (int i= this.insertedTokensKind.length - 1; i > -1; i--) {
if (!this.insertedTokensFlagged[i] &&
this.insertedTokensPosition[i] == end) {
this.insertedTokensFlagged[i]= true;
for (int j= 0; j < expectedEndingToken.length; j++) {
if (expectedEndingToken[j] == this.insertedTokensKind[i]) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
break done;
}
}
flagParent= true;
}
}
if (flagParent) {
ASTNode parent= node.getParent();
while (parent != null) {
parent.setFlags(node.getFlags() | ASTNode.RECOVERED);
if ((parent.getStartPosition() + parent.getLength() - 1) != end) {
parent= null;
} else {
parent= parent.getParent();
}
}
}
}
return true;
}
private boolean flagNodesWithInsertedTokensInside(ASTNode node) {
int start= node.getStartPosition();
int end= start + node.getLength() - 1;
for (int i= 0; i < this.insertedTokensKind.length; i++) {
if (!this.insertedTokensFlagged[i] &&
start <= this.insertedTokensPosition[i] &&
this.insertedTokensPosition[i] < end) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
this.insertedTokensFlagged[i]= true;
}
}
return true;
}
private boolean markIncludedProblems(int start, int end) {
boolean foundProblems= false;
next: for (int i= 0, max= this.problems.length; i < max; i++) {
CategorizedProblem problem= this.problems[i];
if (this.usedOrIrrelevantProblems[i])
continue next;
switch (problem.getID()) {
case IProblem.ParsingErrorOnKeywordNoSuggestion:
case IProblem.ParsingErrorOnKeyword:
case IProblem.ParsingError:
case IProblem.ParsingErrorNoSuggestion:
case IProblem.ParsingErrorInsertTokenBefore:
case IProblem.ParsingErrorInsertTokenAfter:
case IProblem.ParsingErrorDeleteToken:
case IProblem.ParsingErrorDeleteTokens:
case IProblem.ParsingErrorMergeTokens:
case IProblem.ParsingErrorInvalidToken:
case IProblem.ParsingErrorMisplacedConstruct:
case IProblem.ParsingErrorReplaceTokens:
case IProblem.ParsingErrorNoSuggestionForTokens:
case IProblem.ParsingErrorUnexpectedEOF:
case IProblem.ParsingErrorInsertToComplete:
case IProblem.ParsingErrorInsertToCompleteScope:
case IProblem.ParsingErrorInsertToCompletePhrase:
case IProblem.EndOfSource:
case IProblem.InvalidHexa:
case IProblem.InvalidOctal:
case IProblem.InvalidCharacterConstant:
case IProblem.InvalidEscape:
case IProblem.InvalidInput:
case IProblem.InvalidUnicodeEscape:
case IProblem.InvalidFloat:
case IProblem.NullSourceString:
case IProblem.UnterminatedString:
case IProblem.UnterminatedComment:
case IProblem.InvalidDigit:
break;
default:
this.usedOrIrrelevantProblems[i]= true;
continue next;
}
int problemStart= problem.getSourceStart();
int problemEnd= problem.getSourceEnd();
if ((start <= problemStart) && (problemStart <= end) ||
(start <= problemEnd) && (problemEnd <= end)) {
this.usedOrIrrelevantProblems[i]= true;
foundProblems= true;
}
}
return foundProblems;
}
public void endVisit(ExpressionStatement node) {
endVisitNode(node);
if ((node.getFlags() & ASTNode.RECOVERED) == 0)
return;
Expression expression= node.getExpression();
if (expression.getNodeType() == ASTNode.ASSIGNMENT) {
Assignment assignment= (Assignment)expression;
Expression rightHandSide= assignment.getRightHandSide();
if (rightHandSide.getNodeType() == ASTNode.SIMPLE_NAME) {
SimpleName simpleName= (SimpleName)rightHandSide;
if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) {
Expression expression2= assignment.getLeftHandSide();
// unparent the expression to add it in the expression stateemnt
expression2.setParent(null, null);
expression2.setFlags(expression2.getFlags() | ASTNode.RECOVERED);
node.setExpression(expression2);
}
}
}
}
public void endVisit(ForStatement node) {
endVisitNode(node);
List initializers= node.initializers();
if (initializers.size() == 1) {
Expression expression= (Expression)initializers.get(0);
if (expression.getNodeType() == ASTNode.VARIABLE_DECLARATION_EXPRESSION) {
VariableDeclarationExpression variableDeclarationExpression= (VariableDeclarationExpression)expression;
List fragments= variableDeclarationExpression.fragments();
for (int i= 0, max= fragments.size(); i < max; i++) {
VariableDeclarationFragment fragment= (VariableDeclarationFragment)fragments.get(i);
SimpleName simpleName= fragment.getName();
if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) {
fragments.remove(fragment);
variableDeclarationExpression.setFlags(variableDeclarationExpression.getFlags() | ASTNode.RECOVERED);
}
}
}
}
}
public void endVisit(VariableDeclarationStatement node) {
endVisitNode(node);
List fragments= node.fragments();
for (int i= 0, max= fragments.size(); i < max; i++) {
VariableDeclarationFragment fragment= (VariableDeclarationFragment)fragments.get(i);
Expression expression= fragment.getInitializer();
if (expression == null)
continue;
if ((expression.getFlags() & ASTNode.RECOVERED) == 0)
continue;
if (expression.getNodeType() == ASTNode.SIMPLE_NAME) {
SimpleName simpleName= (SimpleName)expression;
if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) {
fragment.setInitializer(null);
fragment.setFlags(fragment.getFlags() | ASTNode.RECOVERED);
}
}
}
}
public void endVisit(NormalAnnotation node) {
endVisitNode(node);
// is inside diet part of the ast
if (this.blockDepth < 1) {
List values= node.values();
int size= values.size();
if (size > 0) {
MemberValuePair lastMemberValuePair= (MemberValuePair)values.get(size - 1);
int annotationEnd= node.getStartPosition() + node.getLength();
int lastMemberValuePairEnd= lastMemberValuePair.getStartPosition() + lastMemberValuePair.getLength();
if (annotationEnd == lastMemberValuePairEnd) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
}
}
}
}
public void endVisit(SingleMemberAnnotation node) {
endVisitNode(node);
// is inside diet part of the ast
if (this.blockDepth < 1) {
Expression value= node.getValue();
int annotationEnd= node.getStartPosition() + node.getLength();
int valueEnd= value.getStartPosition() + value.getLength();
if (annotationEnd == valueEnd) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
}
}
}
}