/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * JavaExpression.java * Creation date: Sep 10, 2003. * By: Edward Lam */ package org.openquark.cal.internal.javamodel; import java.util.Arrays; import org.openquark.cal.compiler.QualifiedName; import org.openquark.cal.services.Assert; /** * Object representation for a Java expression. * * Note that it is important that the classes in the Java Source Model such as JavaExpression be * independent of the particular Java Model -> Byte Code tool (such as BCEL or ASM) that we use. * In particular, no classes from ASM or BCEL should be used anywhere in the Java Source Model * (or Java Source Model to Java source generator). * * @author Edward Lam */ public abstract class JavaExpression { public static final JavaExpression[] EMPTY_JAVA_EXPRESSION_ARRAY = new JavaExpression[0]; public static final JavaTypeName[] EMPTY_TYPE_NAME_ARRAY = new JavaTypeName[0]; /** * Accepts the visitation of a visitor, which implements the * JavaModelVisitor interface. This abstract method is to be overriden * by each concrete subclass so that the corrent visit method on the * visitor may be called based upon the type of the element being * visited. Each concrete subclass of JavaExpression should correspond * one-to-one with a visit method declaration in the SourceModelVisitor * interface. * <p> * * As the JavaModelVisitor follows a more general visitor pattern * where arguments can be passed into the visit methods and return * values obtained from them, this method passes through the argument * into the visit method, and returns as its return value the return * value of the visit method. * <p> * * Nonetheless, for a significant portion of the common cases, the state of the * visitation can simply be kept as member variables within the visitor itself, * thereby eliminating the need to use the argument and return value of the * visit methods. In these scenarios, the recommended approach is to use * {@link Void} as the type argument for both <code>T</code> and <code>R</code>, and * pass in null as the argument, and return null as the return value. * <p> * * @see JavaModelVisitor * * @param <T> the argument type. If the visitation argument is not used, specify {@link Void}. * @param <R> the return type. If the return value is not used, specify {@link Void}. * * @param visitor * the visitor * @param arg * the argument to be passed to the visitor's visitXXX method * @return the return value of the visitor's visitXXX method */ public abstract <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg); /** * A helper class to output an expression as a string. * @author Edward Lam */ static class Stringifier { private static final String EOL = System.getProperty("line.separator"); private static final String INDENT = " "; /** * Return a string representation of an expression. * @param javaExpression the expression * @return a String representation. */ public static String toString(JavaExpression javaExpression) { return toString(javaExpression, 0).toString(); } /* * Not intended to be instantiated. */ private Stringifier() { // Constructor declared private to prevent instantiation. } private static StringBuilder toString(JavaTypeName javaTypeName, int indent) { StringBuilder sb = new StringBuilder(); emitLine(sb, indent, "Type Name: " + javaTypeName.getName()); return sb; } /** * Helper method for the other toString() * @param javaExpression * @param indent the current indent level. * @return StringBuilder the StringBuilder to which to add the representation of the current expression */ private static StringBuilder toString(JavaExpression javaExpression, int indent) { StringBuilder sb = new StringBuilder(); if (javaExpression instanceof Assignment) { Assignment assignment = (Assignment)javaExpression; emitLine(sb, indent, "Assignment: "); sb.append(toString(assignment.getLeftHandSide(), indent + 1)); sb.append(toString(assignment.getValue(), indent + 1)); } else if (javaExpression instanceof CastExpression) { CastExpression castExpression = (CastExpression)javaExpression; emitLine(sb, indent, "Cast Expression: "); sb.append(toString(castExpression.getCastType(), indent + 1)); sb.append(toString(castExpression.getExpressionToCast(), indent + 1)); } else if (javaExpression instanceof ArrayCreationExpression) { ArrayCreationExpression arrayCreation = (ArrayCreationExpression)javaExpression; emitLine(sb, indent, "Array Creation Expression: "); sb.append(toString(arrayCreation.getArrayElementTypeName(), indent + 1)); for (int i = 0, nElements = arrayCreation.getNElementValues(); i < nElements; i++) { sb.append(toString(arrayCreation.getElementValue(i), indent + 1)); } } else if (javaExpression instanceof ClassInstanceCreationExpression) { ClassInstanceCreationExpression classInstanceCreationExpression = (ClassInstanceCreationExpression)javaExpression; emitLine(sb, indent, "Class Instance Creation Expression: "); sb.append(toString(classInstanceCreationExpression.getClassName(), indent + 1)); for (int i = 0, nArgs = classInstanceCreationExpression.getNArgs(); i < nArgs; i++) { sb.append(toString(classInstanceCreationExpression.getArg(i), indent + 1)); } } else if (javaExpression instanceof OperatorExpression) { OperatorExpression operatorExpression = (OperatorExpression)javaExpression; emitLine(sb, indent, "Operator Expression: "); emitLine(sb, indent + 1, operatorExpression.getJavaOperator().getSymbol()); sb.append(toString(operatorExpression.getArgument(0), indent + 1)); if (!(operatorExpression instanceof OperatorExpression.Unary)) { sb.append(toString(operatorExpression.getArgument(1), indent + 1)); } if (operatorExpression instanceof OperatorExpression.Ternary) { sb.append(toString(operatorExpression.getArgument(2), indent + 1)); } } else if (javaExpression instanceof InstanceOf) { InstanceOf instanceOf = (InstanceOf)javaExpression; emitLine(sb, indent, "Instance of: "); sb.append(toString(instanceOf.getJavaExpression(), indent + 1)); sb.append(toString(instanceOf.getReferenceType(), indent + 1)); } else if (javaExpression instanceof MethodInvocation) { MethodInvocation methodInvocation = (MethodInvocation)javaExpression; emitLine(sb, indent, "Method Invocation: "); if (methodInvocation instanceof MethodInvocation.Instance) { JavaExpression invocationTarget = ((MethodInvocation.Instance)methodInvocation).getInvocationTarget(); if (invocationTarget != null) { sb.append(toString(invocationTarget, indent + 1)); } } else { JavaTypeName invocationClass = ((MethodInvocation.Static)methodInvocation).getInvocationClass(); sb.append(toString(invocationClass, indent + 1)); } emitLine(sb, indent + 1, methodInvocation.getMethodName()); for (int i = 0, nArgs = methodInvocation.getNArgs(); i < nArgs; i++) { sb.append(toString(methodInvocation.getArg(i), indent + 1)); } } else if (javaExpression instanceof LiteralWrapper) { LiteralWrapper literalWrapper = (LiteralWrapper)javaExpression; emitLine(sb, indent, "Literal: " + literalWrapper.getLiteralObject()); } else if (javaExpression instanceof ArrayAccess) { ArrayAccess arrayAccess = (ArrayAccess)javaExpression; emitLine(sb, indent, "Array Access: "); sb.append(toString(arrayAccess.getArrayReference(), indent + 1)); sb.append(toString(arrayAccess.getArrayIndex(), indent + 1)); } else if (javaExpression instanceof ArrayLength) { ArrayLength arrayLength = (ArrayLength)javaExpression; emitLine(sb, indent, "Array Length: "); sb.append(toString(arrayLength.getArrayReference(), indent + 1)); } else if (javaExpression instanceof JavaField) { JavaField javaField = (JavaField)javaExpression; emitLine(sb, indent, "Field: " + javaField.getFieldName() + ". Type: " + javaField.getFieldType().getName()); } else if (javaExpression instanceof LocalVariable) { LocalVariable localVariable = (LocalVariable)javaExpression; emitLine(sb, indent, "Local var: " + localVariable.getName()); } else if (javaExpression instanceof MethodVariable) { MethodVariable methodVariable = (MethodVariable)javaExpression; emitLine(sb, indent, "Method var: " + methodVariable.getName()); } else if (javaExpression instanceof LocalName) { LocalName localName = (LocalName)javaExpression; emitLine(sb, indent, "Local name: " + localName.getName()); } else { emitLine(sb, indent, "(Unrecognized expression): " + javaExpression); } return sb; } /** * Emit an indent. * @param sb the StringBuilder to which to add an indent. * @param indent the number of indents to add. * @return StringBuilder sb, returned for convenience. */ private static StringBuilder emitIndent(StringBuilder sb, int indent) { for (int i = 0; i < indent; i++) { sb.append(INDENT); } return sb; } /** * Emit an indent, some text, and an EOL. * @param sb the StringBuilder to which to add an indent. * @param text the text to add. * @param indent the number of indents to add. */ private static void emitLine(StringBuilder sb, int indent,String text) { emitIndent(sb, indent); sb.append(text + EOL); } } /** * An assignment expression. * @author Edward Lam */ public static class Assignment extends JavaExpression { /** The left hand side of the assignment. */ private final Nameable leftHandSide; /** The value to which the left hand side is being assigned. */ private final JavaExpression value; /** * Constructor for an Assignment. * @param leftHandSide the left hand side of the assingment. * @param value the value to which the left hand side is being assigned. */ public Assignment(Nameable leftHandSide, JavaExpression value) { Assert.isNotNull(leftHandSide); Assert.isNotNull(value); this.leftHandSide = leftHandSide; this.value = value; } /** * Get the left hand side of the assignment. * @return Nameable */ public Nameable getLeftHandSide() { return leftHandSide; } /** * Get the value being assigned. * @return JavaExpression */ public JavaExpression getValue() { return value; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitAssignmentExpression(this, arg); } } /** * A cast expression. * @author Edward Lam */ public static class CastExpression extends JavaExpression { /** The expression being cast. */ private final JavaExpression expressionToCast; /** The type to which the expression is being cast. */ private final JavaTypeName castType; /** * Constructor for a CastExpression. * @param castType the type to which the expression is being cast. * @param expressionToCast the expression being cast. */ public CastExpression(JavaTypeName castType, JavaExpression expressionToCast) { Assert.isNotNull(castType); Assert.isNotNull(expressionToCast); this.castType = castType; this.expressionToCast = expressionToCast; } /** * Get the type to which the expression is being cast. * @return JavaTypeName */ public JavaTypeName getCastType() { return castType; } /** * Get the expression being cast. * @return JavaExpression */ public JavaExpression getExpressionToCast() { return expressionToCast; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitCastExpression(this, arg); } } /** * Used to create and initialize a 1-dimensional array. See ClassInstanceCreationExpression for creating (but not initializing) * multi-dimensional arrays. For example, this class can be used to generate expressions such as: * new String[] {"this", "is", "an", "example"} * * @author Bo Ilic */ public static final class ArrayCreationExpression extends JavaExpression { /** element type of the array. For example, for new String[] {"this", "is", "an", "example"} this is String. */ private final JavaTypeName arrayElementTypeName; /** * array element values. For example, for new String[] {"this", "is", "an", "example"} this is * ["this", "is", "an", "example"]. These are assumed to have the *same* type as the arrayElementTypeName. */ private final JavaExpression[] elementValues; public ArrayCreationExpression(JavaTypeName arrayElementTypeName, JavaExpression[] elementValues) { Assert.isNotNull(arrayElementTypeName); Assert.isNotNull(elementValues); this.arrayElementTypeName = arrayElementTypeName; this.elementValues = elementValues.clone(); assert (!checkForNullElements()) : "It is not valid to create an ArrayCreationExpression with a null value for an array element."; } /** * @return true if any of the element values are null. */ private boolean checkForNullElements () { for (int i = 0, n = elementValues.length; i < n; ++i) { if (elementValues[i] == null) { return true; } } return false; } /** * @return JavaTypeName element type of the array. For example, for new String[] {"this", "is", "an", "example"} this is String. */ public JavaTypeName getArrayElementTypeName() { return arrayElementTypeName; } public int getNElementValues() { return elementValues.length; } public JavaExpression getElementValue(int n) { return elementValues[n]; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitArrayCreationExpression(this, arg); } } /** * An expression to create an instance of a class. * @author Edward Lam */ public static class ClassInstanceCreationExpression extends JavaExpression { /** The type of the class instance to create. */ private final JavaTypeName className; /** Arguments to the class instance constructor. For array types, this will be the sizes of the dimensions to instantiate. */ private final JavaExpression[] args; /** The types of the parameters to the constructor. */ private final JavaTypeName[] paramTypes; /** * Constructor for a no-argument ClassInstanceCreationExpression. * @param className the type of the class instance to create. */ public ClassInstanceCreationExpression(JavaTypeName className) { this(className, EMPTY_JAVA_EXPRESSION_ARRAY, EMPTY_TYPE_NAME_ARRAY); } /** * Constructor for an array creation expression. * @param arrayClassName the JavaTypeName for the array type. * @param arrayDimensionSizes the sizes of the dimensions to instantiate. */ public ClassInstanceCreationExpression(JavaTypeName arrayClassName, int[] arrayDimensionSizes) { this(arrayClassName, getWrappedInts(arrayDimensionSizes), getIntTypeArray(arrayDimensionSizes.length)); } /** * Convert an array of int to an array of LiteralWrapper, each of which wraps the corresponding int. * @param ints the array of ints to wrap. * @return LiteralWrapper[] the array of wrapped ints. */ private static LiteralWrapper[] getWrappedInts(int[] ints) { LiteralWrapper[] intWrapperArray = new LiteralWrapper[ints.length]; for (int i = 0; i < ints.length; i++) { intWrapperArray[i] = LiteralWrapper.make(Integer.valueOf(ints[i])); } return intWrapperArray; } /** * Get an array of JavaTypeName, filled with JavaTypeName.INT. * @param arraySize the size of the array. * @return JavaTypeName[] the array. */ private static JavaTypeName[] getIntTypeArray(int arraySize) { JavaTypeName[] intTypeArray = new JavaTypeName[arraySize]; Arrays.fill(intTypeArray, JavaTypeName.INT); return intTypeArray; } /** * Constructor for a single-argument ClassInstanceCreationExpression. * @param className the type of the class instance to create. * @param arg the single argument to the class instance constructor. * @param paramType the type of the single parameter to the class instance constructor. */ public ClassInstanceCreationExpression(JavaTypeName className, JavaExpression arg, JavaTypeName paramType) { this(className, new JavaExpression[] {arg}, new JavaTypeName[] {paramType}); } /** * Constructor for a ClassInstanceCreationExpression. * @param className the type of the class instance to create. * @param args the arguments to the class instance constructor. * @param paramTypes the types of the parameters to the class instance constructor. */ public ClassInstanceCreationExpression(JavaTypeName className, JavaExpression[] args, JavaTypeName[] paramTypes) { Assert.isNotNull(className); Assert.isNotNull(args); Assert.isNotNull(paramTypes); if (args.length != paramTypes.length) { throw new IllegalArgumentException("arg array must be the same length as arg types array."); } this.className = className; this.args = args; this.paramTypes = paramTypes; } /** * Get the type of the class instance to create. * @return JavaTypeName */ public JavaTypeName getClassName() { return className; } /** * Return whether this is an array instance creation expression. * @return boolean */ public boolean isArrayCreationExpression() { return className instanceof JavaTypeName.Reference.Array; } /** * @return the number of arguments to be passed to the constructor. For array types, this will be the number of dimensions of the array * that are being initialized by the constructor. This is greater than or equal to 1 and less than or equal to the number of dimensions * of the array. For example for, new String[3][5][] this will be 2. For new String[2][5][4] this will be 3. In both cases the * array being initialized is a 3-dimensional String array. */ public int getNArgs() { return args.length; } /** * @param n zero-indexed argument number of the constructor, or zero-indexed dimension of the array for array types. * @return The expression for the nth constructor argument. For arrays, this is the int-valued expression setting the size of the nth dimension. */ public JavaExpression getArg(int n) { return args[n]; } /** * @param n zero-indexed argument number of the constructor, or zero-indexed dimension of the array for array types. * @return The type of the nth constructor argument. For arrays, this is the type of the dimension-size setting expression for the * nth dimension i.e. it is always int. */ public JavaTypeName getParamType(int n) { return paramTypes[n]; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitClassInstanceCreationExpression(this, arg); } } /** * A JavaExpression that uses symbolic operators. * ConditionalExpression in the Java grammar. * * warning: do not add ++ or -- as subtypes of OperatorExpression. Because of their side effects, they are not * true operators and should not be treated as subclasses of JavaExpression.OperatorExpression. For example, the * argument expression of ++ cannot be an arbitrary expression of the correct type, it must be a variable name. * * @author Edward Lam */ public abstract static class OperatorExpression extends JavaExpression { /** The operator for this expression. */ private final JavaOperator javaOp; /** The expression(s) operated upon by the operator. */ private final JavaExpression[] args; /** * Constructor for an OperatorExpression. * @param javaOp the operator for this expression. * @param args the expression(s) operated upon by the operator. */ OperatorExpression(JavaOperator javaOp, JavaExpression[] args) { Assert.isNotNull(javaOp); Assert.isNotNull(args); this.javaOp = javaOp; this.args = args; } /** * Get the operator. * @return JavaOperator */ public JavaOperator getJavaOperator() { return javaOp; } /** * Get the nth argument to the operator in this expression. * @param n * @return JavaExpression */ public JavaExpression getArgument(int n) { return args[n]; } /** * An OperatorExpression with a single expression argument. * @author Edward Lam */ public static class Unary extends OperatorExpression { /** * Constructor for a unary operator expression. * @param javaOp the operator. * @param arg the argument */ public Unary(JavaOperator javaOp, JavaExpression arg) { super(javaOp, new JavaExpression[] {arg}); } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitUnaryOperatorExpression(this, arg); } } /** * An OperatorExpression with two expression arguments. * The operator should be infix. * @author Edward Lam */ public static class Binary extends OperatorExpression { /** * Constructor for a binary operator expression * @param javaOp the operator. * @param args the arguments */ public Binary(JavaOperator javaOp, JavaExpression[] args) { super(javaOp, args); } /** * Constructor for a binary operator expression * @param javaOp the operator. * @param arg1 the first argument * @param arg2 the second argument */ public Binary(JavaOperator javaOp, JavaExpression arg1, JavaExpression arg2) { super(javaOp, new JavaExpression[] {arg1, arg2}); } /** * For example, the notComposed operator for LESS_THAN_INT is GREATER_THAN_EQUALS_INT since * not (x < y) is equals to x >= y for all x, y that are ints. * * It is not the case that the notComposed operator for LESS_THAN_DOUBLE is GREATER_THAN_DOUBLE since * the above equation does not hold for Double.NAN. * * @return the operator expression where the operator is replaces by the not composed operator, or * null if there is no such operator. */ public OperatorExpression.Binary getNotComposedOperatorExpr() { JavaOperator notComposedOperator = super.javaOp.getNotComposedOperator(); if (notComposedOperator != null) { return new OperatorExpression.Binary(notComposedOperator, super.args); } return null; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitBinaryOperatorExpression(this, arg); } } /** * An OperatorExpression representing the ?: ternary operator. * @author Edward Lam */ public static class Ternary extends OperatorExpression { /** * Constructor for a ternary operator expression * @param arg1 the first argument. Must have a primitive boolean type. * @param arg2 the second argument. arg2 must have exactly the same (static) type as arg3. * The various conversions as described in the Java language specification section 15.25 are * not supported. * @param arg3 the third argument */ public Ternary(JavaExpression arg1, JavaExpression arg2, JavaExpression arg3) { super(JavaOperator.TERNARY, new JavaExpression[] {arg1, arg2, arg3}); } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitTernaryOperatorExpression(this, arg); } } } /** * A conditional expression for the "instanceof" operator. * @author Edward Lam */ public static class InstanceOf extends JavaExpression { /** The expression to test. */ private final JavaExpression javaExpression; /** The type for which the expression will be tested. */ private final JavaTypeName referenceType; /** * Constructor for an InstanceOf expression * @param javaExpression the expression to test. * @param referenceType the type for which the expression will be tested. */ public InstanceOf(JavaExpression javaExpression, JavaTypeName referenceType) { Assert.isNotNull(javaExpression); Assert.isNotNull(referenceType); this.javaExpression = javaExpression; this.referenceType = referenceType; } /** * Get the expression to test. * @return JavaExpression */ public JavaExpression getJavaExpression() { return javaExpression; } /** * Get the type for which the expression will be tested. * @return JavaTypeName.ReferenceType */ public JavaTypeName getReferenceType() { return referenceType; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitInstanceOfExpression(this, arg); } } /** * A method invocation. * @author Edward Lam */ public static abstract class MethodInvocation extends JavaExpression { /** The name of the method being invoked. */ private final String methodName; /** The arguments to the method invocation. */ private final JavaExpression[] args; /** The types in the method signature. */ private final JavaTypeName[] paramTypes; /** The return type of the method. */ private final JavaTypeName returnType; /** * Represents a non-static method invocation within an expression. * * For example, the calls to myMethod below. * this.myMethod(1, "abc") * getFoo("abc").myMethod(1, "abc") * super.myMethod(1, "abc") * * Note: we do not allow invocation of static methods through an object reference. * * @author Bo Ilic */ public static final class Instance extends MethodInvocation { /** * The target of the invocation (ie. the object being invoked). * Use null for the current object and invocationType != SPECIAL to invoke via the "this" reference. * Use null for the current object and invocationType == SPECIAL to invoke via the "super" reference. */ private final JavaExpression invocationTarget; /** The invocation type. */ private final InvocationType invocationType; /** The class/interface declaring the method being invoked. This may be null. */ private final JavaTypeName declaringClass; /** * Constructor for a method invocation on an object reference. Cannot be used for static methods. * @param invocationTarget the target of the invocation (ie. the object being invoked), or null for the current object. * (i.e. make the call using the "this" pointer if invocation type != SPECIAL, and the "super" pointer if invocation type == SPECIAL) * @param methodName the name of the method being invoked. * @param returnType the return type of the method. * @param invocationType the method invocation type. */ public Instance(JavaExpression invocationTarget, String methodName, JavaTypeName returnType, InvocationType invocationType) { this(invocationTarget, methodName, EMPTY_JAVA_EXPRESSION_ARRAY, EMPTY_TYPE_NAME_ARRAY, returnType, invocationType); } /** * Constructor for a single argument method invocation on an object reference. Cannot be used for static methods. * @param invocationTarget the target of the invocation (ie. the object being invoked), or null for the current object. * (i.e. make the call using the "this" pointer if invocation type != SPECIAL, and the "super" pointer if invocation type == SPECIAL) * @param methodName the name of the method being invoked. * @param arg the argument to the method invocation * @param paramType the types of the param in the method signature * @param returnType the return type of the method. * @param invocationType the method invocation type. */ public Instance(JavaExpression invocationTarget, String methodName, JavaExpression arg, JavaTypeName paramType, JavaTypeName returnType, InvocationType invocationType) { this(invocationTarget, methodName, new JavaExpression[]{arg}, new JavaTypeName[]{paramType}, returnType, invocationType); } /** * Most general constructor for a method invocation on an object reference. Cannot be used for static methods. * @param invocationTarget the target of the invocation (ie. the object being invoked), or null for the current object. * (i.e. make the call using the "this" pointer if invocation type != SPECIAL, and the "super" pointer if invocation type == SPECIAL) * @param methodName the name of the method being invoked. * @param args the arguments to the method invocation * @param paramTypes the types of the params in the method signature * @param returnType the return type of the method * @param invocationType the method invocation type. */ public Instance(JavaExpression invocationTarget, String methodName, JavaExpression[] args, JavaTypeName[] paramTypes, JavaTypeName returnType, InvocationType invocationType) { this (invocationTarget, methodName, null, args, paramTypes, returnType, invocationType); } /** * Most general constructor for a method invocation on an object reference. Cannot be used for static methods. * @param invocationTarget the target of the invocation (ie. the object being invoked), or null for the current object. * (i.e. make the call using the "this" pointer if invocation type != SPECIAL, and the "super" pointer if invocation type == SPECIAL) * @param methodName the name of the method being invoked. * @param declaringClass * @param args the arguments to the method invocation * @param paramTypes the types of the params in the method signature * @param returnType the return type of the method * @param invocationType the method invocation type. */ public Instance(JavaExpression invocationTarget, String methodName, JavaTypeName declaringClass, JavaExpression[] args, JavaTypeName[] paramTypes, JavaTypeName returnType, InvocationType invocationType) { super(methodName, args, paramTypes, returnType); if (invocationType == InvocationType.STATIC) { throw new IllegalArgumentException("This MethodInvocation constructor is for non-static methods only."); } this.declaringClass = declaringClass; this.invocationTarget = invocationTarget; this.invocationType = invocationType; } /** * Get the target of the invocation i.e. the object reference that this method is invoked on. If this is null, * then use the "this" reference as the invocation target. * @return JavaExpression */ public JavaExpression getInvocationTarget() { return invocationTarget; } @Override public InvocationType getInvocationType() { return invocationType; } public JavaTypeName getDeclaringClass () { return declaringClass; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitInstanceMethodInvocationExpression(this, arg); } } /** * Represents the invocation of a static method within an expression. * @author Bo Ilic */ public static final class Static extends MethodInvocation { /** * Name of the class that the static method is to be invoked from. This could be a subclass of the class in which the * static method is defined, and it is sometimes necessary to do this. For example, if package scope class A defines * a static public f, and public class B extends A, then B.f in a different package will not result in a compilation * error but A.f will. */ private final JavaTypeName invocationClass; /** * Constructor for invoking a static method via a class reference for a single argument method (e.g. Integer.valueOf("123")). * @param invocationClass name of the class that the static method is to be invoked from. * @param methodName the name of the method being invoked. * @param arg the argument to the method invocation * @param paramType the types of the params in the method signature * @param returnType the return type of the method */ public Static(JavaTypeName invocationClass, String methodName, JavaExpression arg, JavaTypeName paramType, JavaTypeName returnType) { this(invocationClass, methodName, new JavaExpression[] {arg}, new JavaTypeName[] {paramType}, returnType); } /** * Most general constructor for invoking a static method via a class reference (e.g. Integer.valueOf("123")). * @param invocationClass name of the class that the static method is to be invoked from. * @param methodName the name of the method being invoked. * @param args the arguments to the method invocation * @param paramTypes the types of the params in the method signature * @param returnType the return type of the method */ public Static(JavaTypeName invocationClass, String methodName, JavaExpression[] args, JavaTypeName[] paramTypes, JavaTypeName returnType) { super(methodName, args, paramTypes, returnType); Assert.isNotNull(invocationClass); this.invocationClass = invocationClass; } /** * Name of the class that the static method is to be invoked from. This could be a subclass of the class in which the * static method is defined, and it is sometimes necessary to do this. For example, if package scope class A defines * a static public f, and public class B extends A, then B.f in a different package will not result in a compilation * error but A.f will. * * @return name of the class that the static method is to be invoked from. Will be non-null. */ public JavaTypeName getInvocationClass() { return invocationClass; } @Override public InvocationType getInvocationType() { return InvocationType.STATIC; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitStaticMethodInvocationExpression(this, arg); } } /** * Invocation type enum. * @author Edward Lam */ public static class InvocationType { /** The name of the invocation type. */ private final String typeName; /** * Constructor for an invocation type * @param typeName the name of the invocation. */ private InvocationType(String typeName) { Assert.isNotNull(typeName); this.typeName = typeName; } /** * {@inheritDoc} */ @Override public String toString() { return typeName; } /** Invocation with dispatch based on object's runtime type. */ public static final InvocationType VIRTUAL = new InvocationType("VIRTUAL"); /** Invocation of instance initializer, private, superclass methods. */ public static final InvocationType SPECIAL = new InvocationType("SPECIAL"); /** Invocation of class methods. */ public static final InvocationType STATIC = new InvocationType("STATIC"); /** Invocation of interface methods. */ public static final InvocationType INTERFACE = new InvocationType("INTERFACE"); } /** * Constructor for a method invocation. * @param methodName the name of the method being invoked. * @param args the arguments to the method invocation * @param paramTypes the types of the params in the method signature * @param returnType the return type of the method */ MethodInvocation(String methodName, JavaExpression[] args, JavaTypeName[] paramTypes, JavaTypeName returnType) { Assert.isNotNull(methodName); Assert.isNotNull(args); Assert.isNotNull(paramTypes); Assert.isNotNull(returnType); if (args.length != paramTypes.length) { throw new IllegalArgumentException("arg array must be the same length as arg types array."); } this.methodName = methodName; this.args = args; this.paramTypes = paramTypes; this.returnType = returnType; } /** * Get the name of the method being invoked. * @return String */ public String getMethodName() { return methodName; } /** * @return the number of arguments of the method. */ public int getNArgs() { return args.length; } /** * @param n * @return the nth argument to the invocation of the method. */ public JavaExpression getArg(int n) { return args[n]; } /** * @param n * @return the type of the nth argument of the method. */ public JavaTypeName getParamType(int n) { return paramTypes[n]; } /** * @return JavaTypeName the return type of the method */ public JavaTypeName getReturnType() { return returnType; } /** * @return InvocationType the invocation type */ abstract public InvocationType getInvocationType(); /** * @return the method descriptor, as specified in the Java Virtual Machine Specification section 4.3. */ public String getJVMMethodDescriptor() { StringBuilder sb = new StringBuilder("("); for (int i = 0, nArgs = args.length; i < nArgs; ++i) { sb.append(paramTypes[i].getJVMDescriptor()); } sb.append(')').append(returnType.getJVMDescriptor()); return sb.toString(); } } /** * An expression that's not composed of other expressions. * "Primary" in the grammar. * @author Edward Lam */ static abstract class Primary extends JavaExpression { // Empty base class for the set of expressions that aren't composed of other expressions. } /** * A primary with a name. * This can be invoked or assigned (ie. it's not a literal). * "Name" in the grammar. * @author Edward Lam */ public static abstract class Nameable extends Primary { // Empty base class for primary expressions that are nameable. } /** * A wrapper around a literal object value. * The object is one of: Integer, Double, String, Character, Boolean, Byte, Short, Float, Long, null; * @author Edward Lam */ public static final class LiteralWrapper extends Primary { /** The "null" literal. */ public static final LiteralWrapper NULL = new LiteralWrapper(null); /** The "true" literal. */ public static final LiteralWrapper TRUE = new LiteralWrapper(Boolean.TRUE); /** The "false" literal. */ public static final LiteralWrapper FALSE = new LiteralWrapper(Boolean.FALSE); /** The wrapped literal object. */ private final Object literalObject; /** * Private constructor for a LiteralWrapper. * @param literalObject the object to wrap. */ private LiteralWrapper(final Object literalObject) { this.literalObject = literalObject; } /** * Factory method for obtaining a LiteralWrapper. If the literal object specified is * null, {@link Boolean#TRUE} or {@link Boolean#FALSE}, then the constant * {@link #NULL}, {@link #TRUE} or {@link #FALSE} is respectively returned. * * @param literalObject the object to wrap. * @return an instance of this class. */ public static LiteralWrapper make(final Object literalObject) { if (literalObject == null) { return LiteralWrapper.NULL; } else if (Boolean.TRUE.equals(literalObject)) { return LiteralWrapper.TRUE; } else if (Boolean.FALSE.equals(literalObject)) { return LiteralWrapper.FALSE; } else { return new LiteralWrapper(literalObject); } } /** * Get the wrapped literal object. * @return Object the wrapped literal. Null for the "null" literal. */ public Object getLiteralObject() { return literalObject; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitLiteralWrapperExpression(this, arg); } } /** * A Java class literal. For example: * <ul> * <li> void.class * <li> int.class * <li> java.lang.String.class * <li> Object[].class * <li> short[][].class * </ul> * @author Joseph Wong */ public static final class ClassLiteral extends Primary { /** * The referent type, i.e. the Java type R where this literal corresponds to R.class. * It may be a primitive type, a reference type, or an array type. */ private final JavaTypeName referentType; /** * Constructor for a ClassLiteral expression * @param referentType the Java type R where this literal corresponds to R.class. */ public ClassLiteral(JavaTypeName referentType) { Assert.isNotNull(referentType); this.referentType = referentType; } /** * Get the Java type R where this literal corresponds to R.class. * @return JavaTypeName */ public JavaTypeName getReferentType() { return referentType; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitClassLiteralExpression(this, arg); } } /** * A reference to a field, either static, non-static or the "this" field within an expression. * Some examples of cases handled: * MyClass.foo //reference to the static field foo of class MyClass * getFoo(1, "abc").foo //reference to the non-static field foo through the instance expression getFoo(1, "abc") * this.foo //reference to foo in the current instance * this //reference to the this field itself * * @author Edward Lam */ public static abstract class JavaField extends Nameable { /** The name of the field. */ private final String fieldName; /** The type of the field. */ private final JavaTypeName fieldType; /** * The special "this" field. * * @author Bo Ilic */ public static final class This extends JavaField { public This(JavaTypeName fieldType) { super("this", fieldType); } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitThisFieldExpression(this, arg); } } /** * Reference to a static field such as Foo.foo for the static field foo defined in class Foo. * * @author Bo Ilic */ public static final class Static extends JavaField { /** * Name of the class that the static field is to be invoked from. This could be a subclass of the class in which the * static field is defined, and it is sometimes necessary to do this. For example, if package scope class A defines * a static public f, and public class B extends A, then B.f in a different package will not result in a compilation * error but A.f will. */ private final JavaTypeName invocationClass; /** * Constructor for a Java static (class) field. * @param invocationClass name of the class that the static field is to be invoked from. * @param fieldName the name of the field. * @param fieldType the type of the field. */ public Static(JavaTypeName invocationClass, String fieldName, JavaTypeName fieldType) { super(fieldName, fieldType); Assert.isNotNullArgument(invocationClass); this.invocationClass = invocationClass; } /** * Name of the class that the static field is to be invoked from. This could be a subclass of the class in which the * static field is defined, and it is sometimes necessary to do this. For example, if package scope class A defines * a static public f, and public class B extends A, then B.f in a different package will not result in a compilation * error but A.f will. * @return name of the class that the static field is to be invoked from. Will be non-null. */ public JavaTypeName getInvocationClass() { return invocationClass; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitStaticFieldExpression(this, arg); } } /** * Reference to a field through an instance reference. This could be an expression such as * The instance reference could be an expression such as getFoo(1, "abc") and then used such as getFoo(1, "abc").foo * or the instance expression could simply be this e.g. this.foo. * This class cannot be used for static fields. * * @author Bo Ilic */ public static final class Instance extends JavaField { /** The instance in which the field exists. null means to use "this" as the instance expression. */ private final JavaExpression instance; /** The QualifiedName of the CAL supercombinator which this field is * an instance of. May be null. */ private final QualifiedName originatingCALSupercombinator; public Instance(JavaExpression instance, String fieldName, JavaTypeName fieldType) { super(fieldName, fieldType); this.instance = instance; this.originatingCALSupercombinator = null; } public Instance(JavaExpression instance, String fieldName, JavaTypeName fieldType, QualifiedName originatingCALSupercombinator) { super(fieldName, fieldType); this.instance = instance; this.originatingCALSupercombinator = originatingCALSupercombinator; } /** * Get the instance in which the field exists. * @return JavaExpression the instance in which the field exists. Null for fields in the current instance (i.e. use "this" * as the Java expression. */ public JavaExpression getInstance() { return this.instance; } /** * @return the QualifiedName of the CAL supercombinator which this field is * an instance of. May be null. */ public QualifiedName getOriginatingCALSupercombinator () { return this.originatingCALSupercombinator; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitInstanceFieldExpression(this, arg); } } /** * Constructor for a JavaField. * @param fieldName the name of the field. * @param fieldType the type of the field. */ JavaField(String fieldName, JavaTypeName fieldType) { Assert.isNotNull(fieldName, "Unable to create JavaField: fieldName == null"); Assert.isNotNull(fieldType, "Unable to create JavaField: fieldType == null"); this.fieldName = fieldName; this.fieldType = fieldType; } /** * Get the name of the field. * @return String */ public String getFieldName() { return fieldName; } /** * Get the type of the field * @return JavaTypeName */ public JavaTypeName getFieldType() { return fieldType; } } /** * A local name. * This could be a local variable, a method variable, or a field in the current instance, depending on the context. * @author Edward Lam */ public static class LocalName extends Nameable { /** The name of the variable. */ private final String name; /** The declared type of the variable. */ private final JavaTypeName typeName; /** * Constructor for this variable. * @param name the name of the variable. * @param typeName the declared type of the variable. */ public LocalName(String name, JavaTypeName typeName) { Assert.isNotNull(name); Assert.isNotNull(typeName); this.name = name; this.typeName = typeName; } /** * Get the name of the variable. * @return String */ public String getName() { return name; } /** * Get the declared type of the variable. * @return JavaTypeName */ public JavaTypeName getTypeName() { return typeName; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitLocalNameExpression(this, arg); } } /** * A local variable. * @author Edward Lam */ public static class LocalVariable extends Nameable { /** The name of the variable. */ private final String name; /** The declared type of the variable. */ private final JavaTypeName typeName; /** * Constructor for a local variable. * @param name the name of the variable. * @param typeName the declared type of the variable. */ public LocalVariable(String name, JavaTypeName typeName) { Assert.isNotNull(name); Assert.isNotNull(typeName); this.name = name; this.typeName = typeName; } /** * Get the name of the variable. * @return String */ public String getName() { return name; } /** * Get the declared type of the variable. * @return JavaTypeName */ public JavaTypeName getTypeName() { return typeName; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitLocalVariableExpression(this, arg); } } /** * A variable introduced as a parameter to a method. * @author Edward Lam */ public static class MethodVariable extends Nameable { /** The name of the variable. */ private final String name; /** * Constructor for a method variable. * @param name */ public MethodVariable(String name) { Assert.isNotNull(name); this.name = name; } /** * Get the name of the variable. * @return String */ public String getName() { return name; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitMethodVariableExpression(this, arg); } } /** * An array access (i.e. subscripting an array). * * @author Edward Lam */ public static final class ArrayAccess extends Nameable { /** The array valued expression to subscript. */ private final JavaExpression arrayReference; /** The index into the array. */ private final JavaExpression arrayIndex; /** * Constructor for an array access. * @param arrayReference the array. * @param arrayIndex an expression that evaluates to an int valued index. */ public ArrayAccess(JavaExpression arrayReference, JavaExpression arrayIndex) { if (arrayReference == null || arrayIndex == null) { throw new IllegalArgumentException ("Unable to create ArrayAccess: null argument."); } this.arrayReference = arrayReference; this.arrayIndex = arrayIndex; } /** * @return Nameable */ public JavaExpression getArrayReference() { return arrayReference; } /** * @return JavaExpression an expression that evaluates to an int valued index */ public JavaExpression getArrayIndex() { return arrayIndex; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitArrayAccessExpression(this, arg); } } /** * Models the .length "field" on arrays. * * @author Bo Ilic */ public static final class ArrayLength extends JavaExpression { /** The array valued expression to take the length of. */ private final JavaExpression arrayReference; /** * @param arrayReference the array. */ public ArrayLength(JavaExpression arrayReference) { if (arrayReference == null) { throw new IllegalArgumentException (); } this.arrayReference = arrayReference; } /** * @return Nameable */ public JavaExpression getArrayReference() { return arrayReference; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitArrayLengthExpression(this, arg); } } /** * This is used as a place holder for an expresson that will * be determined at a later point in time. * @author rcypher */ public static final class PlaceHolder extends JavaExpression { JavaExpression actualExpression; String name; public PlaceHolder (String name) { this.name = name; } public JavaExpression getActualExpression () { return actualExpression; } public void setActualExpression (JavaExpression actualExpression) { this.actualExpression = actualExpression; } /** * {@inheritDoc} */ @Override public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitPlaceHolderExpression(this, arg); } } /** * @return representation of this JavaExpression for debug purposes only. */ @Override public String toString() { return JavaSourceGenerator.toDebugString(this); } }