0001: /* ====================================================================
0002: * Tea - Copyright (c) 1997-2000 Walt Disney Internet Group
0003: * ====================================================================
0004: * The Tea Software License, Version 1.1
0005: *
0006: * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
0007: *
0008: * Redistribution and use in source and binary forms, with or without
0009: * modification, are permitted provided that the following conditions
0010: * are met:
0011: *
0012: * 1. Redistributions of source code must retain the above copyright
0013: * notice, this list of conditions and the following disclaimer.
0014: *
0015: * 2. Redistributions in binary form must reproduce the above copyright
0016: * notice, this list of conditions and the following disclaimer in
0017: * the documentation and/or other materials provided with the
0018: * distribution.
0019: *
0020: * 3. The end-user documentation included with the redistribution,
0021: * if any, must include the following acknowledgment:
0022: * "This product includes software developed by the
0023: * Walt Disney Internet Group (http://opensource.go.com/)."
0024: * Alternately, this acknowledgment may appear in the software itself,
0025: * if and wherever such third-party acknowledgments normally appear.
0026: *
0027: * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
0028: * not be used to endorse or promote products derived from this
0029: * software without prior written permission. For written
0030: * permission, please contact opensource@dig.com.
0031: *
0032: * 5. Products derived from this software may not be called "Tea",
0033: * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
0034: * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
0035: * written permission of the Walt Disney Internet Group.
0036: *
0037: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0038: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0039: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0040: * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
0041: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0042: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0043: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0044: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
0045: * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0046: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0047: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0048: * ====================================================================
0049: *
0050: * For more information about Tea, please see http://opensource.go.com/.
0051: */
0052:
0053: package com.go.tea.compiler;
0054:
0055: import java.lang.reflect.Array;
0056: import java.lang.reflect.Field;
0057: import java.lang.reflect.Modifier;
0058: import java.lang.reflect.Method;
0059: import java.util.Collection;
0060: import java.util.List;
0061: import java.util.Map;
0062: import java.util.Set;
0063: import java.util.HashSet;
0064: import java.util.Iterator;
0065: import java.beans.IntrospectionException;
0066: import com.go.tea.util.BeanAnalyzer;
0067: import com.go.tea.util.KeyedPropertyDescriptor;
0068:
0069: /******************************************************************************
0070: * Immutable representation of an expression's type.
0071: *
0072: * @author Brian S O'Neill
0073: * @version
0074: * <!--$$Revision:--> 45 <!-- $-->, <!--$$JustDate:--> 01/02/05 <!-- $-->
0075: * @see com.go.tea.parsetree.Expression
0076: */
0077: public class Type implements java.io.Serializable {
0078: /** Type that is compatble with all other types */
0079: public static final Type NULL_TYPE = new Type(Object.class) {
0080: public String getSimpleName() {
0081: return toString();
0082: }
0083:
0084: public String getFullName() {
0085: return toString();
0086: }
0087:
0088: public String toString() {
0089: return "null-type";
0090: }
0091: };
0092:
0093: /** Type that represents void, provided as a convenience */
0094: public static final Type VOID_TYPE = new Type(void.class);
0095:
0096: /** Type that represents all objects, provided as a convenience */
0097: public static final Type OBJECT_TYPE = new Type(Object.class);
0098:
0099: /** Type for representing ints, provided as a convenience */
0100: public static final Type INT_TYPE = new Type(int.class);
0101:
0102: /** Type for representing longs, provided as a convenience */
0103: public static final Type LONG_TYPE = new Type(long.class);
0104:
0105: /** Type for representing booleans, provided as a convenience */
0106: public static final Type BOOLEAN_TYPE = new Type(boolean.class);
0107:
0108: /** Type for representing Strings, provided as a convenience */
0109: public static final Type STRING_TYPE = new Type(String.class);
0110:
0111: /** Type for representing non-null Strings, provided as a convenience */
0112: public static final Type NON_NULL_STRING_TYPE = STRING_TYPE
0113: .toNonNull();
0114:
0115: private static final Method[] EMPTY_METHOD_ARRAY = new Method[0];
0116:
0117: private final Class mObjectClass;
0118: private final Class mNaturalClass;
0119: private final boolean mPrimitive;
0120:
0121: private transient boolean mCheckedForArrayLookup;
0122: private transient Type mArrayElementType;
0123: private transient Type[] mArrayIndexTypes;
0124: private transient Method[] mArrayAccessMethods;
0125: private transient boolean mCheckedForIteration;
0126: private transient Type mIterationElementType;
0127:
0128: public Type(Class type) {
0129: if (type.isPrimitive()) {
0130: mNaturalClass = type;
0131: mObjectClass = convertToObject(type);
0132: mPrimitive = true;
0133: } else {
0134: mObjectClass = mNaturalClass = type;
0135: mPrimitive = false;
0136: }
0137: }
0138:
0139: private Type(Class object, Class natural) {
0140: mObjectClass = object;
0141: mNaturalClass = natural;
0142: if (natural.isPrimitive()) {
0143: mPrimitive = true;
0144: } else {
0145: mPrimitive = false;
0146: }
0147: }
0148:
0149: Type(Type type) {
0150: mObjectClass = type.mObjectClass;
0151: mNaturalClass = type.mNaturalClass;
0152: mPrimitive = type.mPrimitive;
0153:
0154: mCheckedForArrayLookup = type.mCheckedForArrayLookup;
0155: mArrayElementType = type.mArrayElementType;
0156: mArrayIndexTypes = type.mArrayIndexTypes;
0157: mArrayAccessMethods = type.mArrayAccessMethods;
0158: mCheckedForIteration = type.mCheckedForIteration;
0159: mIterationElementType = type.mIterationElementType;
0160: }
0161:
0162: /**
0163: * Class returned never represents a primitive type.
0164: */
0165: public Class getObjectClass() {
0166: return mObjectClass;
0167: }
0168:
0169: /**
0170: * Returns the natural class for this type. If type is primitive, then its
0171: * primitive peer is returned.
0172: */
0173: public Class getNaturalClass() {
0174: return mNaturalClass;
0175: }
0176:
0177: /**
0178: * Returns true if this type is a primitive.
0179: */
0180: public boolean isPrimitive() {
0181: return mPrimitive;
0182: }
0183:
0184: /**
0185: * Returns true if this type is not primitive, but it has a primitive
0186: * type peer.
0187: */
0188: public boolean hasPrimitivePeer() {
0189: if (mObjectClass == Integer.class
0190: || mObjectClass == Boolean.class
0191: || mObjectClass == Byte.class
0192: || mObjectClass == Character.class
0193: || mObjectClass == Short.class
0194: || mObjectClass == Long.class
0195: || mObjectClass == Float.class
0196: || mObjectClass == Double.class
0197: || mObjectClass == Void.class) {
0198:
0199: return true;
0200: }
0201:
0202: return false;
0203: }
0204:
0205: /**
0206: * Returns a new type from this one that represents a primitive type.
0207: * If this type cannot be represented by a primitive, then this is
0208: * returned.
0209: */
0210: public Type toPrimitive() {
0211: if (mPrimitive) {
0212: return this ;
0213: } else {
0214: return new Type(mObjectClass,
0215: convertToPrimitive(mObjectClass));
0216: }
0217: }
0218:
0219: /**
0220: * Returns a new type from this one that represents a non-primitive type.
0221: * If this type actually is primitive, the returned type is marked as not
0222: * being able to reference null. i.e. if this type is int,
0223: * new Type(Integer.class).toNonNull() is returned.
0224: */
0225: public Type toNonPrimitive() {
0226: if (mPrimitive) {
0227: return new Type(mObjectClass).toNonNull();
0228: } else {
0229: return this ;
0230: }
0231: }
0232:
0233: /**
0234: * Returns true if this type cannot reference null. For primitive types,
0235: * true is always returned.
0236: */
0237: public boolean isNonNull() {
0238: return isPrimitive();
0239: }
0240:
0241: /**
0242: * Returns true if this type can reference null, or simply the opposite
0243: * result of isNonNull.
0244: */
0245: public boolean isNullable() {
0246: return !isNonNull();
0247: }
0248:
0249: /**
0250: * Returns this type converted such that it cannot reference null.
0251: */
0252: public Type toNonNull() {
0253: if (isNonNull()) {
0254: return this ;
0255: } else {
0256: return new Type(this ) {
0257: public boolean isNonNull() {
0258: return true;
0259: }
0260:
0261: public boolean isNullable() {
0262: return false;
0263: }
0264:
0265: public Type toNullable() {
0266: return Type.this ;
0267: }
0268: };
0269: }
0270: }
0271:
0272: /**
0273: * Returns this type converted such that it can reference null. The
0274: * resulting type is never primitive.
0275: */
0276: public Type toNullable() {
0277: if (!isNonNull()) {
0278: return this ;
0279: } else {
0280: return new Type(mObjectClass);
0281: }
0282: }
0283:
0284: /**
0285: * If this Type supports array lookup, then return the element type.
0286: * Otherwise, null is returned.
0287: */
0288: public Type getArrayElementType() throws IntrospectionException {
0289: if (!mCheckedForArrayLookup) {
0290: checkForArrayLookup();
0291: }
0292:
0293: return mArrayElementType;
0294: }
0295:
0296: /**
0297: * If this Type supports array lookup, then return the index type. Because
0298: * the index type may be overloaded, an array is returned.
0299: * Null is returned if this Type doesn't support array lookup.
0300: */
0301: public Type[] getArrayIndexTypes() throws IntrospectionException {
0302: if (!mCheckedForArrayLookup) {
0303: checkForArrayLookup();
0304: }
0305:
0306: return mArrayIndexTypes == null ? null
0307: : (Type[]) mArrayIndexTypes.clone();
0308: }
0309:
0310: /**
0311: * If this Type supports array lookup, then return all of the methods
0312: * that can be called to access the array. If there are no methods, then
0313: * an empty array is returned. Null is returned only if this Type
0314: * doesn't support array lookup.
0315: */
0316: public Method[] getArrayAccessMethods()
0317: throws IntrospectionException {
0318: if (!mCheckedForArrayLookup) {
0319: checkForArrayLookup();
0320: }
0321:
0322: return mArrayAccessMethods == null ? null
0323: : (Method[]) mArrayAccessMethods.clone();
0324: }
0325:
0326: /**
0327: * If this type supports iteration, then the element type is returned.
0328: * Otherwise, null is returned.
0329: */
0330: public Type getIterationElementType() throws IntrospectionException {
0331: if (!mCheckedForIteration) {
0332: mCheckedForIteration = true;
0333:
0334: if (mNaturalClass.isArray()) {
0335: mIterationElementType = getArrayElementType();
0336: } else if (Collection.class.isAssignableFrom(mNaturalClass)) {
0337: mIterationElementType = OBJECT_TYPE;
0338:
0339: try {
0340: Field field = mNaturalClass
0341: .getField(BeanAnalyzer.ELEMENT_TYPE_FIELD_NAME);
0342: if (field.getType() == Class.class
0343: && Modifier.isStatic(field.getModifiers())) {
0344:
0345: mIterationElementType = new Type((Class) field
0346: .get(null));
0347: }
0348: } catch (NoSuchFieldException e) {
0349: } catch (IllegalAccessException e) {
0350: }
0351: }
0352: }
0353:
0354: return mIterationElementType;
0355: }
0356:
0357: /**
0358: * Returns true if this type supports iteration in the reverse direction.
0359: */
0360: public boolean isReverseIterationSupported() {
0361: return mNaturalClass.isArray()
0362: || List.class.isAssignableFrom(mNaturalClass);
0363: }
0364:
0365: private void checkForArrayLookup() throws IntrospectionException {
0366: mCheckedForArrayLookup = true;
0367:
0368: if (mObjectClass.isArray()) {
0369: mArrayElementType = new Type(mObjectClass
0370: .getComponentType());
0371: mArrayAccessMethods = EMPTY_METHOD_ARRAY;
0372: mArrayIndexTypes = new Type[] { INT_TYPE };
0373: return;
0374: }
0375:
0376: try {
0377: Map properties = BeanAnalyzer
0378: .getAllProperties(mObjectClass);
0379:
0380: KeyedPropertyDescriptor keyed = (KeyedPropertyDescriptor) properties
0381: .get(BeanAnalyzer.KEYED_PROPERTY_NAME);
0382:
0383: if (keyed == null) {
0384: return;
0385: }
0386:
0387: mArrayElementType = new Type(keyed.getKeyedPropertyType());
0388: mArrayAccessMethods = keyed.getKeyedReadMethods();
0389: } catch (ClassCastException e) {
0390: return;
0391: }
0392:
0393: int length = mArrayAccessMethods.length;
0394: mArrayIndexTypes = new Type[length];
0395: for (int i = 0; i < length; i++) {
0396: Method m = mArrayAccessMethods[i];
0397: mArrayIndexTypes[i] = new Type(m.getReturnType());
0398: }
0399: }
0400:
0401: /**
0402: * Accessed by the TypeChecker, to override the default.
0403: */
0404: Type setArrayElementType(Type elementType)
0405: throws IntrospectionException {
0406: Type type = new Type(mObjectClass, mNaturalClass);
0407: type.checkForArrayLookup();
0408: type.mArrayElementType = elementType;
0409: return type;
0410: }
0411:
0412: public String getSimpleName() {
0413: if (mNaturalClass.isArray()) {
0414: int dim = 0;
0415: Class baseNat = mNaturalClass;
0416: Type baseType = this ;
0417: while (baseNat.isArray()) {
0418: dim++;
0419: baseNat = baseNat.getComponentType();
0420: try {
0421: baseType = baseType.getArrayElementType();
0422: } catch (IntrospectionException e) {
0423: baseType = new Type(baseNat);
0424: }
0425: }
0426:
0427: String baseName = baseType.getSimpleName();
0428: StringBuffer nameBuf = new StringBuffer(baseName.length()
0429: + dim * 2);
0430: nameBuf.append(baseName);
0431:
0432: while (dim-- > 0) {
0433: nameBuf.append('[');
0434: nameBuf.append(']');
0435: }
0436:
0437: return nameBuf.toString();
0438: } else if (mPrimitive) {
0439: return mNaturalClass.getName();
0440: } else {
0441: String name = mObjectClass.getName();
0442: int index = name.lastIndexOf('.');
0443: if (index >= 0) {
0444: name = name.substring(index + 1);
0445: }
0446: return name;
0447: }
0448: }
0449:
0450: public String getFullName() {
0451: if (isPrimitive()) {
0452: return mNaturalClass.getName();
0453: }
0454:
0455: StringBuffer nameBuf = new StringBuffer(20);
0456: if (isNonNull()) {
0457: nameBuf.append("non-null ");
0458: }
0459:
0460: if (!mNaturalClass.isArray()) {
0461: nameBuf.append(mObjectClass.getName());
0462: } else {
0463: int dim = 0;
0464: Class baseNat = mNaturalClass;
0465: Type baseType = this ;
0466: while (baseNat.isArray()) {
0467: dim++;
0468: baseNat = baseNat.getComponentType();
0469: try {
0470: baseType = baseType.getArrayElementType();
0471: } catch (IntrospectionException e) {
0472: baseType = new Type(baseNat);
0473: }
0474: }
0475:
0476: String baseName = baseType.getFullName();
0477: nameBuf.append(baseName);
0478:
0479: while (dim-- > 0) {
0480: nameBuf.append('[');
0481: nameBuf.append(']');
0482: }
0483: }
0484:
0485: return nameBuf.toString();
0486: }
0487:
0488: public String toString() {
0489: if (!isPrimitive() && isNonNull()) {
0490: return "non-null " + mNaturalClass.getName();
0491: } else {
0492: return mNaturalClass.getName();
0493: }
0494: }
0495:
0496: public int hashCode() {
0497: return mNaturalClass.hashCode();
0498: }
0499:
0500: public boolean equals(Object another) {
0501: if (this == another) {
0502: return true;
0503: }
0504:
0505: if (another instanceof Type) {
0506: Type t = (Type) another;
0507:
0508: if (this == NULL_TYPE || another == NULL_TYPE) {
0509: return false;
0510: }
0511:
0512: if (mNaturalClass == t.mNaturalClass
0513: && isNonNull() == t.isNonNull()) {
0514:
0515: if (!mCheckedForArrayLookup
0516: && !t.mCheckedForArrayLookup) {
0517: // Don't recursively check array element type. They
0518: // will be the same. This test fixes an infinite recursion
0519: // bug if the element type is of the same type.
0520: return true;
0521: }
0522:
0523: try {
0524: Type this ArrayType = getArrayElementType();
0525: Type otherArrayType = t.getArrayElementType();
0526:
0527: if (this ArrayType != null) {
0528: if (this ArrayType.equals(otherArrayType)) {
0529: return true;
0530: }
0531: } else {
0532: if (otherArrayType == null) {
0533: return true;
0534: }
0535: }
0536: } catch (IntrospectionException e) {
0537: }
0538: }
0539: }
0540:
0541: return false;
0542: }
0543:
0544: /**
0545: * Returns a type that is compatible with this type, and the one passed in.
0546: * The type returned is selected using a best-fit algorithm.
0547: *
0548: * <p>If the type passed in represents a primitive type, but this type is
0549: * not, the type returned is an object (or a subclass of), but never a
0550: * primitive type. Compatible primitive types are returned when both this
0551: * and the parameter type were already primitive types.
0552: *
0553: * <p>Input types which are arrays are also supported by this method.
0554: *
0555: * <p>Returns null if the given type isn't compatible with this one.
0556: */
0557: public Type getCompatibleType(Type other) {
0558: if (other == null) {
0559: return null;
0560: }
0561:
0562: if (equals(other)) {
0563: if (this == NULL_TYPE) {
0564: return other;
0565: } else {
0566: return this ;
0567: }
0568: }
0569:
0570: Class classA = mObjectClass;
0571: Class classB = other.mObjectClass;
0572:
0573: Type compat;
0574:
0575: if (classA == Void.class) {
0576: if (classB == Void.class) {
0577: compat = this ;
0578: } else {
0579: return null;
0580: }
0581: } else if (classB == Void.class) {
0582: return null;
0583: } else if (other == NULL_TYPE) {
0584: compat = this .toNullable();
0585: } else if (this == NULL_TYPE) {
0586: compat = other.toNullable();
0587: } else if (Number.class.isAssignableFrom(classA)
0588: && Number.class.isAssignableFrom(classB)) {
0589:
0590: Class clazz = compatibleNumber(classA, classB);
0591: if (isPrimitive() && other.isPrimitive()) {
0592: compat = new Type(clazz, convertToPrimitive(clazz));
0593: } else {
0594: compat = new Type(clazz);
0595: }
0596: } else {
0597: compat = new Type(findCommonBaseClass(classA, classB));
0598: }
0599:
0600: if (isNonNull() && other.isNonNull()) {
0601: compat = compat.toNonNull();
0602: }
0603:
0604: return compat;
0605: }
0606:
0607: /**
0608: * Returns the most specific common superclass or interface that can be
0609: * used to represent both of the specified classes. Null is only returned
0610: * if either class refers to a primitive type and isn't the same as the
0611: * other class.
0612: */
0613: public static Class findCommonBaseClass(Class a, Class b) {
0614: Class clazz = findCommonBaseClass0(a, b);
0615:
0616: if (clazz != null && clazz.isInterface()) {
0617: // Only return interface if it actually defines something.
0618: if (clazz.getMethods().length <= 0) {
0619: //clazz = Object.class;
0620: }
0621: }
0622:
0623: return clazz;
0624: }
0625:
0626: private static Class findCommonBaseClass0(Class a, Class b) {
0627: if (a == b) {
0628: return a;
0629: }
0630:
0631: if (a.isPrimitive() || b.isPrimitive()) {
0632: return null;
0633: }
0634:
0635: if (a.isArray() && b.isArray()) {
0636: Class clazz = findCommonBaseClass(a.getComponentType(), b
0637: .getComponentType());
0638:
0639: if (clazz == null) {
0640: return Object.class;
0641: }
0642:
0643: return Array.newInstance(clazz, 0).getClass();
0644: }
0645:
0646: // Determine the intersection set of all the classes, superclasses and
0647: // interfaces from the passed in classes.
0648: Set set = new HashSet(19);
0649: addToClassSet(set, a);
0650:
0651: Set setB = new HashSet(19);
0652: addToClassSet(setB, b);
0653:
0654: set.retainAll(setB);
0655:
0656: int size = set.size();
0657: if (size == 1) {
0658: return (Class) set.iterator().next();
0659: } else if (size == 0) {
0660: return Object.class;
0661: }
0662:
0663: // Reduce the set by removing classes/interfaces that are extended and
0664: // interfaces that are implemented by other classes.
0665: Iterator i = set.iterator();
0666: while (i.hasNext()) {
0667: Class x = (Class) i.next();
0668: Iterator j = set.iterator();
0669: while (j.hasNext()) {
0670: Class y = (Class) j.next();
0671: if (x != y && x.isAssignableFrom(y)) {
0672: i.remove();
0673: break;
0674: }
0675: }
0676: }
0677:
0678: size = set.size();
0679: if (size == 1) {
0680: return (Class) set.iterator().next();
0681: } else if (size == 0) {
0682: return Object.class;
0683: }
0684:
0685: // Reduce the set by discarding interfaces.
0686: i = set.iterator();
0687: while (i.hasNext()) {
0688: if (((Class) i.next()).isInterface()) {
0689: i.remove();
0690: }
0691: }
0692:
0693: if (set.size() == 1) {
0694: return (Class) set.iterator().next();
0695: }
0696:
0697: return Object.class;
0698: }
0699:
0700: private static void addToClassSet(Set set, Class clazz) {
0701: if (clazz == null || !set.add(clazz)) {
0702: return;
0703: }
0704:
0705: addToClassSet(set, clazz.getSuperclass());
0706:
0707: Class[] interfaces = clazz.getInterfaces();
0708: for (int i = 0; i < interfaces.length; i++) {
0709: addToClassSet(set, interfaces[i]);
0710: }
0711: }
0712:
0713: private static Class compatibleNumber(Class classA, Class classB) {
0714: if (classA == Integer.class) {
0715:
0716: if (classB == Integer.class || classB == Byte.class
0717: || classB == Short.class) {
0718:
0719: return Integer.class;
0720: }
0721:
0722: if (classB == Long.class) {
0723: return classB;
0724: }
0725: } else if (classA == Byte.class || classA == Short.class) {
0726: if (classB == Integer.class || classB == Byte.class
0727: || classB == Short.class) {
0728:
0729: return Integer.class;
0730: }
0731:
0732: if (classB == Long.class) {
0733: return classB;
0734: }
0735:
0736: if (classB == Float.class) {
0737: return Float.class;
0738: }
0739: } else if (classA == Float.class) {
0740: if (classB == Float.class) {
0741: return classB;
0742: }
0743:
0744: if (classB == Byte.class || classB == Short.class) {
0745: return Float.class;
0746: }
0747: } else if (classA == Long.class) {
0748: if (classB == Integer.class || classB == Byte.class
0749: || classB == Short.class || classB == Long.class) {
0750:
0751: return Long.class;
0752: }
0753: }
0754:
0755: return Double.class;
0756: }
0757:
0758: /**
0759: * If class passed in represents a primitive type, its object peer is
0760: * returned. Otherwise, it is returned unchanged.
0761: */
0762: private static Class convertToObject(Class type) {
0763: if (type == int.class) {
0764: return Integer.class;
0765: } else if (type == boolean.class) {
0766: return Boolean.class;
0767: } else if (type == byte.class) {
0768: return Byte.class;
0769: } else if (type == char.class) {
0770: return Character.class;
0771: } else if (type == short.class) {
0772: return Short.class;
0773: } else if (type == long.class) {
0774: return Long.class;
0775: } else if (type == float.class) {
0776: return Float.class;
0777: } else if (type == double.class) {
0778: return Double.class;
0779: } else if (type == void.class) {
0780: return Void.class;
0781: } else {
0782: return type;
0783: }
0784: }
0785:
0786: /**
0787: * If class passed in has a primitive type peer, it is returned.
0788: * Otherwise, it is returned unchanged.
0789: */
0790: private static Class convertToPrimitive(Class type) {
0791: if (type == Integer.class) {
0792: return int.class;
0793: } else if (type == Boolean.class) {
0794: return boolean.class;
0795: } else if (type == Byte.class) {
0796: return byte.class;
0797: } else if (type == Character.class) {
0798: return char.class;
0799: } else if (type == Short.class) {
0800: return short.class;
0801: } else if (type == Long.class) {
0802: return long.class;
0803: } else if (type == Float.class) {
0804: return float.class;
0805: } else if (type == Double.class) {
0806: return double.class;
0807: } else if (type == Void.class) {
0808: return void.class;
0809: } else {
0810: return type;
0811: }
0812: }
0813:
0814: /**
0815: * Returns the conversion cost of assigning the given type to this type.
0816: * Conversions are allowed between arrays as well if they have the
0817: * same dimensions. If no legal conversion exists, -1 is returned.
0818: * The conversion costs are as follows:
0819: *
0820: * <ol>
0821: * <li>any type is assignable by its own type
0822: * <li>any superclass type is assignable by a subclass type
0823: * <li>any object with a primitive peer can be converted to its primitive
0824: * <li>any primitive can be converted to its object peer
0825: * <li>any byte or short can be widened to an int
0826: * <li>any byte, short or int can be widened to a long
0827: * <li>any byte or short can be widened to a float
0828: * <li>any primitive number can be widened to a double
0829: * <li>any Number object can be converted to a primitive and widened
0830: * <li>any primitive number can be converted to a Number object
0831: * <li>any primitive number can be widened to a Number object
0832: * <li>any Number object can be widened to another Number object
0833: * <li>any primitive number can be narrowed to a double
0834: * <li>any Number object can be narrowed to a double
0835: * <li>any primitive number can be narrowed to a Double object
0836: * <li>any Number object can be narrowed to a Double object
0837: * <li>any primitive number can be narrowed to a long
0838: * <li>any Number object can be narrowed to a long
0839: * <li>any primitive number can be narrowed to a Long object
0840: * <li>any Number object can be narrowed to a Long object
0841: * <li>any primitive number can be narrowed to a float
0842: * <li>any Number object can be narrowed to a float
0843: * <li>any primitive number can be narrowed to a Float object
0844: * <li>any Number object can be narrowed to a Float object
0845: * <li>any primitive number can be narrowed to a int
0846: * <li>any Number object can be narrowed to a int
0847: * <li>any primitive number can be narrowed to a Integer object
0848: * <li>any Number object can be narrowed to a Integer object
0849: * <li>any primitive number can be narrowed to a short
0850: * <li>any Number object can be narrowed to a short
0851: * <li>any primitive number can be narrowed to a Short object
0852: * <li>any Number object can be narrowed to a Short object
0853: * <li>any primitive number can be narrowed to a byte
0854: * <li>any Number object can be narrowed to a byte
0855: * <li>any primitive number can be narrowed to a Byte object
0856: * <li>any Number object can be narrowed to a Byte object
0857: * <li>any primitive can be converted to an object
0858: * <li>NULL_TYPE can be converted to any object
0859: * <li>anything can be converted to a String
0860: * </ol>
0861: *
0862: * @return the conversion cost, or -1 if the other can't assign to this
0863: */
0864: public int convertableFrom(Type other) {
0865: if (equals(other)) {
0866: return 1;
0867: }
0868:
0869: Class this Nat = mNaturalClass;
0870: Class otherNat = other.mNaturalClass;
0871:
0872: if (this Nat.isAssignableFrom(otherNat)) {
0873: return 2;
0874: }
0875:
0876: if (other == NULL_TYPE && !this Nat.isPrimitive()) {
0877: return 38;
0878: }
0879:
0880: if (this Nat.isArray() && otherNat.isArray()) {
0881: // Get dimensions of each array.
0882: int this Dim = 0;
0883: while (this Nat.isArray()) {
0884: this Dim++;
0885: this Nat = this Nat.getComponentType();
0886: }
0887:
0888: int otherDim = 0;
0889: while (otherNat.isArray()) {
0890: otherDim++;
0891: otherNat = otherNat.getComponentType();
0892: }
0893:
0894: if (this Dim != otherDim) {
0895: return -1;
0896: }
0897: }
0898:
0899: int aCode = typeCode(this Nat);
0900: if (aCode < 0) {
0901: return -1;
0902: }
0903: int bCode = typeCode(otherNat);
0904: if (bCode < 0) {
0905: if (aCode == 18) {
0906: // Anything can be converted to a String.
0907: return 39;
0908: } else {
0909: return -1;
0910: }
0911: }
0912:
0913: return mCostTable[bCode][aCode];
0914: }
0915:
0916: private static int typeCode(Class clazz) {
0917: if (clazz.isPrimitive()) {
0918: if (clazz == boolean.class) {
0919: return 0;
0920: } else if (clazz == char.class) {
0921: return 1;
0922: } else if (clazz == byte.class) {
0923: return 2;
0924: } else if (clazz == short.class) {
0925: return 3;
0926: } else if (clazz == int.class) {
0927: return 4;
0928: } else if (clazz == float.class) {
0929: return 5;
0930: } else if (clazz == long.class) {
0931: return 6;
0932: } else if (clazz == double.class) {
0933: return 7;
0934: }
0935: } else {
0936: if (clazz == Boolean.class) {
0937: return 8;
0938: } else if (clazz == Character.class) {
0939: return 9;
0940: } else if (clazz == Byte.class) {
0941: return 10;
0942: } else if (clazz == Short.class) {
0943: return 11;
0944: } else if (clazz == Integer.class) {
0945: return 12;
0946: } else if (clazz == Float.class) {
0947: return 13;
0948: } else if (clazz == Long.class) {
0949: return 14;
0950: } else if (clazz == Double.class) {
0951: return 15;
0952: } else if (Number.class.isAssignableFrom(clazz)) {
0953: return 16;
0954: } else if (clazz == Object.class) {
0955: return 17;
0956: } else if (clazz == String.class) {
0957: return 18;
0958: }
0959: }
0960:
0961: return -1;
0962: }
0963:
0964: // 19 by 19 two dimensional byte array. [from][to]
0965: private static byte[][] mCostTable = {
0966: //0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
0967:
0968: { 1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1,
0969: -1, -1, 37, 39 }, // 0
0970: { -1, 1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1,
0971: -1, -1, 37, 39 }, // 1
0972:
0973: { -1, -1, 1, 5, 5, 7, 6, 8, -1, -1, 4, 11, 11, 11, 11, 11,
0974: 10, 37, 39 }, // 2
0975: { -1, -1, 33, 1, 5, 7, 6, 8, -1, -1, 35, 4, 11, 11, 11, 11,
0976: 10, 37, 39 }, // 3
0977: { -1, -1, 33, 29, 1, 21, 6, 8, -1, -1, 35, 31, 4, 23, 11,
0978: 11, 10, 37, 39 }, // 4
0979: { -1, -1, 33, 29, 25, 1, 17, 8, -1, -1, 35, 31, 27, 4, 19,
0980: 11, 10, 37, 39 }, // 5
0981: { -1, -1, 33, 29, 25, 21, 1, 13, -1, -1, 35, 31, 27, 23, 4,
0982: 15, 10, 37, 39 }, // 6
0983: { -1, -1, 33, 29, 25, 21, 17, 1, -1, -1, 35, 31, 27, 23,
0984: 19, 4, 10, 37, 39 }, // 7
0985:
0986: { 3, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
0987: -1, -1, 2, 39 }, // 8
0988: { -1, 3, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1,
0989: -1, -1, 2, 39 }, // 9
0990:
0991: { -1, -1, 3, 9, 9, 9, 9, 9, -1, -1, 1, 12, 12, 12, 12, 12,
0992: 2, 2, 39 }, // 10
0993: { -1, -1, 34, 3, 9, 9, 9, 9, -1, -1, 36, 1, 12, 12, 12, 12,
0994: 2, 2, 39 }, // 11
0995: { -1, -1, 34, 30, 3, 22, 9, 9, -1, -1, 36, 32, 1, 24, 12,
0996: 12, 2, 2, 39 }, // 12
0997: { -1, -1, 34, 30, 26, 3, 18, 9, -1, -1, 36, 32, 28, 1, 20,
0998: 12, 2, 2, 39 }, // 13
0999: { -1, -1, 34, 30, 26, 22, 3, 14, -1, -1, 36, 32, 28, 24, 1,
1000: 16, 2, 2, 39 }, // 14
1001: { -1, -1, 34, 30, 26, 22, 18, 3, -1, -1, 36, 32, 28, 24,
1002: 20, 1, 2, 2, 39 }, // 15
1003:
1004: { -1, -1, 34, 30, 26, 22, 18, 14, -1, -1, 36, 32, 28, 24,
1005: 20, 16, 1, 2, 39 }, // 16
1006:
1007: { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1008: -1, -1, -1, 1, 39 }, // 17
1009: { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1010: -1, -1, -1, 2, 1 }, // 18
1011: { 66, 82, 73, 65, 78, 32, 83, 32, 79, 39, 78, 69, 73, 76,
1012: 76, -1, -1, -1, -1 } // 19
1013: };
1014: }
|