/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.ast;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.SourceCodePositioner;
import apex.jorje.semantic.ast.AstNode;
import apex.jorje.semantic.ast.compilation.UserClass;
import apex.jorje.semantic.ast.compilation.UserClassMethods;
import apex.jorje.semantic.ast.compilation.UserEnum;
import apex.jorje.semantic.ast.compilation.UserExceptionMethods;
import apex.jorje.semantic.ast.compilation.UserInterface;
import apex.jorje.semantic.ast.compilation.UserTrigger;
import apex.jorje.semantic.ast.condition.StandardCondition;
import apex.jorje.semantic.ast.expression.ArrayLoadExpression;
import apex.jorje.semantic.ast.expression.ArrayStoreExpression;
import apex.jorje.semantic.ast.expression.AssignmentExpression;
import apex.jorje.semantic.ast.expression.BinaryExpression;
import apex.jorje.semantic.ast.expression.BindExpressions;
import apex.jorje.semantic.ast.expression.BooleanExpression;
import apex.jorje.semantic.ast.expression.ClassRefExpression;
import apex.jorje.semantic.ast.expression.DottedExpression;
import apex.jorje.semantic.ast.expression.Expression;
import apex.jorje.semantic.ast.expression.InstanceOfExpression;
import apex.jorje.semantic.ast.expression.JavaMethodCallExpression;
import apex.jorje.semantic.ast.expression.JavaVariableExpression;
import apex.jorje.semantic.ast.expression.LiteralExpression;
import apex.jorje.semantic.ast.expression.MapEntryNode;
import apex.jorje.semantic.ast.expression.MethodCallExpression;
import apex.jorje.semantic.ast.expression.NewListInitExpression;
import apex.jorje.semantic.ast.expression.NewListLiteralExpression;
import apex.jorje.semantic.ast.expression.NewMapInitExpression;
import apex.jorje.semantic.ast.expression.NewMapLiteralExpression;
import apex.jorje.semantic.ast.expression.NewNameValueObjectExpression;
import apex.jorje.semantic.ast.expression.NewObjectExpression;
import apex.jorje.semantic.ast.expression.NewSetInitExpression;
import apex.jorje.semantic.ast.expression.NewSetLiteralExpression;
import apex.jorje.semantic.ast.expression.PackageVersionExpression;
import apex.jorje.semantic.ast.expression.PostfixExpression;
import apex.jorje.semantic.ast.expression.PrefixExpression;
import apex.jorje.semantic.ast.expression.ReferenceExpression;
import apex.jorje.semantic.ast.expression.SoqlExpression;
import apex.jorje.semantic.ast.expression.SoslExpression;
import apex.jorje.semantic.ast.expression.SuperMethodCallExpression;
import apex.jorje.semantic.ast.expression.SuperVariableExpression;
import apex.jorje.semantic.ast.expression.TernaryExpression;
import apex.jorje.semantic.ast.expression.ThisMethodCallExpression;
import apex.jorje.semantic.ast.expression.ThisVariableExpression;
import apex.jorje.semantic.ast.expression.TriggerVariableExpression;
import apex.jorje.semantic.ast.expression.VariableExpression;
import apex.jorje.semantic.ast.member.Field;
import apex.jorje.semantic.ast.member.Method;
import apex.jorje.semantic.ast.member.Parameter;
import apex.jorje.semantic.ast.member.Property;
import apex.jorje.semantic.ast.member.bridge.BridgeMethodCreator;
import apex.jorje.semantic.ast.modifier.Annotation;
import apex.jorje.semantic.ast.modifier.AnnotationParameter;
import apex.jorje.semantic.ast.modifier.ModifierNode;
import apex.jorje.semantic.ast.modifier.ModifierOrAnnotation;
import apex.jorje.semantic.ast.statement.BlockStatement;
import apex.jorje.semantic.ast.statement.BreakStatement;
import apex.jorje.semantic.ast.statement.CatchBlockStatement;
import apex.jorje.semantic.ast.statement.ContinueStatement;
import apex.jorje.semantic.ast.statement.DmlDeleteStatement;
import apex.jorje.semantic.ast.statement.DmlInsertStatement;
import apex.jorje.semantic.ast.statement.DmlMergeStatement;
import apex.jorje.semantic.ast.statement.DmlUndeleteStatement;
import apex.jorje.semantic.ast.statement.DmlUpdateStatement;
import apex.jorje.semantic.ast.statement.DmlUpsertStatement;
import apex.jorje.semantic.ast.statement.DoLoopStatement;
import apex.jorje.semantic.ast.statement.ExpressionStatement;
import apex.jorje.semantic.ast.statement.FieldDeclaration;
import apex.jorje.semantic.ast.statement.FieldDeclarationStatements;
import apex.jorje.semantic.ast.statement.ForEachStatement;
import apex.jorje.semantic.ast.statement.ForLoopStatement;
import apex.jorje.semantic.ast.statement.IfBlockStatement;
import apex.jorje.semantic.ast.statement.IfElseBlockStatement;
import apex.jorje.semantic.ast.statement.ReturnStatement;
import apex.jorje.semantic.ast.statement.RunAsBlockStatement;
import apex.jorje.semantic.ast.statement.Statement;
import apex.jorje.semantic.ast.statement.ThrowStatement;
import apex.jorje.semantic.ast.statement.TryCatchFinallyBlockStatement;
import apex.jorje.semantic.ast.statement.VariableDeclaration;
import apex.jorje.semantic.ast.statement.VariableDeclarationStatements;
import apex.jorje.semantic.ast.statement.WhileLoopStatement;
import apex.jorje.semantic.ast.visitor.AdditionalPassScope;
import apex.jorje.semantic.ast.visitor.AstVisitor;
import apex.jorje.semantic.exception.Errors;
import apex.jorje.semantic.tester.TestNode;
public final class ApexTreeBuilder extends AstVisitor<AdditionalPassScope> {
private static final Map<Class<? extends AstNode>, Constructor<? extends ApexNode<?>>> NODE_TYPE_TO_NODE_ADAPTER_TYPE = new HashMap<>();
static {
register(Annotation.class, ASTAnnotation.class);
register(AnnotationParameter.class, ASTAnnotationParameter.class);
register(ArrayLoadExpression.class, ASTArrayLoadExpression.class);
register(ArrayStoreExpression.class, ASTArrayStoreExpression.class);
register(AssignmentExpression.class, ASTAssignmentExpression.class);
register(BinaryExpression.class, ASTBinaryExpression.class);
register(BindExpressions.class, ASTBindExpressions.class);
register(BlockStatement.class, ASTBlockStatement.class);
register(BooleanExpression.class, ASTBooleanExpression.class);
register(BreakStatement.class, ASTBreakStatement.class);
register(BridgeMethodCreator.class, ASTBridgeMethodCreator.class);
register(CatchBlockStatement.class, ASTCatchBlockStatement.class);
register(ClassRefExpression.class, ASTClassRefExpression.class);
register(ContinueStatement.class, ASTContinueStatement.class);
register(DmlDeleteStatement.class, ASTDmlDeleteStatement.class);
register(DmlInsertStatement.class, ASTDmlInsertStatement.class);
register(DmlMergeStatement.class, ASTDmlMergeStatement.class);
register(DmlUndeleteStatement.class, ASTDmlUndeleteStatement.class);
register(DmlUpdateStatement.class, ASTDmlUpdateStatement.class);
register(DmlUpsertStatement.class, ASTDmlUpsertStatement.class);
register(DoLoopStatement.class, ASTDoLoopStatement.class);
register(DottedExpression.class, ASTDottedExpression.class);
register(Expression.class, ASTExpression.class);
register(ExpressionStatement.class, ASTExpressionStatement.class);
register(Field.class, ASTField.class);
register(FieldDeclaration.class, ASTFieldDeclaration.class);
register(FieldDeclarationStatements.class, ASTFieldDeclarationStatements.class);
register(ForEachStatement.class, ASTForEachStatement.class);
register(ForLoopStatement.class, ASTForLoopStatement.class);
register(IfBlockStatement.class, ASTIfBlockStatement.class);
register(IfElseBlockStatement.class, ASTIfElseBlockStatement.class);
register(InstanceOfExpression.class, ASTInstanceOfExpression.class);
register(JavaMethodCallExpression.class, ASTJavaMethodCallExpression.class);
register(JavaVariableExpression.class, ASTJavaVariableExpression.class);
register(LiteralExpression.class, ASTLiteralExpression.class);
register(MapEntryNode.class, ASTMapEntryNode.class);
register(Method.class, ASTMethod.class);
register(MethodCallExpression.class, ASTMethodCallExpression.class);
register(ModifierNode.class, ASTModifierNode.class);
register(ModifierOrAnnotation.class, ASTModifierOrAnnotation.class);
register(NewListInitExpression.class, ASTNewListInitExpression.class);
register(NewListLiteralExpression.class, ASTNewListLiteralExpression.class);
register(NewMapInitExpression.class, ASTNewMapInitExpression.class);
register(NewMapLiteralExpression.class, ASTNewMapLiteralExpression.class);
register(NewNameValueObjectExpression.class, ASTNewNameValueObjectExpression.class);
register(NewObjectExpression.class, ASTNewObjectExpression.class);
register(NewSetInitExpression.class, ASTNewSetInitExpression.class);
register(NewSetLiteralExpression.class, ASTNewSetLiteralExpression.class);
register(PackageVersionExpression.class, ASTPackageVersionExpression.class);
register(Parameter.class, ASTParameter.class);
register(PostfixExpression.class, ASTPostfixExpression.class);
register(PrefixExpression.class, ASTPrefixExpression.class);
register(Property.class, ASTProperty.class);
register(ReferenceExpression.class, ASTReferenceExpression.class);
register(ReturnStatement.class, ASTReturnStatement.class);
register(RunAsBlockStatement.class, ASTRunAsBlockStatement.class);
register(SoqlExpression.class, ASTSoqlExpression.class);
register(SoslExpression.class, ASTSoslExpression.class);
register(StandardCondition.class, ASTStandardCondition.class);
register(Statement.class, ASTStatement.class);
register(SuperMethodCallExpression.class, ASTSuperMethodCallExpression.class);
register(SuperVariableExpression.class, ASTSuperVariableExpression.class);
register(TernaryExpression.class, ASTTernaryExpression.class);
register(TestNode.class, ASTTestNode.class);
register(ThisMethodCallExpression.class, ASTThisMethodCallExpression.class);
register(ThisVariableExpression.class, ASTThisVariableExpression.class);
register(ThrowStatement.class, ASTThrowStatement.class);
register(TriggerVariableExpression.class, ASTTriggerVariableExpression.class);
register(TryCatchFinallyBlockStatement.class, ASTTryCatchFinallyBlockStatement.class);
register(UserClass.class, ASTUserClass.class);
register(UserTrigger.class, ASTUserTrigger.class);
register(UserClassMethods.class, ASTUserClassMethods.class);
register(UserEnum.class, ASTUserEnum.class);
register(UserExceptionMethods.class, ASTUserExceptionMethods.class);
register(UserInterface.class, ASTUserInterface.class);
register(VariableDeclaration.class, ASTVariableDeclaration.class);
register(VariableDeclarationStatements.class, ASTVariableDeclarationStatements.class);
register(VariableExpression.class, ASTVariableExpression.class);
register(WhileLoopStatement.class, ASTWhileLoopStatement.class);
}
private static <T extends AstNode> void register(Class<T> nodeType, Class<? extends ApexNode<T>> nodeAdapterType) {
try {
NODE_TYPE_TO_NODE_ADAPTER_TYPE.put(nodeType, nodeAdapterType.getConstructor(nodeType));
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
// The nodes having children built.
private Stack<Node> nodes = new Stack<>();
// The Apex nodes with children to build.
private Stack<AstNode> parents = new Stack<>();
private SourceCodePositioner sourceCodePositioner;
public ApexTreeBuilder(String sourceCode) {
sourceCodePositioner = new SourceCodePositioner(sourceCode);
}
AdditionalPassScope scope = new AdditionalPassScope(new Errors());
static <T extends AstNode> ApexNode<T> createNodeAdapter(T node) {
try {
@SuppressWarnings("unchecked")
// the register function makes sure only ApexNode<T> can be added,
// where T is "T extends AstNode".
Constructor<? extends ApexNode<T>> constructor = (Constructor<? extends ApexNode<T>>) NODE_TYPE_TO_NODE_ADAPTER_TYPE
.get(node.getClass());
if (constructor == null) {
throw new IllegalArgumentException(
"There is no Node adapter class registered for the Node class: " + node.getClass());
}
return constructor.newInstance(node);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getTargetException());
}
}
public <T extends AstNode> ApexNode<T> build(T astNode) {
// Create a Node
ApexNode<T> node = createNodeAdapter(astNode);
calculateLineNumbers(node);
// Append to parent
Node parent = nodes.isEmpty() ? null : nodes.peek();
if (parent != null) {
parent.jjtAddChild(node, parent.jjtGetNumChildren());
node.jjtSetParent(parent);
}
// Build the children...
nodes.push(node);
parents.push(astNode);
astNode.traverse(this, scope);
nodes.pop();
parents.pop();
return node;
}
private void calculateLineNumbers(ApexNode<?> node) {
AbstractApexNode<?> apexNode = (AbstractApexNode<?>) node;
apexNode.calculateLineNumbers(sourceCodePositioner);
}
private boolean visit(AstNode node) {
if (parents.peek() == node) {
return true;
} else {
build(node);
return false;
}
}
@Override
public boolean visit(UserEnum node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(UserInterface node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(UserTrigger node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ArrayLoadExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ArrayStoreExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(AssignmentExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(BinaryExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(BooleanExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ClassRefExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(InstanceOfExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(JavaMethodCallExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(JavaVariableExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(LiteralExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ReferenceExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(MethodCallExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(NewListInitExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(NewMapInitExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(NewSetInitExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(NewListLiteralExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(NewObjectExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(NewSetLiteralExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(NewNameValueObjectExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(PackageVersionExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(PostfixExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(PrefixExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(TernaryExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(StandardCondition node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(TriggerVariableExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(DottedExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(VariableExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(BlockStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(BreakStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ContinueStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(DmlDeleteStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(DmlInsertStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(DmlMergeStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(DmlUndeleteStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(DmlUpdateStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(DmlUpsertStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(DoLoopStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ExpressionStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ForEachStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ForLoopStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(FieldDeclaration node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(FieldDeclarationStatements node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(IfBlockStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(IfElseBlockStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ReturnStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(RunAsBlockStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ThrowStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(VariableDeclaration node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(VariableDeclarationStatements node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(WhileLoopStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(BindExpressions node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(SoqlExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(SoslExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(NewMapLiteralExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(MapEntryNode node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(CatchBlockStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(TryCatchFinallyBlockStatement node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(Property node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(Field node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(Parameter node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(BridgeMethodCreator node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(UserClassMethods node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(UserExceptionMethods node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(Annotation node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(AnnotationParameter node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ModifierNode node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(SuperMethodCallExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ThisMethodCallExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(SuperVariableExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(ThisVariableExpression node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(UserClass node, AdditionalPassScope scope) {
return visit(node);
}
@Override
public boolean visit(Method node, AdditionalPassScope scope) {
return visit(node);
}
}