blob: c7e58a9483c7f5358a6bcdba76d7bf89f7047f62 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2006 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.jem.workbench.utility;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jem.internal.instantiation.*;
/**
* Create a parse tree from an AST node.
* @since 1.0.0
*/
public class ParseTreeCreationFromAST extends ASTVisitor {
protected final Resolver resolver;
protected PTExpression expression; // Each visit (or endvisit) will put into expression the result of the visit if it produced an expression.
/**
* This is the abstract base class used by ParseTreeCreationFromAST to resolve the types to the appropriate
* types (e.g. "String" to "java.lang.String").
*
* @see org.eclipse.jem.workbench.utility.ParseTreeCreationFromAST
* @since 1.0.0
*/
public static abstract class Resolver {
/**
* Resolve the Name. It can return either a PTName if it is just a classname,
* or a PTFieldAccess if it resolves to a PTFieldAccess. The PTFieldAccess should
* be complete. e.g we have class like:
* <code>
* package xyz;
* public class AClass {
* public java.awt.Rectangle rect;
* }
* </code>
* Then a Name like <code>AClass.rect.x</code> should resolve to:
*
* PTFieldAccess:
* receiver:
* PTFieldAccess
* receiver: xyz.AClass
* field: "rect"
* field: "x"
*
* Actually it can return any valid expression that has a value (i.e. it cannot be a method invocation with a <code>void</code> return type).
*
* @param name
* @return Either a fully-qualified name (as a PTName) or a PTFieldAccess, any other type of expression.
*
* @since 1.0.0
*/
public abstract PTExpression resolveName(Name name) throws InvalidExpressionException;
/**
* Resolve the type. If it is an array type return it in format "type[][]".
*
* @param type
* @return The type name, including brackets if array type.
*
* @since 1.0.0
*/
public abstract String resolveType(Type type) throws InvalidExpressionException;
/**
* This is for resolving "this" literal. It should either return a PTThisLiteral, if it
* can't do resolve, or some PTExpression that can resolve to "this" for evaluation.
*
* @return If resolvable, a PTExpression, else a PTThisLiteral if not resolvable.
* @throws InvalidExpressionException
*
* @since 1.0.0
*/
public abstract PTExpression resolveThis() throws InvalidExpressionException;
/**
* Resolve the type specified as a Name. It may be a simple name or it may be
* a qualified name. This is used when we have Name that we know must be a
* type. This is so that there is no confusion with it possibly being a field or variable
* that has the same case and spelling as a type name.
* @param name
* @return the type name.
* @throws InvalidExpressionException
*
* @since 1.0.0
*/
public abstract String resolveType(Name name) throws InvalidExpressionException;
/**
* This is used by the resolver if it can't resolve for some reason. This will throw
* an invalid expression exception which will be handled by the ParseTreeCreationFromAST.
*
* @param msg Message to be put into the exception.
* @throws InvalidExpressionException
*
* @since 1.0.0
*/
protected final void throwInvalidExpressionException(String msg) throws InvalidExpressionException {
throw new InvalidExpressionException(msg);
}
}
/*
* When an invalid expression has been found this exception should be thrown. It will
* be caught at the top and converted into an InvalidExpression and the rest of the parse tree will be
* thrown away.
*
* The message will be a message as to why it is invalid.
*
* @since 1.0.0
*/
protected static class InvalidExpressionException extends IllegalArgumentException {
/**
* Comment for <code>serialVersionUID</code>
*
* @since 1.1.0
*/
private static final long serialVersionUID = 2429845631915206678L;
/**
* @param s The message to be used in the final invalid expression.
*
* @since 1.0.0
*/
public InvalidExpressionException(String s) {
super(s);
}
}
/**
* Construct with the given resolver.
*
* @param resolver
*
* @since 1.0.0
*/
public ParseTreeCreationFromAST(Resolver resolver) {
this.resolver = resolver;
}
/**
* Process the AST Expression and return a PTExpression. If any part was invalid, then
* only an PTInvalidExpression will be returned.
*
* @param astExpression
* @return The PTExpression.
*
* @since 1.0.0
*/
public final PTExpression createExpression(Expression astExpression) {
try {
return perform(astExpression);
} catch (InvalidExpressionException e) {
// Create a msg that is formed of the exception message and the full init string.
String msg = MessageFormat.format(WorkbenchUtilityMessages.ParseTreeCreationFromAST_0, new Object[] {e.getLocalizedMessage(), astExpression.toString()});
PTInvalidExpression exp = InstantiationFactory.eINSTANCE.createPTInvalidExpression();
exp.setMessage(msg);
return exp;
}
}
/*
* Visit the AST expression and get the ParseTree Expression.
* This is used by the individual visits when parsing a tree.
* It passes to the top method (createExpression), which can
* handle the InvalidExpressionException.
*
* If any visit doesn't return an expression, then an invalid
* expression exception will be thrown to indicate this. If the
* incoming expression is <code>null</code>, then return of <code>null</code> is ok because
* this would be for an optional expression which didn't exist.
*
* @return The new ParseTree Expression or <code>null</code> if incoming expression was null.
*
* @see createExpression(org.eclipse.jdt.core.dom.Expression)
* @exception InvalidExpressionException
* @since 1.0.0
*/
protected final PTExpression perform(Expression astExpression) {
if (astExpression != null) {
expression = null;
astExpression.accept(this);
if (expression == null)
throw new InvalidExpressionException(MessageFormat.format(WorkbenchUtilityMessages.ParseTreeCreationFromAST_ExpressionTooComplicated_EXC_, new Object[] {astExpression.toString()}));
return expression;
} else
return null; // This is ok. It means an optional expression was being processed and the expression didn't exist.
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ArrayAccess)
*/
public boolean visit(ArrayAccess node) {
PTArrayAccess aa = InstantiationFactory.eINSTANCE.createPTArrayAccess();
List indexes = aa.getIndexes();
Expression arrayExp = node;
while (arrayExp.getNodeType() == ASTNode.ARRAY_ACCESS) {
// Visit the index to get the index expression.
ArrayAccess array = (ArrayAccess) arrayExp;
indexes.add(0, perform(array.getIndex())); // We're trying to create the final expression from inside out, the indexes are created in reverse order.
arrayExp = array.getArray();
}
aa.setArray(perform(arrayExp)); // Final arrayExp is the true expression.
expression = aa; // Set the return expression for this visit.
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ArrayCreation)
*/
public boolean visit(ArrayCreation node) {
PTArrayCreation ac = InstantiationFactory.eINSTANCE.createPTArrayCreation();
ac.setType(resolver.resolveType(node.getType()));
List acDims = ac.getDimensions();
List nDims = node.dimensions();
int nsize = nDims.size();
for (int i = 0; i < nsize; i++) {
acDims.add(perform((Expression) nDims.get(i)));
}
ac.setInitializer((PTArrayInitializer) perform(node.getInitializer()));
expression = ac;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ArrayInitializer)
*/
public boolean visit(ArrayInitializer node) {
PTArrayInitializer ai = InstantiationFactory.eINSTANCE.createPTArrayInitializer();
List exps = node.expressions();
List aiexps = ai.getExpressions();
int nexp = exps.size();
for (int i = 0; i < nexp; i++) {
aiexps.add(perform((Expression) exps.get(i)));
}
expression = ai;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Assignment)
*/
public boolean visit(Assignment node) {
return false; // We can't handle assignment.
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.BooleanLiteral)
*/
public boolean visit(BooleanLiteral node) {
PTBooleanLiteral bl = InstantiationFactory.eINSTANCE.createPTBooleanLiteral();
bl.setBooleanValue(node.booleanValue());
expression = bl;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.CastExpression)
*/
public boolean visit(CastExpression node) {
PTCastExpression ct = InstantiationFactory.eINSTANCE.createPTCastExpression();
ct.setType(resolver.resolveType(node.getType()));
ct.setExpression(perform(node.getExpression()));
expression = ct;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.CharacterLiteral)
*/
public boolean visit(CharacterLiteral node) {
PTCharacterLiteral cl = InstantiationFactory.eINSTANCE.createPTCharacterLiteral();
cl.setEscapedValue(node.getEscapedValue());
cl.setCharValue(node.charValue());
expression = cl;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ClassInstanceCreation)
*/
public boolean visit(ClassInstanceCreation node) {
if (node.getAnonymousClassDeclaration() != null) {
PTAnonymousClassDeclaration adecl = InstantiationFactory.eINSTANCE.createPTAnonymousClassDeclaration();
adecl.setDeclaration(node.toString());
expression = adecl;
} else {
PTClassInstanceCreation cic = InstantiationFactory.eINSTANCE.createPTClassInstanceCreation();
// If ast level = 2, then you must use getName, but the name needs to be turned into a type
// so that it can be resolved. If ast level > 2, then it will return a type to be resolved.
// Note: can't just use resolve name on the name because if a field and a class were spelled
// the same then the codegen resolver would return an instance ref to the field instead.
String type = node.getAST().apiLevel() == AST.JLS2 ? resolver.resolveType(node.getName()) : resolver.resolveType(node.getType());
if (type == null) {
type = node.getAST().apiLevel() == AST.JLS2 ? node.getName().getFullyQualifiedName() : node.getType().toString();
}
cic.setType(type);
List args = cic.getArguments();
List nargs = node.arguments();
int nsize = nargs.size();
for (int i = 0; i < nsize; i++) {
args.add(perform((Expression) nargs.get(i)));
}
expression = cic;
}
return false;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ConditionalExpression)
*/
public boolean visit(ConditionalExpression node) {
PTConditionalExpression ce = InstantiationFactory.eINSTANCE.createPTConditionalExpression();
ce.setCondition(perform(node.getExpression()));
ce.setTrue(perform(node.getThenExpression()));
ce.setFalse(perform(node.getElseExpression()));
expression = ce;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.FieldAccess)
*/
public boolean visit(FieldAccess node) {
expression = createFieldAccess(node.getName().getIdentifier(), perform(node.getExpression()));
return false;
}
protected PTExpression createFieldAccess(String name, PTExpression receiver) {
PTFieldAccess fa = InstantiationFactory.eINSTANCE.createPTFieldAccess();
fa.setReceiver(receiver);
fa.setField(name);
return fa;
}
private static HashMap infixOperToParseOper;
private final PTInfixOperator getParseInfix(InfixExpression.Operator operator) {
if (prefixOperToParseOper == null) {
infixOperToParseOper = new HashMap(5);
infixOperToParseOper.put(InfixExpression.Operator.AND, PTInfixOperator.AND_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.CONDITIONAL_AND, PTInfixOperator.CONDITIONAL_AND_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.CONDITIONAL_OR, PTInfixOperator.CONDITIONAL_OR_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.DIVIDE, PTInfixOperator.DIVIDE_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.EQUALS, PTInfixOperator.EQUALS_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.GREATER_EQUALS, PTInfixOperator.GREATER_EQUALS_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.GREATER, PTInfixOperator.GREATER_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.LEFT_SHIFT, PTInfixOperator.LEFT_SHIFT_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.LESS_EQUALS, PTInfixOperator.LESS_EQUALS_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.LESS, PTInfixOperator.LESS_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.MINUS, PTInfixOperator.MINUS_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.NOT_EQUALS, PTInfixOperator.NOT_EQUALS_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.OR, PTInfixOperator.OR_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.PLUS, PTInfixOperator.PLUS_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.REMAINDER, PTInfixOperator.REMAINDER_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.RIGHT_SHIFT_SIGNED, PTInfixOperator.RIGHT_SHIFT_SIGNED_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED, PTInfixOperator.RIGHT_SHIFT_UNSIGNED_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.TIMES, PTInfixOperator.TIMES_LITERAL);
infixOperToParseOper.put(InfixExpression.Operator.XOR, PTInfixOperator.XOR_LITERAL);
}
return (PTInfixOperator) infixOperToParseOper.get(operator);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.InfixExpression)
*/
public boolean visit(InfixExpression node) {
PTInfixExpression inf = InstantiationFactory.eINSTANCE.createPTInfixExpression();
inf.setLeftOperand(perform(node.getLeftOperand()));
PTInfixOperator inoper = getParseInfix(node.getOperator());
if (inoper == null) {
// It is not one we can handle.
throw new InvalidExpressionException(
MessageFormat.format(WorkbenchUtilityMessages.ParseTreeCreationFromAST_OperatorTooComplicatedToHandle_EXC_, new Object[] { node.getOperator().toString() }));
}
inf.setOperator(inoper);
inf.setRightOperand(perform(node.getRightOperand()));
List eops = inf.getExtendedOperands();
List neops = node.extendedOperands();
int nsize = neops.size();
for (int i = 0; i < nsize; i++) {
eops.add(perform((Expression) neops.get(i)));
}
expression = inf;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.InstanceofExpression)
*/
public boolean visit(InstanceofExpression node) {
PTInstanceof inof = InstantiationFactory.eINSTANCE.createPTInstanceof();
inof.setOperand(perform(node.getLeftOperand()));
inof.setType(resolver.resolveType(node.getRightOperand()));
expression = inof;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.PostfixExpression)
*/
public boolean visit(PostfixExpression node) {
return false; // We can't handle post fix.
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodInvocation)
*/
public boolean visit(MethodInvocation node) {
expression = createMethodInvocation(node.getName().getIdentifier(), perform(node.getExpression()), node.arguments());
return false;
}
/**
* Create a method invocation using the giving receiver, name, args.
* @param node
* @return
*
* @since 1.2.0
*/
protected PTMethodInvocation createMethodInvocation(String name, PTExpression receiver, List argExpressions) {
PTMethodInvocation mi = InstantiationFactory.eINSTANCE.createPTMethodInvocation();
mi.setReceiver(receiver);
mi.setName(name);
List args = mi.getArguments();
int nsize = argExpressions.size();
for (int i = 0; i < nsize; i++) {
args.add(perform((Expression) argExpressions.get(i)));
}
return mi;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.NullLiteral)
*/
public boolean visit(NullLiteral node) {
expression = InstantiationFactory.eINSTANCE.createPTNullLiteral();
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.NumberLiteral)
*/
public boolean visit(NumberLiteral node) {
PTNumberLiteral nl = InstantiationFactory.eINSTANCE.createPTNumberLiteral();
nl.setToken(node.getToken());
expression = nl;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ParenthesizedExpression)
*/
public boolean visit(ParenthesizedExpression node) {
PTParenthesizedExpression pe = InstantiationFactory.eINSTANCE.createPTParenthesizedExpression();
pe.setExpression(perform(node.getExpression()));
expression = pe;
return false;
}
private static HashMap prefixOperToParseOper;
private final PTPrefixOperator getParsePrefix(PrefixExpression.Operator operator) {
if (prefixOperToParseOper == null) {
prefixOperToParseOper = new HashMap(5);
prefixOperToParseOper.put(PrefixExpression.Operator.COMPLEMENT, PTPrefixOperator.COMPLEMENT_LITERAL);
prefixOperToParseOper.put(PrefixExpression.Operator.MINUS, PTPrefixOperator.MINUS_LITERAL);
prefixOperToParseOper.put(PrefixExpression.Operator.NOT, PTPrefixOperator.NOT_LITERAL);
prefixOperToParseOper.put(PrefixExpression.Operator.PLUS, PTPrefixOperator.PLUS_LITERAL);
}
return (PTPrefixOperator) prefixOperToParseOper.get(operator);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.PrefixExpression)
*/
public boolean visit(PrefixExpression node) {
if (node.getOperand().getNodeType() == ASTNode.NUMBER_LITERAL) {
// For number literals we see if it is a "+" or "-" prefix, and if it is, we simply
// create a PTNumberLiteral with the operator already in it. It is a simplification.
// Any other operator we've left alone since those won't be decoded simply by the
// Number decoder.
// If not a number literal, then leave alone since needs to be handled as a prefix
// operation.
PrefixExpression.Operator operator = node.getOperator();
if (operator == PrefixExpression.Operator.PLUS || operator == PrefixExpression.Operator.MINUS) {
PTNumberLiteral nm = InstantiationFactory.eINSTANCE.createPTNumberLiteral();
nm.setToken(operator.toString() + ((NumberLiteral) node.getOperand()).getToken());
expression = nm;
return false;
}
}
PTPrefixExpression pe = InstantiationFactory.eINSTANCE.createPTPrefixExpression();
PTPrefixOperator ptoper = getParsePrefix(node.getOperator());
if (ptoper == null) {
// It is not one we can handle.
throw new InvalidExpressionException(
MessageFormat.format(WorkbenchUtilityMessages.ParseTreeCreationFromAST_OperatorTooComplicatedToHandle_EXC_, new Object[] { node.getOperator().toString() }));
}
pe.setOperator(ptoper);
pe.setExpression(perform(node.getOperand()));
expression = pe;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.QualifiedName)
*/
public boolean visit(QualifiedName node) {
expression = resolver.resolveName(node);
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SimpleName)
*/
public boolean visit(SimpleName node) {
expression = resolver.resolveName(node);
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.StringLiteral)
*/
public boolean visit(StringLiteral node) {
PTStringLiteral sl = InstantiationFactory.eINSTANCE.createPTStringLiteral();
sl.setEscapedValue(node.getEscapedValue());
sl.setLiteralValue(node.getLiteralValue());
expression = sl;
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SuperFieldAccess)
*/
public boolean visit(SuperFieldAccess node) {
expression = createFieldAccess(node.getName().getIdentifier(), resolver.resolveThis());
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SuperMethodInvocation)
*/
public boolean visit(SuperMethodInvocation node) {
expression = createMethodInvocation(node.getName().getIdentifier(), resolver.resolveThis(), node.arguments());
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ThisExpression)
*/
public boolean visit(ThisExpression node) {
expression = resolver.resolveThis();
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeLiteral)
*/
public boolean visit(TypeLiteral node) {
PTTypeLiteral ptl = InstantiationFactory.eINSTANCE.createPTTypeLiteral();
ptl.setType(resolver.resolveType(node.getType()));
expression = ptl;
return false;
}
}