/*
* 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.
*/
/*
* JavaTypeName.java
* Created: Dec 17, 2004
* By: Bo Ilic
*/
package org.openquark.cal.internal.javamodel;
import java.io.PrintStream;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.internal.runtime.lecc.RTValue;
import org.openquark.cal.machine.CALExecutor;
import org.openquark.cal.runtime.CALExecutorException;
import org.openquark.cal.runtime.CalFunction;
import org.openquark.cal.runtime.CalValue;
import org.openquark.cal.runtime.DebugSupport;
import org.openquark.cal.runtime.ErrorInfo;
import org.openquark.cal.services.Assert;
import org.openquark.cal.util.EquivalenceRelation;
/**
* A representation of a Java class / type. This is similar to the java.lang.Class type except that the class
* represented does not need to exist.
*
* @author Edward Lam
*/
abstract public class JavaTypeName {
public static final int VOID_TAG = 0;
public static final int BOOLEAN_TAG = 1;
public static final int BYTE_TAG = 2;
public static final int SHORT_TAG = 3;
public static final int CHAR_TAG = 4;
public static final int INT_TAG = 5;
public static final int LONG_TAG = 6;
public static final int DOUBLE_TAG = 7;
public static final int FLOAT_TAG = 8;
public static final int ARRAY_TAG = 9;
public static final int OBJECT_TAG = 10;
/**
* It turns out that benchmarking shows that calling JavaTypeName.getJVMInternalName and getJVMDescriptor was expensive
* when generating bytecodes using the ASM bytecode generator.
* We reduce this cost by
* a) caching the results of these functions in JavaTypeName
* b) limiting the number of different JavaTypeName objects created- many are for the same types shown below.
* Use these static constants above wherever possible.
*
* Note that some of the static constants such as RTFullApp* are not used in the Java code. The purpose of adding them here
* is to add them to the cache since they are used many times over in the generated lecc Java code.
*
*/
private static final Map<String, JavaTypeName> cachedTypes = new HashMap<String, JavaTypeName>();
//primitive Java types
public static final JavaTypeName VOID = makeCached(void.class);
public static final JavaTypeName BOOLEAN = makeCached(boolean.class);
public static final JavaTypeName BYTE = makeCached(byte.class);
public static final JavaTypeName SHORT = makeCached(short.class);
public static final JavaTypeName CHAR = makeCached(char.class);
public static final JavaTypeName INT = makeCached(int.class);
public static final JavaTypeName LONG = makeCached(long.class);
public static final JavaTypeName DOUBLE = makeCached(double.class);
public static final JavaTypeName FLOAT = makeCached(float.class);
//primitive one dimensional array types
public static final JavaTypeName BOOLEAN_ARRAY = makeCached(boolean[].class);
public static final JavaTypeName BYTE_ARRAY = makeCached(byte[].class);
public static final JavaTypeName SHORT_ARRAY = makeCached(short[].class);
public static final JavaTypeName CHAR_ARRAY = makeCached(char[].class);
public static final JavaTypeName INT_ARRAY = makeCached(int[].class);
public static final JavaTypeName LONG_ARRAY = makeCached(long[].class);
public static final JavaTypeName DOUBLE_ARRAY = makeCached(double[].class);
public static final JavaTypeName FLOAT_ARRAY = makeCached(float[].class);
//object wrappers to the primitive Java types
public static final JavaTypeName VOID_OBJECT = makeCached(Void.class);
public static final JavaTypeName BOOLEAN_OBJECT = makeCached(Boolean.class);
public static final JavaTypeName BYTE_OBJECT = makeCached(Byte.class);
public static final JavaTypeName SHORT_OBJECT = makeCached(Short.class);
public static final JavaTypeName CHARACTER_OBJECT = makeCached(Character.class);
public static final JavaTypeName INTEGER_OBJECT = makeCached(Integer.class);
public static final JavaTypeName LONG_OBJECT = makeCached(Long.class);
public static final JavaTypeName DOUBLE_OBJECT = makeCached(Double.class);
public static final JavaTypeName FLOAT_OBJECT = makeCached(Float.class);
//classes in the standard java libraries
public static final JavaTypeName OBJECT = makeCached(Object.class);
public static final JavaTypeName STRING = makeCached(String.class);
public static final JavaTypeName STRING_ARRAY = makeCached(String[].class);
public static final JavaTypeName STRING_BUILDER = makeCached(StringBuilder.class);
public static final JavaTypeName THROWABLE = makeCached(Throwable.class);
public static final JavaTypeName BIG_INTEGER = makeCached(BigInteger.class);
public static final JavaTypeName LIST = makeCached(java.util.List.class);
public static final JavaTypeName MAP = makeCached(java.util.Map.class);
public static final JavaTypeName WEAK_HASH_MAP = makeCached(WeakHashMap.class);
public static final JavaTypeName SYSTEM = makeCached(System.class);
public static final JavaTypeName PRINT_STREAM = makeCached(PrintStream.class);
public static final JavaTypeName COMPARATOR = makeCached(java.util.Comparator.class);
public static final JavaTypeName THREAD = makeCached(Thread.class);
public static final JavaTypeName COLLECTIONS = makeCached (Collections.class);
public static final JavaTypeName ITERATOR = makeCached(java.util.Iterator.class);
public static final JavaTypeName CLASS = makeCached(Class.class);
// Exception types from the standard Java libraries
public static final JavaTypeName CLASS_CAST_EXCEPTION = makeCached(ClassCastException.class);
public static final JavaTypeName RUNTIME_EXCEPTION = makeCached(RuntimeException.class);
public static final JavaTypeName NULL_POINTER_EXCEPTION = makeCached(NullPointerException.class);
public static final JavaTypeName INDEX_OUT_OF_BOUNDS_EXCEPTION = makeCached(IndexOutOfBoundsException.class);
public static final JavaTypeName ASSERTION_ERROR = makeCached(AssertionError.class);
// Error info type
public static final JavaTypeName ERRORINFO = makeCached(ErrorInfo.class);
public static final JavaTypeName CAL_VALUE = makeCached(CalValue.class);
public static final JavaTypeName CAL_VALUE_ARRAY = makeCached(CalValue[].class);
public static final JavaTypeName DEBUG_SUPPORT = makeCached(DebugSupport.class);
public static final JavaTypeName CAL_EXECUTOR = makeCached(CALExecutor.class);
public static final JavaTypeName CAL_EXECUTOR_EXCEPTION = makeCached(CALExecutorException.class);
public static final JavaTypeName EQUIVALENCE_RELATION = makeCached(EquivalenceRelation.class);
public static final JavaTypeName CAL_FUNCTION = makeCached(CalFunction.class);
public static final JavaTypeName MODULE_NAME = makeCached(ModuleName.class);
public static final JavaTypeName QUALIFIED_NAME = makeCached(QualifiedName.class);
public static final JavaTypeName[] NO_ARGS = new JavaTypeName[0];
//todoBI we should not have a dependency on lecc classes in this file
static final JavaTypeName RTVALUE = makeCached(RTValue.class);
/**
* Subclasses of Primitive represent the primitive Java types (void, int, char, ...).
* The subclasses are singletons.
*
* @author Bo Ilic
*/
public abstract static class Primitive extends JavaTypeName {
public static final class Void extends Primitive {
private Void () {/* Constructor made private to prevent instantiation. */}
@Override
public String getName() {
return "void";
}
@Override
public boolean equals(Object other) {
return other instanceof Void;
}
@Override
public int getTag() {
return JavaTypeName.VOID_TAG;
}
@Override
public String getJVMDescriptor() {
//void is not a field type, and so does not have a field descriptor, however, methods that return void use V do denote the return
return "V";
}
@Override
JavaTypeName makeArrayType() {
throw new UnsupportedOperationException("[void] is not a valid type.");
}
}
public static final class Boolean extends Primitive {
private Boolean() {/* Constructor made private to prevent instantiation. */}
@Override
public String getName() {
return "boolean";
}
@Override
public boolean equals(Object other) {
return other instanceof Boolean;
}
@Override
public int getTag() {
return JavaTypeName.BOOLEAN_TAG;
}
@Override
public String getJVMDescriptor() {
return "Z";
}
@Override
JavaTypeName makeArrayType() {
return JavaTypeName.BOOLEAN_ARRAY;
}
}
public static final class Byte extends Primitive {
private Byte () {/* Constructor made private to prevent instantiation. */}
@Override
public String getName() {
return "byte";
}
@Override
public boolean equals(Object other) {
return other instanceof Byte;
}
@Override
public int getTag() {
return JavaTypeName.BYTE_TAG;
}
@Override
public String getJVMDescriptor() {
return "B";
}
@Override
JavaTypeName makeArrayType() {
return JavaTypeName.BYTE_ARRAY;
}
}
public static final class Short extends Primitive {
private Short() {/* Constructor made private to prevent instantiation. */}
@Override
public String getName() {
return "short";
}
@Override
public boolean equals(Object other) {
return other instanceof Short;
}
@Override
public int getTag() {
return JavaTypeName.SHORT_TAG;
}
@Override
public String getJVMDescriptor() {
return "S";
}
@Override
JavaTypeName makeArrayType() {
return JavaTypeName.SHORT_ARRAY;
}
}
public static final class Char extends Primitive {
private Char() {/* Constructor made private to prevent instantiation. */}
@Override
public String getName() {
return "char";
}
@Override
public boolean equals(Object other) {
return other instanceof Char;
}
@Override
public int getTag() {
return JavaTypeName.CHAR_TAG;
}
@Override
public String getJVMDescriptor() {
return "C";
}
@Override
JavaTypeName makeArrayType() {
return JavaTypeName.CHAR_ARRAY;
}
}
public static final class Int extends Primitive {
private Int() {/* Constructor made private to prevent instantiation. */}
@Override
public String getName() {
return "int";
}
@Override
public boolean equals(Object other) {
return other instanceof Int;
}
@Override
public int getTag() {
return JavaTypeName.INT_TAG;
}
@Override
public String getJVMDescriptor() {
return "I";
}
@Override
JavaTypeName makeArrayType() {
return JavaTypeName.INT_ARRAY;
}
}
public static final class Long extends Primitive {
private Long() {/* Constructor made private to prevent instantiation. */}
@Override
public String getName() {
return "long";
}
@Override
public boolean equals(Object other) {
return other instanceof Long;
}
@Override
public int getTag() {
return JavaTypeName.LONG_TAG;
}
@Override
public String getJVMDescriptor() {
return "J";
}
@Override
JavaTypeName makeArrayType() {
return JavaTypeName.LONG_ARRAY;
}
}
public static final class Double extends Primitive {
private Double() {/* Constructor made private to prevent instantiation. */}
@Override
public String getName() {
return "double";
}
@Override
public boolean equals(Object other) {
return other instanceof Double;
}
@Override
public int getTag() {
return JavaTypeName.DOUBLE_TAG;
}
@Override
public String getJVMDescriptor() {
return "D";
}
@Override
JavaTypeName makeArrayType() {
return JavaTypeName.DOUBLE_ARRAY;
}
}
public static final class Float extends Primitive {
private Float() {/* Constructor made private to prevent instantiation. */}
@Override
public String getName() {
return "float";
}
@Override
public boolean equals(Object other) {
return other instanceof Float
;
}
@Override
public int getTag() {
return JavaTypeName.FLOAT_TAG;
}
@Override
public String getJVMDescriptor() {
return "F";
}
@Override
JavaTypeName makeArrayType() {
return JavaTypeName.FLOAT_ARRAY;
}
}
private Primitive() {/* Constructor made private to prevent instantiation. */}
@Override
public String getFullJavaSourceName() {
return getName();
}
@Override
public String getUnqualifiedJavaSourceName() {
return getName();
}
@Override
public String getPackageName() {
return "";
}
@Override
public boolean isInnerClass() {
return false;
}
/**
* @return true if this is an Object type.
*/
@Override
public boolean isObjectReference () {
return false;
}
@Override
public boolean isInterface() {
return false;
}
}
/**
* Represent reference types in Java i.e. array or object types.
* @author Bo Ilic
*/
public abstract static class Reference extends JavaTypeName {
/**
* The fully qualified name of the class. Uses $ as a separator between nested classes and
* the period . as the package name separator. Uses the special "right bracket" notation
* for arrays as specified in the JVM spec i.e. this is the name returned by java.lang.Class.getName().
*/
private final String name;
public static final class Array extends Reference {
private final boolean isElementTypeAnInterface;
private String cachedJVMDescriptor;
private Array(String name, boolean isElementTypeAnInterface) {
super(name);
this.isElementTypeAnInterface = isElementTypeAnInterface;
}
@Override
public boolean equals(java.lang.Object other) {
if (other instanceof Array) {
return getName().equals(((Array)other).getName());
}
return false;
}
@Override
public int hashCode () {
return getName().hashCode();
}
@Override
public String getFullJavaSourceName() {
return getFullJavaSourceName(super.name, true);
}
/**
* @param arrayTypeName the name of the class returned by java.lang.Class.getName().
* @param replaceDollarSigns true if dollar signs should be replaced by periods
* @return the fully qualified name of a class as used in Java source code.
*/
static String getFullJavaSourceName(final String arrayTypeName, boolean replaceDollarSigns) {
final int dim = getNDimensions(arrayTypeName);
final StringBuilder sb = new StringBuilder();
switch (arrayTypeName.charAt(dim)) {
case 'Z':
sb.append("boolean");
break;
case 'B':
sb.append("byte");
break;
case 'C':
sb.append("char");
break;
case 'D':
sb.append("double");
break;
case 'F':
sb.append("float");
break;
case 'I':
sb.append("int");
break;
case 'J':
sb.append("long");
break;
case 'S':
sb.append("short");
break;
case 'L':
{
//the format is: [[[LelementTypeName;
//so we skip the L and the final semicolon
//also replace the $ if the element type is an inner class with .
final int length = arrayTypeName.length();
final String elementTypeName = arrayTypeName.substring(dim + 1, length - 1);
final String sourceElementTypeName;
if (replaceDollarSigns) {
sourceElementTypeName = elementTypeName.replace('$', '.');
} else {
sourceElementTypeName = elementTypeName;
}
sb.append(sourceElementTypeName);
break;
}
default:
{
throw new IllegalStateException();
}
}
for (int i = 0; i < dim; ++i) {
sb.append("[]");
}
return sb.toString();
}
@Override
public String getUnqualifiedJavaSourceName() {
String sourceName = getFullJavaSourceName();
int lastDotIndex = sourceName.lastIndexOf('.');
if (lastDotIndex == -1) {
//an array of primitive types, such as int[]
return sourceName;
}
return sourceName.substring(lastDotIndex + 1);
}
/**
* @return the element type of the array. For example, for the 2-dimensional array int[][] this is int.
*/
public JavaTypeName getElementType() {
int dim = getNDimensions();
switch (super.name.charAt(dim)) {
case 'Z':
return JavaTypeName.BOOLEAN;
case 'B':
return JavaTypeName.BYTE;
case 'C':
return JavaTypeName.CHAR;
case 'D':
return JavaTypeName.DOUBLE;
case 'F':
return JavaTypeName.FLOAT;
case 'I':
return JavaTypeName.INT;
case 'J':
return JavaTypeName.LONG;
case 'S':
return JavaTypeName.SHORT;
case 'L':
{
//the format is: [[[LelementTypeName;
//so we skip the L and the final semicolon
int length = super.name.length();
return new Reference.Object(super.name.substring(dim + 1, length - 1), isElementTypeAnInterface);
}
default:
{
throw new IllegalStateException();
}
}
}
/**
* @return the number of dimensions of this array type. Will be greater than or equal to 1.
*/
public int getNDimensions() {
return getNDimensions(super.name);
}
/**
* @param arrayTypeName the name of a Java array type, as returned by Class.getName()
* @return the number of dimensions of this array type. Will be greater than or equal to 1.
*/
static int getNDimensions(final String arrayTypeName) {
//we know that this is an array, so it is at least 1 dimensional
int i = 1;
while (arrayTypeName.charAt(i) == '[') {
++i;
}
return i;
}
/**
* @return the incremental element type of the array. For example, for the 2-dimensional array int[][] this is int[]
* (and not int).
*/
public JavaTypeName getIncrementalElementType() {
final int dim = getNDimensions();
if (dim == 1) {
return getElementType();
}
return new Reference.Array(super.name.substring(1), isElementTypeAnInterface);
}
@Override
public String getPackageName() {
int dim = getNDimensions();
switch (super.name.charAt(dim)) {
case 'Z':
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
return "";
case 'L':
{
//the format is: [[[LelementTypeName;
//so we skip the L and the final semicolon
//also replace the $ if the element type is an inner class with .
int lastDotIndex = super.name.lastIndexOf('.');
return super.name.substring(dim + 1, lastDotIndex);
}
default:
{
throw new IllegalStateException();
}
}
}
@Override
public int getTag() {
return JavaTypeName.ARRAY_TAG;
}
@Override
public String getJVMDescriptor() {
if (cachedJVMDescriptor != null) {
return cachedJVMDescriptor;
}
return cachedJVMDescriptor = super.name.replace('.', '/');
}
@Override
public JavaTypeName makeArrayType() {
//delegate to JavaTypeName.make in case the type is in the cache.
return JavaTypeName.make("[" + super.name, isElementTypeAnInterface);
}
@Override
public boolean isInnerClass() {
return false;
}
/**
* @return true if this is an Object type.
*/
@Override
public boolean isObjectReference () {
return false;
}
@Override
public boolean isInterface() {
return false;
}
}
public static final class Object extends Reference {
/** true if this type is an interface (rather than a class) */
private final boolean isInterface;
private String cachedJVMDescriptor;
private String cachedJVMInternalName;
private Object(String name, boolean isInterface) {
super(name);
if (name.indexOf('.') == -1) {
throw new IllegalArgumentException("The default package is not supported.");
}
this.isInterface = isInterface;
}
@Override
public boolean equals(java.lang.Object other) {
if (other instanceof Reference.Object) {
return getName().equals(((Reference.Object)other).getName());
}
return false;
}
@Override
public int hashCode () {
return getName().hashCode();
}
@Override
public String getFullJavaSourceName() {
return getName().replace('$', '.');
}
@Override
public String getPackageName() {
int lastDotIndex = super.name.lastIndexOf('.');
return super.name.substring(0, lastDotIndex);
}
@Override
public int getTag() {
return JavaTypeName.OBJECT_TAG;
}
/**
* Get the Object type's unqualified name in its internal format.
* This is what is returned by JavaTypeName.getName() without the package part.
* $ signs will be present for inner classes.
* @return what is returned by JavaTypeName.getName() without the package part.
*/
public String getInternalUnqualifiedName() {
//todoBI change name to getUnqualifiedName().
// find the last '.', and return everything to the right of it.
int lastDotIndex = super.name.lastIndexOf('.');
return super.name.substring(lastDotIndex + 1);
}
@Override
public String getUnqualifiedJavaSourceName() {
// find the last '.', and return everything to the right of it, with '$' replaced by '.'.
return getInternalUnqualifiedName().replace('$', '.');
}
/**
* Get the base name ie. everything to the right of any '.' or '$'.
* For example, this is "String" for "java.lang.String" and
* "OperatorExpression" for "org.openquark.cal.internal.runtime.lecc.JavaExpression$OperatorExpression"
*
* @return the base name.
*/
public String getBaseName() {
// find the last '$' or '.', and return everything to the right of it.
int index = super.name.lastIndexOf('$');
if (index == -1) {
index = super.name.lastIndexOf('.');
}
return super.name.substring(index + 1);
}
/**
* Get the name of the type that should be imported to import this type.
* For example, for JavaTypeName.make("org.openquark.cal.internal.runtime.lecc.JavaExpression$OperatorExpression")
* this is "org.openquark.cal.internal.runtime.lecc.JavaExpression".
* @return String
*/
public String getImportName() {
// find the last '$', and return everything to the left of it.
int index = super.name.indexOf('$');
if (index != -1) {
return super.name.substring(0, index);
}
return super.name;
}
/**
* For Object types, this is defined in the JVM spec as the fully qualified name of the class (i.e. what is returned by getName()),
* with '.' replaced by '/'.
* @return the internal name of this Object type, in the sense of the JVM spec.
*/
@Override
public String getJVMInternalName() {
if (cachedJVMInternalName != null) {
return cachedJVMInternalName;
}
return cachedJVMInternalName = super.name.replace('.', '/');
}
@Override
public String getJVMDescriptor() {
if (cachedJVMDescriptor != null) {
return cachedJVMDescriptor;
}
return cachedJVMDescriptor = new StringBuilder("L").append(getJVMInternalName()).append(';').toString();
}
@Override
JavaTypeName makeArrayType() {
//delegate to JavaTypeName.make in case the type is in the cache.
return JavaTypeName.make(new StringBuilder("[L").append(super.name).append(';').toString(), isInterface);
}
@Override
public boolean isInnerClass() {
return super.name.indexOf('$') != -1;
}
/**
* @return true if this is an Object type.
*/
@Override
public boolean isObjectReference () {
return true;
}
@Override
public boolean isInterface() {
return isInterface;
}
}
private Reference(String name) {
Assert.isNotNull(name);
this.name = name;
}
@Override
public String getName() {
return name;
}
}
private JavaTypeName () {
// Constructor made private to prevent direct instantiation.
}
/**
* @param name the name of the class as returned by the java.lang.Class.getName() method.
* @param isInterfaceOrInterfaceArray true if this type is an interface (such as java.util.List,
* or an array, possibly multidimensional, of interfaces.
* @return JavaTypeName. May not be a new reference if the JavaTypeName already exists in the cache.
*/
public static JavaTypeName make(String name, boolean isInterfaceOrInterfaceArray) {
//the primitive types are all in the cache.
JavaTypeName typeName = cachedTypes.get(name);
if (typeName != null) {
return typeName;
}
if (name.charAt(0) == '[') {
return new Reference.Array(name, isInterfaceOrInterfaceArray);
}
return new Reference.Object(name, isInterfaceOrInterfaceArray);
}
/**
* @param type
* @return JavaTypeName. May not be a new reference if the JavaTypeName already exists in the cache.
*/
public static JavaTypeName make(Class<?> type) {
return make(type.getName(), isInterfaceOrInterfaceArray(type));
}
/**
* @param type
* @return true if type is an interface itself or is an array, possibly multidimensional of interfaces.
*/
private static boolean isInterfaceOrInterfaceArray(Class<?> type) {
if (type.isInterface()) {
return true;
}
if (type.isArray()) {
//Class.getComponentType returns int[] for int[][]
return isInterfaceOrInterfaceArray(type.getComponentType());
}
//class that is not an array, or a primitive type
return false;
}
private static JavaTypeName makeCached(Class<?> type) {
final String name = type.getName();
final JavaTypeName typeName;
if (type.isPrimitive()) {
if (type == int.class) {
typeName = new JavaTypeName.Primitive.Int();
} else if (type == void.class) {
typeName = new JavaTypeName.Primitive.Void();
} else if (type == boolean.class) {
typeName = new JavaTypeName.Primitive.Boolean();
} else if (type == byte.class) {
typeName = new JavaTypeName.Primitive.Byte();
} else if (type == char.class) {
typeName = new JavaTypeName.Primitive.Char();
} else if (type == short.class) {
typeName = new JavaTypeName.Primitive.Short();
} else if (type == double.class) {
typeName = new JavaTypeName.Primitive.Double();
} else if (type == float.class) {
typeName = new JavaTypeName.Primitive.Float();
} else if (type == long.class) {
typeName = new JavaTypeName.Primitive.Long();
} else {
throw new IllegalArgumentException();
}
} else if (type.isArray()) {
typeName = new Reference.Array(type.getName(), isInterfaceOrInterfaceArray(type));
} else {
typeName = new Reference.Object(type.getName(), type.isInterface());
}
//add to cache
if (cachedTypes.put(name, typeName) != null) {
throw new IllegalArgumentException("Attempt to cache previously cached type.");
}
return typeName;
}
/**
* @param elementType
* @return a 1-dimensional array having elements of the given elementType (which may itself be an array type)
*/
public static JavaTypeName makeArrayType(JavaTypeName elementType) {
return elementType.makeArrayType();
}
/**
* For example,
* "int" (i.e. int.class.getName())
* "void" (void.class.getName())
* "java.lang.String"
* "[I" (int[].class.getName())
* "[Ljava.lang.String;" (java.lang.String[].class.getName())
*
* Inner class names are separated from their enclosing class names via a $.
*
* @return the name of the type as would be returned by java.lang.Class.getName().
*/
abstract public String getName ();
@Override
abstract public boolean equals(java.lang.Object other);
@Override
public int hashCode() {
return getName().hashCode();
}
/**
* The full name of the type in Java, i.e. including the package name for non-primitive types, suitable for use in a Java source file.
* In other words, for array brackets appear at the end of the name in matching pairs (e.g. java.lang.String[])
* There are no $'s in the names of inner classes (the . separator is used).
*
* For example, this is
* "String" for JavaTypeName.make("java.lang.String")
* "org.openquark.cal.internal.runtime.lecc.JavaExpression.OperatorExpression" for JavaTypeName.make("org.openquark.cal.internal.runtime.lecc.JavaExpression$OperatorExpression")
* "int" for JavaTypeName.make("int")
* "void" for JavaTypeName.make("void")
* "int[]" for JavaTypeName.make("[I")
* "java.lang.String[]" for JavaTypeName.make("[Ljava.lang.String;")
*
* @return full name of the type in Java, suitable for use in a Java source file.
*/
abstract public String getFullJavaSourceName();
/**
* The full name of the type in Java, i.e. including the package name for non-primitive types, suitable for use in a Java source file.
* In other words, for array brackets appear at the end of the name in matching pairs (e.g. java.lang.String[])
* There are no $'s in the names of inner classes (the . separator is used).
*
* For example, this is
* "String" for java.lang.String.class
* "org.openquark.cal.internal.runtime.lecc.JavaExpression.OperatorExpression" for org.openquark.cal.internal.runtime.lecc.JavaExpression.OperatorExpression.class
* "int" for int.class
* "void" for void.class
* "int[]" for int[].class
* "java.lang.String[]" for java.lang.String[].class
*
* @param type
* @return full name of the type in Java, suitable for use in a Java source file.
*/
static public String getFullJavaSourceName(Class<?> type) {
if (type.isPrimitive()) {
return type.getName();
}
if (type.isArray()) {
return JavaTypeName.Reference.Array.getFullJavaSourceName(type.getName(), true);
}
return type.getName().replace('$', '.');
}
/**
* The name of the type, in a canonical form suitable for appearing in CAL source in a foreign data declaration.
* This is similar to what is returned by getFullJavaSourceName(Class) except that inner classes are separated by a $.
*
* For example, it will return:
* java.lang.String
* java.util.Map$Entry
* int
* void
* int[]
* java.lang.String[]
*
* @param type
* @return a canonical form of the Java type name, suitable for use in a CAL source file.
*/
static public String getCalSourceName(Class<?> type) {
if (type.isPrimitive()) {
return type.getName();
}
if (type.isArray()) {
return JavaTypeName.Reference.Array.getFullJavaSourceName(type.getName(), false);
}
return type.getName();
}
/**
* Get the unqualified name of this type as seen in Java source.
* This is what is returned by JavaTypeName.getFullSourceName() without the package part.
*
* For example, this is
* "String" for JavaTypeName.make("java.lang.String")
* "JavaExpression.OperatorExpression" for JavaTypeName.make("org.openquark.cal.internal.runtime.lecc.JavaExpression$OperatorExpression")
* "int" for JavaTypeName.make("int")
* "void" for JavaTypeName.make("void")
* "int[]" for JavaTypeName.make("[I")
* "String[]" for JavaTypeName.make("[Ljava.lang.String;")
*
* @return what is returned by JavaTypeName.getFullJavaSourceName() without the package part. Suitable for use in a Java source file.
*/
abstract public String getUnqualifiedJavaSourceName();
/**
* @return package name, or the empty String if there is not package (such as for primitive types, or the array types
* with primitive elements such as int[]. For example, returns "java.lang" for JavaTypeName.make("java.lang.String").
*/
abstract public String getPackageName();
abstract public int getTag();
/**
* As specified in section 4.3 of the Java Virtual Machine Spec.
* @return the name of the type, in the format of a field descriptor, as specified in the Java language specification.
*/
abstract public String getJVMDescriptor();
/**
* @return the type of a 1-dimensional array having this JavaTypeName as its element type.
*/
abstract JavaTypeName makeArrayType();
@Override
public String toString() {
return getName();
}
/**
* For Object types, this is defined in the JVM spec as the fully qualified name of the class (i.e. what is returned by getName()),
* with '.' replaced by '/'. For other types we define it as equal to the JVM descriptor.
* @return the internal name of this Object type, in the sense of the JVM spec.
*/
public String getJVMInternalName() {
return getJVMDescriptor();
}
/**
* @return true if an Object type that is an inner class. Note: not true for arrays of inner classes.
*/
public abstract boolean isInnerClass();
/**
* @return true if this is an Object type.
*/
public abstract boolean isObjectReference ();
/**
* @return true if this type is in fact an interface. Note that arrays of interfaces return false.
*/
public abstract boolean isInterface();
}