blob: f889f02883c3a4a80326e74b5a0c17031f69c4dd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2017 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.tests.dom;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.WildcardType;
import org.eclipse.jdt.core.tests.util.Util;
import org.eclipse.jdt.internal.core.dom.NaiveASTFlattener;
/**
* Serialize the ast and decorate this ast with markers.
* Compute also extra information about marked nodes.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class MarkedASTFlattener extends NaiveASTFlattener {
public static class DefaultMarkedNodeLabelProvider extends MarkedNodeLabelProvider implements DefaultMarkedNodeLabelProviderOptions {
private int options;
public DefaultMarkedNodeLabelProvider(int options) {
this.options = options;
}
private void appendBinding(ASTNode node, StringBuffer buffer) {
buffer.append('[');
try {
IBinding binding = resolveBinding(node);
if (binding != null) {
boolean first = true;
if ((this.options & BINDING_KIND) != 0) {
if (!first) buffer.append(',');
first = false;
appendBindingKind(binding, buffer);
}
if ((this.options & BINDING_KEY) != 0) {
if (!first) buffer.append(',');
first = false;
appendBindingKey(binding, buffer);
}
if ((this.options & BINDING_FLAGS) != 0) {
if (!first) buffer.append(',');
first = false;
appendBindingFlags(binding, buffer);
}
} else {
buffer.append("null");
}
} catch (IllegalArgumentException e) {
buffer.append("N/A");
}
buffer.append(']');
}
private void appendBindingFlags(IBinding binding, StringBuffer buffer) {
boolean firstFlag = true;
if (binding.isDeprecated()) {
if (!firstFlag) buffer.append('|');
firstFlag = false;
buffer.append("DEPRECATED");
}
if (binding.isSynthetic()) {
if (!firstFlag) buffer.append('|');
firstFlag = false;
buffer.append("SYNTHETIC");
}
if (binding.isRecovered()) {
if (!firstFlag) buffer.append('|');
firstFlag = false;
buffer.append("RECOVERED");
}
}
private void appendBindingKey(IBinding binding, StringBuffer buffer) {
buffer.append(binding.getKey());
}
private void appendBindingKind(IBinding binding, StringBuffer buffer) {
switch (binding.getKind()) {
case IBinding.ANNOTATION:
buffer.append("ANNOTATION");break;
case IBinding.MEMBER_VALUE_PAIR:
buffer.append("MEMBER_VALUE_PAIR");break;
case IBinding.METHOD:
buffer.append("METHOD");break;
case IBinding.PACKAGE:
buffer.append("PACKAGE");break;
case IBinding.TYPE:
buffer.append("TYPE");break;
case IBinding.VARIABLE:
buffer.append("VARIABLE");break;
default:
buffer.append("UNKNOWN");break;
}
}
private void appendFlags(ASTNode node, StringBuffer buffer) {
boolean firstFlag = true;
int flags = node.getFlags();
if ((flags & ASTNode.MALFORMED) != 0) {
if (!firstFlag) buffer.append('|');
firstFlag = false;
buffer.append("MALFORMED");
}
if ((flags & ASTNode.PROTECT) != 0) {
if (!firstFlag) buffer.append('|');
firstFlag = false;
buffer.append("PROTECT");
}
if ((flags & ASTNode.RECOVERED) != 0) {
if (!firstFlag) buffer.append('|');
firstFlag = false;
buffer.append("RECOVERED");
}
// All nodes are ORIGINAL by default. So an information is printed only when the node isn't ORIGINAL
if ((flags & ASTNode.ORIGINAL) == 0) {
if (!firstFlag) buffer.append('|');
firstFlag = false;
buffer.append("!ORIGINAL");
}
}
private void appendNodeExtendedPosition(ASTNode node, StringBuffer buffer) {
ASTNode root = node.getRoot();
if (root.getNodeType() == ASTNode.COMPILATION_UNIT) {
CompilationUnit cu = (CompilationUnit) root;
int extendedStartPosition = cu.getExtendedStartPosition(node);
int extendedLength = cu.getExtendedLength(node);
if (extendedStartPosition != node.getStartPosition() ||
extendedLength != node.getLength()) {
buffer.append('[');
buffer.append(cu.getExtendedStartPosition(node));
buffer.append(',');
buffer.append(cu.getExtendedLength(node));
buffer.append(']');
}
} else {
buffer.append("[N/A]");
}
}
private void appendNodePosition(ASTNode node, StringBuffer buffer) {
buffer.append('[');
buffer.append(node.getStartPosition());
buffer.append(',');
buffer.append(node.getLength());
buffer.append(']');
}
private void appendNodeType(ASTNode node, StringBuffer buffer) {
switch (node.getNodeType()) {
case ASTNode.ANNOTATION_TYPE_DECLARATION :
buffer.append("ANNOTATION_TYPE_DECLARATION");break;
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION :
buffer.append("ANNOTATION_TYPE_MEMBER_DECLARATION");break;
case ASTNode.ANONYMOUS_CLASS_DECLARATION :
buffer.append("ANONYMOUS_CLASS_DECLARATION");break;
case ASTNode.ARRAY_ACCESS :
buffer.append("ARRAY_ACCESS");break;
case ASTNode.ARRAY_CREATION :
buffer.append("ARRAY_CREATION");break;
case ASTNode.ARRAY_INITIALIZER :
buffer.append("ARRAY_INITIALIZER");break;
case ASTNode.ARRAY_TYPE :
buffer.append("ARRAY_TYPE");break;
case ASTNode.ASSERT_STATEMENT :
buffer.append("ASSERT_STATEMENT");break;
case ASTNode.ASSIGNMENT :
buffer.append("ASSIGNMENT");break;
case ASTNode.BLOCK :
buffer.append("BLOCK");break;
case ASTNode.BLOCK_COMMENT :
buffer.append("BLOCK_COMMENT");break;
case ASTNode.BOOLEAN_LITERAL :
buffer.append("BOOLEAN_LITERAL");break;
case ASTNode.BREAK_STATEMENT :
buffer.append("BREAK_STATEMENT");break;
case ASTNode.CAST_EXPRESSION :
buffer.append("CAST_EXPRESSION");break;
case ASTNode.CATCH_CLAUSE :
buffer.append("CATCH_CLAUSE");break;
case ASTNode.CHARACTER_LITERAL :
buffer.append("CHARACTER_LITERAL");break;
case ASTNode.CLASS_INSTANCE_CREATION :
buffer.append("CLASS_INSTANCE_CREATION");break;
case ASTNode.COMPILATION_UNIT :
buffer.append("COMPILATION_UNIT");break;
case ASTNode.CONDITIONAL_EXPRESSION :
buffer.append("CONDITIONAL_EXPRESSION");break;
case ASTNode.CONSTRUCTOR_INVOCATION :
buffer.append("CONSTRUCTOR_INVOCATION");break;
case ASTNode.CONTINUE_STATEMENT :
buffer.append("CONTINUE_STATEMENT");break;
case ASTNode.DO_STATEMENT :
buffer.append("DO_STATEMENT");break;
case ASTNode.EMPTY_STATEMENT :
buffer.append("EMPTY_STATEMENT");break;
case ASTNode.ENHANCED_FOR_STATEMENT :
buffer.append("ENHANCED_FOR_STATEMENT");break;
case ASTNode.ENUM_CONSTANT_DECLARATION :
buffer.append("ENUM_CONSTANT_DECLARATION");break;
case ASTNode.ENUM_DECLARATION :
buffer.append("ENUM_DECLARATION");break;
case ASTNode.EXPRESSION_STATEMENT :
buffer.append("EXPRESSION_STATEMENT");break;
case ASTNode.EXPORTS_DIRECTIVE :
buffer.append("EXPORTS_STATEMENT");break;
case ASTNode.FIELD_ACCESS :
buffer.append("FIELD_ACCESS");break;
case ASTNode.FIELD_DECLARATION :
buffer.append("FIELD_DECLARATION");break;
case ASTNode.FOR_STATEMENT :
buffer.append("FOR_STATEMENT");break;
case ASTNode.IF_STATEMENT :
buffer.append("IF_STATEMENT");break;
case ASTNode.IMPORT_DECLARATION :
buffer.append("IMPORT_DECLARATION");break;
case ASTNode.INFIX_EXPRESSION :
buffer.append("INFIX_EXPRESSION");break;
case ASTNode.INITIALIZER :
buffer.append("INITIALIZER");break;
case ASTNode.INSTANCEOF_EXPRESSION :
buffer.append("INSTANCEOF_EXPRESSION");break;
case ASTNode.JAVADOC :
buffer.append("JAVADOC");break;
case ASTNode.LABELED_STATEMENT :
buffer.append("LABELED_STATEMENT");break;
case ASTNode.LINE_COMMENT :
buffer.append("LINE_COMMENT");break;
case ASTNode.MARKER_ANNOTATION :
buffer.append("MARKER_ANNOTATION");break;
case ASTNode.MEMBER_REF :
buffer.append("MEMBER_REF");break;
case ASTNode.MEMBER_VALUE_PAIR :
buffer.append("MEMBER_VALUE_PAIR");break;
case ASTNode.METHOD_DECLARATION :
buffer.append("METHOD_DECLARATION");break;
case ASTNode.METHOD_INVOCATION :
buffer.append("METHOD_INVOCATION");break;
case ASTNode.METHOD_REF :
buffer.append("METHOD_REF");break;
case ASTNode.METHOD_REF_PARAMETER :
buffer.append("METHOD_REF_PARAMETER");break;
case ASTNode.MODIFIER :
buffer.append("MODIFIER");break;
case ASTNode.MODULE_DECLARATION :
buffer.append("MODULE_DECLARATION");break;
case ASTNode.NORMAL_ANNOTATION :
buffer.append("NORMAL_ANNOTATION");break;
case ASTNode.NULL_LITERAL :
buffer.append("NULL_LITERAL");break;
case ASTNode.NUMBER_LITERAL :
buffer.append("NUMBER_LITERAL");break;
case ASTNode.OPENS_DIRECTIVE :
buffer.append("OPENS_STATEMENT");break;
case ASTNode.PACKAGE_DECLARATION :
buffer.append("PACKAGE_DECLARATION");break;
case ASTNode.PARAMETERIZED_TYPE :
buffer.append("PARAMETERIZED_TYPE");break;
case ASTNode.PARENTHESIZED_EXPRESSION :
buffer.append("PARENTHESIZED_EXPRESSION");break;
case ASTNode.POSTFIX_EXPRESSION :
buffer.append("POSTFIX_EXPRESSION");break;
case ASTNode.PREFIX_EXPRESSION :
buffer.append("PREFIX_EXPRESSION");break;
case ASTNode.PRIMITIVE_TYPE :
buffer.append("PRIMITIVE_TYPE");break;
case ASTNode.PROVIDES_DIRECTIVE :
buffer.append("PROVIDES_STATEMENT");break;
case ASTNode.QUALIFIED_NAME :
buffer.append("QUALIFIED_NAME");break;
case ASTNode.QUALIFIED_TYPE :
buffer.append("QUALIFIED_TYPE");break;
case ASTNode.REQUIRES_DIRECTIVE :
buffer.append("REQUIRES_STATEMENT");break;
case ASTNode.RETURN_STATEMENT :
buffer.append("RETURN_STATEMENT");break;
case ASTNode.SIMPLE_NAME :
buffer.append("SIMPLE_NAME");break;
case ASTNode.SIMPLE_TYPE :
buffer.append("SIMPLE_TYPE");break;
case ASTNode.SINGLE_MEMBER_ANNOTATION :
buffer.append("SINGLE_MEMBER_ANNOTATION");break;
case ASTNode.SINGLE_VARIABLE_DECLARATION :
buffer.append("SINGLE_VARIABLE_DECLARATION");break;
case ASTNode.STRING_LITERAL :
buffer.append("STRING_LITERAL");break;
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION :
buffer.append("SUPER_CONSTRUCTOR_INVOCATION");break;
case ASTNode.SUPER_FIELD_ACCESS :
buffer.append("SUPER_FIELD_ACCESS");break;
case ASTNode.SUPER_METHOD_INVOCATION :
buffer.append("SUPER_METHOD_INVOCATION");break;
case ASTNode.SWITCH_CASE:
buffer.append("SWITCH_CASE");break;
case ASTNode.SWITCH_STATEMENT :
buffer.append("SWITCH_STATEMENT");break;
case ASTNode.SYNCHRONIZED_STATEMENT :
buffer.append("SYNCHRONIZED_STATEMENT");break;
case ASTNode.TAG_ELEMENT :
buffer.append("TAG_ELEMENT");break;
case ASTNode.TEXT_ELEMENT :
buffer.append("TEXT_ELEMENT");break;
case ASTNode.THIS_EXPRESSION :
buffer.append("THIS_EXPRESSION");break;
case ASTNode.THROW_STATEMENT :
buffer.append("THROW_STATEMENT");break;
case ASTNode.TRY_STATEMENT :
buffer.append("TRY_STATEMENT");break;
case ASTNode.TYPE_DECLARATION :
buffer.append("TYPE_DECLARATION");break;
case ASTNode.TYPE_DECLARATION_STATEMENT :
buffer.append("TYPE_DECLARATION_STATEMENT");break;
case ASTNode.TYPE_LITERAL :
buffer.append("TYPE_LITERAL");break;
case ASTNode.TYPE_PARAMETER :
buffer.append("TYPE_PARAMETER");break;
case ASTNode.USES_DIRECTIVE :
buffer.append("USES_STATEMENT");break;
case ASTNode.VARIABLE_DECLARATION_EXPRESSION :
buffer.append("VARIABLE_DECLARATION_EXPRESSION");break;
case ASTNode.VARIABLE_DECLARATION_FRAGMENT :
buffer.append("VARIABLE_DECLARATION_FRAGMENT");break;
case ASTNode.VARIABLE_DECLARATION_STATEMENT :
buffer.append("VARIABLE_DECLARATION_STATEMENT");break;
case ASTNode.WHILE_STATEMENT :
buffer.append("WHILE_STATEMENT");break;
case ASTNode.WILDCARD_TYPE :
buffer.append("WILDCARD_TYPE");break;
default:
buffer.append("UNKNOWN");
}
}
public String getText(ASTNode node) {
StringBuffer buffer = new StringBuffer();
boolean first = true;
if ((this.options & NODE_TYPE) != 0) {
if (!first) buffer.append(',');
first = false;
appendNodeType(node, buffer);
}
if ((this.options & NODE_POSITION) != 0) {
if (!first) buffer.append(',');
first = false;
appendNodePosition(node, buffer);
}
if ((this.options & NODE_EXTENDED_POSITION) != 0) {
if (!first) buffer.append(',');
first = false;
appendNodeExtendedPosition(node, buffer);
}
if ((this.options & NODE_FLAGS) != 0) {
if (!first) buffer.append(',');
first = false;
appendFlags(node, buffer);
}
if ((this.options & BINDING_OPTIONS) != 0) {
if (!first) buffer.append(',');
first = false;
appendBinding(node, buffer);
}
return buffer.toString();
}
protected IBinding resolveBinding(ASTNode node) {
switch (node.getNodeType()) {
case ASTNode.PACKAGE_DECLARATION:
return ((PackageDeclaration) node).resolveBinding();
case ASTNode.TYPE_DECLARATION:
return ((TypeDeclaration) node).resolveBinding();
case ASTNode.ANONYMOUS_CLASS_DECLARATION:
return ((AnonymousClassDeclaration) node).resolveBinding();
case ASTNode.TYPE_DECLARATION_STATEMENT:
return ((TypeDeclarationStatement) node).resolveBinding();
case ASTNode.METHOD_DECLARATION:
return ((MethodDeclaration) node).resolveBinding();
case ASTNode.METHOD_INVOCATION:
return ((MethodInvocation) node).resolveMethodBinding();
case ASTNode.TYPE_PARAMETER:
return ((TypeParameter) node).resolveBinding();
case ASTNode.PARAMETERIZED_TYPE:
return ((ParameterizedType) node).resolveBinding();
case ASTNode.WILDCARD_TYPE:
return ((WildcardType) node).resolveBinding();
case ASTNode.SIMPLE_NAME:
return ((SimpleName) node).resolveBinding();
case ASTNode.ARRAY_TYPE:
return ((ArrayType) node).resolveBinding();
case ASTNode.ASSIGNMENT:
return ((Assignment) node).getRightHandSide().resolveTypeBinding();
case ASTNode.SIMPLE_TYPE:
return ((SimpleType) node).resolveBinding();
case ASTNode.QUALIFIED_NAME:
return ((QualifiedName) node).resolveBinding();
case ASTNode.MARKER_ANNOTATION:
return ((MarkerAnnotation) node).resolveAnnotationBinding();
case ASTNode.NORMAL_ANNOTATION:
return ((NormalAnnotation) node).resolveAnnotationBinding();
case ASTNode.SINGLE_MEMBER_ANNOTATION:
return ((SingleMemberAnnotation) node).resolveAnnotationBinding();
case ASTNode.VARIABLE_DECLARATION_FRAGMENT:
return ((VariableDeclarationFragment) node).resolveBinding();
default:
throw new IllegalArgumentException();
}
}
}
/**
* Compute extra information about a marked node
*/
public static abstract class MarkedNodeLabelProvider {
public abstract String getText(ASTNode node);
}
private final static String AST_DELIMITER = "===== AST =====";
private final static String DETAILS_DELIMITER = "===== Details =====";
private final static String PROBLEMS_DELIMITER = "===== Problems =====";
private final static String NO_PROBLEM = "No problem";
private static final String NO_CORRESPONDING_NODE = "No corresponding node";
// options
private boolean reportAST;
private boolean reportProblems;
private String source;
private CompilationUnit unit;
private AbstractASTTests.MarkerInfo markerInfo;
private Map markerFromNode;
private Map nodeFromMarker;
private Map markerPositonInBuffer;
private boolean[] foundNodeFromMarker;
private StringBuffer markedNodesBuffer;
private MarkedNodeLabelProvider labelProvider;
public MarkedASTFlattener(
boolean reportAST,
boolean reportProblems,
MarkedNodeLabelProvider labelProvider) {
this.reportAST = reportAST;
this.reportProblems = reportProblems;
this.markedNodesBuffer = new StringBuffer();
this.labelProvider = labelProvider;
}
public String getResult() {
StringBuffer resultBuffer = new StringBuffer();
if (this.reportAST) {
resultBuffer.append(AST_DELIMITER);
resultBuffer.append('\n');
resultBuffer.append(super.getResult());
resultBuffer.append('\n');
}
resultBuffer.append(DETAILS_DELIMITER);
resultBuffer.append(this.markedNodesBuffer);
if (this.reportProblems) {
resultBuffer.append('\n');
resultBuffer.append(PROBLEMS_DELIMITER);
resultBuffer.append('\n');
StringBuffer problemBuffer = new StringBuffer();
IProblem[] problems = this.unit.getProblems();
int problemCount = problems.length;
if (problemCount != 0) {
for (int i = 0; i < problemCount; i++) {
org.eclipse.jdt.core.tests.util.Util.appendProblem(problemBuffer, problems[i], this.source == null ? null : this.source.toCharArray() , i + 1);
}
} else {
problemBuffer.append(NO_PROBLEM);
}
resultBuffer.append(Util.convertToIndependantLineDelimiter(problemBuffer.toString()));
}
return resultBuffer.toString();
}
public void postVisit(ASTNode node) {
String markerName;
if (this.reportAST && (markerName = (String)this.markerFromNode.get(node)) != null) {
// add start marker
int pos = ((Integer)this.markerPositonInBuffer.get(markerName)).intValue();
while (Character.isWhitespace(this.buffer.charAt(pos))) {pos++;}
this.buffer.insert(pos, this.markerInfo.markerStartStart + markerName + this.markerInfo.markerStartEnd);
// add end marker
pos = this.buffer.length() - 1;
while (Character.isWhitespace(this.buffer.charAt(pos))) {pos--;}
this.buffer.insert(pos + 1, this.markerInfo.markerEndStart + markerName + this.markerInfo.markerEndEnd);
}
}
public void preVisit(ASTNode node) {
String markerName = null;
int index = -1;
found : while ((index = this.markerInfo.indexOfASTStart(node.getStartPosition(), index + 1)) != -1) {
if (node.getStartPosition() + node.getLength() == this.markerInfo.astEnds[index]) {
markerName = String.valueOf(index + 1);
if (this.nodeFromMarker.get(markerName) == null) {
this.markerFromNode.put(node, markerName);
this.nodeFromMarker.put(markerName, node);
this.markerPositonInBuffer.put(markerName, Integer.valueOf(this.buffer.length()));
this.foundNodeFromMarker[index] = true;
}
break found;
}
}
if (markerName != null) {
this.markedNodesBuffer.append('\n');
this.markedNodesBuffer.append(markerName);
this.markedNodesBuffer.append(':');
this.markedNodesBuffer.append(this.labelProvider.getText(node));
}
}
public void process(CompilationUnit cu, AbstractASTTests.MarkerInfo mf) {
this.source = mf.source;
this.unit = cu;
this.markerInfo = mf;
this.markerFromNode = new HashMap();
this.nodeFromMarker = new HashMap();
this.markerPositonInBuffer = new HashMap();
int length = mf.astStarts.length;
this.foundNodeFromMarker = new boolean[length];
this.unit.accept(this);
for (int i = 0; i < length; i++) {
if (!this.foundNodeFromMarker[i]) {
this.markedNodesBuffer.append('\n');
this.markedNodesBuffer.append(String.valueOf(i + 1));
this.markedNodesBuffer.append(':');
this.markedNodesBuffer.append(NO_CORRESPONDING_NODE);
}
}
}
}