0001: /*
0002:
0003: Derby - Class org.apache.derby.iapi.services.loader.ClassInspector
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.iapi.services.loader;
0023:
0024: import org.apache.derby.iapi.services.sanity.SanityManager;
0025:
0026: import org.apache.derby.iapi.error.StandardException;
0027:
0028: import org.apache.derby.iapi.reference.SQLState;
0029:
0030: import java.lang.reflect.*;
0031: import java.util.StringTokenizer;
0032: import java.util.List;
0033: import java.util.ArrayList;
0034: import java.util.NoSuchElementException;
0035: import java.util.Collections;
0036:
0037: /**
0038: Methods to find out relationships between classes and methods within a class.
0039: All class names within this interface are treated as java language class names,
0040: e.g. int, COM.foo.Myclass, int[], java.lang.Object[]. That is java internal
0041: class names as defined in the class file format are not understood.
0042: */
0043: public final class ClassInspector {
0044: private static final String[] primTypeNames = { "boolean", "byte",
0045: "char", "short", "int", "long", "float", "double" };
0046:
0047: // collect these as static, instead of each time allocates these new
0048: // Strings for every method resolution
0049:
0050: private static final String[] nonPrimTypeNames = {
0051: "java.lang.Boolean", "java.lang.Byte",
0052: "java.lang.Character", "java.lang.Short",
0053: "java.lang.Integer", "java.lang.Long", "java.lang.Float",
0054: "java.lang.Double" };
0055:
0056: private final ClassFactory cf;
0057:
0058: /**
0059: DO NOT USE! use the method in ClassFactory.
0060: */
0061: public ClassInspector(ClassFactory cf) {
0062: this .cf = cf;
0063: }
0064:
0065: /**
0066: * Is the given object an instance of the named class?
0067: *
0068: * @param className The name of the class
0069: * @param obj The object to test to see if it's an instance
0070: * of the named class
0071: *
0072: * @return true if obj is an instanceof className, false if not
0073: */
0074: public boolean instanceOf(String className, Object obj)
0075: throws ClassNotFoundException {
0076: Class clazz = getClass(className);
0077: // is className an untyped null
0078: if (clazz == null)
0079: return false;
0080:
0081: return clazz.isInstance(obj);
0082: }
0083:
0084: /**
0085: * Is one named class assignable to another named class or interface?
0086: *
0087: * @param fromClassName The name of the class to be assigned
0088: * @param toClassName The name of the class to be assigned to
0089: *
0090: * @return true if an object of type fromClass can be assigned to an
0091: * object of type toClass, false if not.
0092: */
0093: public boolean assignableTo(String fromClassName, String toClassName) {
0094: try {
0095: Class toClass = getClass(toClassName);
0096: // is toClass an untyped null
0097: if (toClass == null) {
0098: return false;
0099: }
0100:
0101: Class fromClass = getClass(fromClassName);
0102:
0103: // is fromClass an untyped null
0104: if (fromClass == null)
0105: return !toClass.isPrimitive() || (toClass == Void.TYPE);
0106:
0107: return toClass.isAssignableFrom(fromClass);
0108: } catch (ClassNotFoundException cnfe) {
0109: /* If either class can't be found, they can't be assigned */
0110: return false;
0111: }
0112: }
0113:
0114: /**
0115: * Does the named class exist, and is it accessible?
0116: *
0117: * @param className The name of the class to test for existence
0118: *
0119: * @return true if the class exists and is accessible, false if not
0120: */
0121: public boolean accessible(String className)
0122: throws ClassNotFoundException {
0123: Class theClass = getClass(className);
0124: if (theClass == null)
0125: return false;
0126:
0127: /* Classes must be public to be accessible */
0128: if (!Modifier.isPublic(theClass.getModifiers()))
0129: return false;
0130:
0131: return true;
0132: }
0133:
0134: /**
0135: * Get the Java name of the return type from a Member representing
0136: * a method or the type of a Member representing a field.
0137: *
0138: * @param member A Member representing the method for
0139: * which we want the return type.
0140: *
0141: * @return A Java-language-style string describing the return type of
0142: * the method (for example, it returns "int" instead of "I".
0143: */
0144: public String getType(Member member) {
0145: Class type;
0146:
0147: if (member instanceof Method)
0148: type = ((Method) member).getReturnType();
0149: else if (member instanceof Field)
0150: type = ((Field) member).getType();
0151: else if (member instanceof Constructor)
0152: type = ((Constructor) member).getDeclaringClass();
0153: else
0154: type = Void.TYPE;
0155:
0156: return ClassInspector.readableClassName(type);
0157: }
0158:
0159: /**
0160: * Find a public method that implements a given signature.
0161: * The signature is given using the full Java class names of the types.
0162: <BR>
0163: * A untyped null paramter is indicated by passing in an empty string ("")
0164: * as its class name.
0165: <BR>
0166: If receiverType respresents an interface then the methods of java.lang.Object
0167: arer included in the candidate list.
0168: <BR>
0169: If the caller is simply checking to see that a public method with the
0170: specified name exists, regardless of the signature, exists, then the
0171: caller should pass in a null for parmTypes. (This is useful for checking
0172: the validity of a method alias when creating one.)
0173: <BR>
0174: We use a two-pass algorithm to resolve methods. In the first pass, we
0175: use all "object" types to try to match a method. If this fails, in the
0176: second pass, an array of "primitive" types (if the parameter has one,
0177: otherwise the same object type is used) is passed in, as well as the
0178: "object" type array. For each parameter of a method, we try to match it
0179: against either the "object" type, or the "primitive" type. Of all the
0180: qualified candidate methods found, we choose the closest one to the input
0181: parameter types. This involves comparing methods whose parameters are
0182: mixed "object" and "primitive" types in the second pass. This is
0183: eventually handled in classConvertableFromTo.
0184: *
0185: * @param receiverType The class name of the receiver
0186: * @param methodName The name of the method
0187: * @param parmTypes An array of class names representing the
0188: * parameter types. Pass a zero-element array if
0189: * there are no parameters. Pass a null if it is
0190: * okay to match any signature.
0191: * @param primParmTypes This is used in the second pass of the two-pass
0192: * method resolution algorithm. Use primitive type
0193: * if it has one, otherwise use same object type
0194: * @param isParam Array of booleans telling whether parameter is a ?.
0195: * @param staticMethod Find a static method.
0196: @param repeatLastParameter If true the last parameter may be repeated any number of times (total count must be greater than one).
0197: If false the laste parameter is matched as usual. This also requires an exact match on the last parameter type.
0198: *
0199: * @return A Member representing the matching method. Returns null
0200: * if no such method.
0201: *
0202: * @exception ClassNotFoundException One or more of the classes does
0203: * not exist.
0204: * @exception StandardException Thrown on ambiguous method invocation.
0205: *
0206: * @see Member
0207: * @see Modifier
0208: */
0209: public Member findPublicMethod(String receiverType,
0210: String methodName, String[] parmTypes,
0211: String[] primParmTypes, boolean[] isParam,
0212: boolean staticMethod, boolean repeatLastParameter)
0213: throws ClassNotFoundException, StandardException {
0214: Class receiverClass = getClass(receiverType);
0215: if (receiverClass == null)
0216: return null;
0217:
0218: // primitives don't have methods
0219: // note that arrays do since they are objects they have
0220: // all the methods of java.lang.Object
0221: if (receiverClass.isPrimitive()) {
0222: return null;
0223: }
0224:
0225: // if parmTypes is null, then the caller is simply
0226: // looking to see if any public method with the
0227: // specified name exists, regardless of its signature
0228: if (parmTypes == null) {
0229: Method[] methods = receiverClass.getMethods();
0230:
0231: for (int index = 0; index < methods.length; index++) {
0232: if (staticMethod) {
0233: if (!Modifier.isStatic(methods[index]
0234: .getModifiers())) {
0235: continue;
0236: }
0237: }
0238:
0239: if (methodName.equals(methods[index].getName())) {
0240: // We found a match
0241: return methods[index];
0242: }
0243: }
0244: // No match
0245: return null;
0246: }
0247:
0248: // convert the parameter types to classes
0249: Class[] paramClasses = new Class[parmTypes.length];
0250: Class[] primParamClasses = null;
0251: if (primParmTypes != null)
0252: primParamClasses = new Class[primParmTypes.length];
0253: for (int i = 0; i < paramClasses.length; i++) {
0254: paramClasses[i] = getClass(parmTypes[i]);
0255: if (primParmTypes == null)
0256: continue;
0257: if (primParmTypes[i].equals(parmTypes[i])) // no separate primitive
0258: primParamClasses[i] = null;
0259: else
0260: primParamClasses[i] = getClass(primParmTypes[i]);
0261: }
0262:
0263: // no overloading possible if there are no arguments, so perform
0264: // an exact match lookup.
0265: if (paramClasses.length == 0) {
0266:
0267: try {
0268: Method method = receiverClass.getMethod(methodName,
0269: paramClasses);
0270:
0271: if (staticMethod) {
0272: if (!Modifier.isStatic(method.getModifiers()))
0273: return null;
0274: }
0275:
0276: return method;
0277:
0278: } catch (NoSuchMethodException nsme2) {
0279:
0280: // if we are an interface then the method could be defined on Object
0281: if (!receiverClass.isInterface())
0282: return null;
0283: }
0284: }
0285:
0286: // now the tricky method resolution
0287: Member[] methodList = receiverClass.getMethods();
0288: // if we have an interface we need to add the methods of Object into the mix
0289: if (receiverClass.isInterface()) {
0290:
0291: Member[] objectMethods = java.lang.Object.class
0292: .getMethods();
0293: if (methodList.length == 0) {
0294: methodList = objectMethods;
0295: } else {
0296: Member[] set = new Member[methodList.length
0297: + objectMethods.length];
0298: System.arraycopy(methodList, 0, set, 0,
0299: methodList.length);
0300: System.arraycopy(objectMethods, 0, set,
0301: methodList.length, objectMethods.length);
0302: methodList = set;
0303: }
0304: }
0305:
0306: return resolveMethod(receiverClass, methodName, paramClasses,
0307: primParamClasses, isParam, staticMethod,
0308: repeatLastParameter, methodList);
0309: }
0310:
0311: /**
0312: * Find a public field for a class.
0313: This follows the sematics of the java compiler for locating a field.
0314: This means if a field fieldName exists in the class with package, private or
0315: protected then an error is raised. Even if the field hides a field fieldName
0316: in a super-class/super--interface. See the JVM spec on fields.
0317: *
0318: * @param receiverType The class name of the receiver
0319: * @param fieldName The name of the field
0320: * @param staticField Find a static field
0321: *
0322: * @return A Member representing the matching field.
0323: * @exception StandardException Class or field does not exist or is not public or a security exception.
0324: *
0325: * @see Member
0326: * @see Modifier
0327: */
0328: public Member findPublicField(String receiverType,
0329: String fieldName, boolean staticField)
0330: throws StandardException {
0331:
0332: Exception e = null;
0333: try {
0334:
0335: Class receiverClass = getClass(receiverType);
0336: if (receiverClass == null)
0337: return null;
0338: if (receiverClass.isArray() || receiverClass.isPrimitive()) {
0339: // arrays don't have fields (the fake field 'length' is not returned here)
0340: return null;
0341: }
0342:
0343: int modifier = staticField ? (Modifier.PUBLIC | Modifier.STATIC)
0344: : Modifier.PUBLIC;
0345:
0346: // Look for a public field first
0347: Field publicField = receiverClass.getField(fieldName);
0348:
0349: if ((publicField.getModifiers() & modifier) == modifier) {
0350: /*
0351: If the class is an interface then we avoid looking for a declared field
0352: that can hide a super-class's public field and not be accessable. This is because
0353: a interface's fields are always public. This avoids a security check.
0354: */
0355: if (receiverClass.isInterface()
0356: || (publicField.getDeclaringClass()
0357: .equals(receiverClass)))
0358: return publicField;
0359:
0360: /*
0361: Now check to see if there is a declared field that hides the public field.
0362: */
0363:
0364: try {
0365:
0366: Field declaredField = receiverClass
0367: .getDeclaredField(fieldName);
0368:
0369: if (SanityManager.DEBUG) {
0370:
0371: if ((declaredField.getModifiers() & Modifier.PUBLIC) == Modifier.PUBLIC)
0372: SanityManager
0373: .THROWASSERT("declared field not expected to be public here "
0374: + declaredField);
0375: }
0376:
0377: } catch (NoSuchFieldException nsfe) {
0378:
0379: // no field hides the public field in the super class
0380: return publicField;
0381: }
0382: }
0383:
0384: } catch (ClassNotFoundException cnfe) {
0385: e = cnfe;
0386: } catch (NoSuchFieldException nsfep) {
0387: e = nsfep;
0388: } catch (SecurityException se) {
0389: e = se;
0390: }
0391:
0392: throw StandardException.newException(
0393: staticField ? SQLState.LANG_NO_STATIC_FIELD_FOUND
0394: : SQLState.LANG_NO_FIELD_FOUND, e, fieldName,
0395: receiverType);
0396: }
0397:
0398: /**
0399: * Find a public constructor that implements a given signature.
0400: * The signature is given using the full Java class names of the types.
0401: <BR>
0402: * A untyped null paramter is indicated by passing in an empty string ("")
0403: * as its class name.
0404: *
0405: * @param receiverType The class name of the receiver
0406: * @param parmTypes An array of class names representing the
0407: * parameter types. Pass a zero-element array if
0408: * there are no parameters.
0409: * @param primParmTypes This is used in the second pass of the two-pass
0410: * method resolution algorithm. Use primitive type
0411: * if it has one, otherwise use same object type
0412: * @param isParam Array of booleans telling whether parameter is a ?.
0413: *
0414: * @return A Member representing the matching constructor. Returns null
0415: * if no such constructor.
0416: *
0417: * @exception ClassNotFoundException One or more of the classes does
0418: * not exist.
0419: * @exception StandardException Thrown on ambiguous constructor invocation.
0420: *
0421: * @see Member
0422: * @see Modifier
0423: */
0424: public Member findPublicConstructor(String receiverType,
0425: String[] parmTypes, String[] primParmTypes,
0426: boolean[] isParam) throws ClassNotFoundException,
0427: StandardException {
0428: Class receiverClass = getClass(receiverType);
0429: if (receiverClass == null)
0430: return null;
0431:
0432: // arrays, primitives, and interfaces do not have constructors
0433: if (receiverClass.isArray() || receiverClass.isPrimitive()
0434: || receiverClass.isInterface()) {
0435: return null;
0436: }
0437:
0438: // convert the parameter types to classes
0439: Class[] paramClasses = new Class[parmTypes.length];
0440: Class[] primParamClasses = null;
0441: if (primParmTypes != null)
0442: primParamClasses = new Class[primParmTypes.length];
0443: boolean unknownParameters = false;
0444: for (int i = 0; i < paramClasses.length; i++) {
0445: paramClasses[i] = getClass(parmTypes[i]);
0446: if (paramClasses[i] == null)
0447: unknownParameters = true;
0448: if (primParmTypes == null)
0449: continue;
0450: if (primParmTypes[i].equals(parmTypes[i])) // no separate primitive
0451: primParamClasses[i] = null;
0452: else
0453: primParamClasses[i] = getClass(primParmTypes[i]);
0454: }
0455:
0456: try {
0457:
0458: if (!unknownParameters && (primParmTypes == null)) {
0459: // look for an exact match for first pass
0460: Member method = receiverClass
0461: .getConstructor(paramClasses);
0462:
0463: return method;
0464: }
0465:
0466: } catch (NoSuchMethodException nsme) {
0467:
0468: // no overloading possible if there are no arguments
0469: if (paramClasses.length == 0)
0470: return null;
0471:
0472: // now the tricky method resolution
0473: }
0474:
0475: // name is only used for debugging
0476: return resolveMethod(receiverClass, "<init>", paramClasses,
0477: primParamClasses, isParam, false, false, receiverClass
0478: .getConstructors());
0479: }
0480:
0481: /**
0482: * Get the parameter types for a method described by a Member as a String[].
0483: *
0484: * @param method A Member describing a method
0485: *
0486: * @return A String[] describing the parameters of the method
0487: */
0488: public String[] getParameterTypes(Member method) {
0489:
0490: Class[] parameterClasses;
0491: if (method instanceof Method) {
0492: parameterClasses = ((Method) method).getParameterTypes();
0493: } else {
0494: parameterClasses = ((Constructor) method)
0495: .getParameterTypes();
0496: }
0497:
0498: String[] parameterTypes = new String[parameterClasses.length];
0499:
0500: for (int i = 0; i < parameterTypes.length; i++) {
0501: parameterTypes[i] = ClassInspector
0502: .readableClassName(parameterClasses[i]);
0503: }
0504:
0505: return parameterTypes;
0506: }
0507:
0508: /**
0509: * Determine whether a type is a Java primitive, like int or boolean
0510: *
0511: * @param typeName The name of the Java type
0512: *
0513: * @return true if it's a primitive type
0514: */
0515: public static boolean primitiveType(String typeName) {
0516: for (int i = 0; i < primTypeNames.length; i++) {
0517: if (typeName.equals(primTypeNames[i]))
0518: return true;
0519: }
0520:
0521: return false;
0522: }
0523:
0524: /**
0525: * Tricky function to resolve a method. If primParamClasses is null
0526: * we know it's first pass. First pass try to match as all "object"
0527: * types, second pass try to match any combination of "object" and
0528: * "primitive" types. Find the closest match among all the qualified
0529: * candidates. If there's a tie, it's ambiguous.
0530: *
0531: * @param receiverClass the class who holds the methods
0532: * @param methodName the name of method
0533: * @param paramClasses object type classes of input parameters
0534: * @param primParamClasses primitive type classes or null
0535: * @param isParam isParam (for ?) array
0536: * @param staticMethod static method or not
0537: * @param methods method stack
0538: * @return the matched method
0539: *
0540: **/
0541: private Member resolveMethod(Class receiverClass,
0542: String methodName, Class[] paramClasses,
0543: Class[] primParamClasses, boolean[] isParam,
0544: boolean staticMethod, boolean repeatLastParameter,
0545: Member[] methods) throws StandardException {
0546:
0547: if (SanityManager.DEBUG) {
0548: if (SanityManager.DEBUG_ON("MethodResolutionInfo")) {
0549: SanityManager.DEBUG("MethodResolutionInfo",
0550: "MRI - Begin method resolution trace for "
0551: + methodName + "() with "
0552: + paramClasses.length
0553: + (repeatLastParameter ? "+" : "")
0554: + " parameters");
0555:
0556: for (int parmCtr = 0; parmCtr < paramClasses.length; parmCtr++) {
0557: SanityManager
0558: .DEBUG(
0559: "MethodResolutionInfo",
0560: "MRI - Parameter #"
0561: + parmCtr
0562: + " is of type "
0563: + (paramClasses[parmCtr] == null ? "null"
0564: : paramClasses[parmCtr]
0565: .getName()));
0566: }
0567: }
0568: }
0569:
0570: /* Step through all the methods available in this class */
0571: int candidateIndex = -1;
0572:
0573: boolean firstTimeAround = true;
0574: boolean ambiguous;
0575: boolean somethingChanged;
0576: do {
0577:
0578: ambiguous = false;
0579: somethingChanged = false;
0580:
0581: nextMethod: for (int i = 0; i < methods.length; i++) {
0582:
0583: Member currentMethod = methods[i];
0584:
0585: // on second and later times around there will be null entries
0586: // also, don't compare ourself to ourself
0587: if ((currentMethod == null) || (i == candidateIndex)) {
0588: continue;
0589: }
0590:
0591: // must have the same number of parameters
0592: Class[] currentMethodParameters = currentMethod instanceof Method ? ((Method) currentMethod)
0593: .getParameterTypes()
0594: : ((Constructor) currentMethod)
0595: .getParameterTypes();
0596:
0597: // only check the basic stuff once
0598: if (firstTimeAround) {
0599:
0600: if (repeatLastParameter) {
0601: // match any number of parameters greater or equal to
0602: // the passed in number, but repeating the last type.
0603: if (currentMethodParameters.length < paramClasses.length) {
0604: methods[i] = null; // remove non-applicable methods
0605: continue;
0606: }
0607:
0608: } else {
0609:
0610: // regular match on parameter count
0611: if (currentMethodParameters.length != paramClasses.length) {
0612: methods[i] = null; // remove non-applicable methods
0613: continue;
0614: }
0615: }
0616:
0617: /* Look only at methods that match the modifiers */
0618: if (staticMethod
0619: && !Modifier.isStatic(currentMethod
0620: .getModifiers())) {
0621: methods[i] = null; // remove non-applicable methods
0622: continue;
0623: }
0624:
0625: /* Look only at methods with the right name */
0626: if (!methodName.startsWith("<")) {
0627: if (!methodName.equals(currentMethod.getName())) {
0628: methods[i] = null; // remove non-applicable methods
0629: continue;
0630: }
0631: }
0632:
0633: if (repeatLastParameter) {
0634: // With N parameters requested check all parameters from N-1 to end are equal
0635: // to the requested parameter.
0636: for (int pr = paramClasses.length - 1; pr < currentMethodParameters.length; pr++) {
0637: if (!currentMethodParameters[pr]
0638: .equals(paramClasses[paramClasses.length - 1])) {
0639: methods[i] = null; // remove non-applicable methods
0640: continue nextMethod;
0641: }
0642: }
0643: }
0644: }
0645:
0646: if (SanityManager.DEBUG) {
0647: if (SanityManager.DEBUG_ON("MethodResolutionInfo")) {
0648: SanityManager.DEBUG("MethodResolutionInfo",
0649: "MRI - Considering :"
0650: + currentMethod.toString());
0651: }
0652: }
0653:
0654: // can the required signature be converted to those of this method
0655: if (!signatureConvertableFromTo(paramClasses,
0656: primParamClasses, currentMethodParameters,
0657: isParam, false)) {
0658:
0659: if (SanityManager.DEBUG) {
0660: if (SanityManager
0661: .DEBUG_ON("MethodResolutionInfo")) {
0662: SanityManager.DEBUG("MethodResolutionInfo",
0663: "MRI - Skipping :"
0664: + currentMethod.toString());
0665: }
0666: }
0667:
0668: methods[i] = null; // remove non-applicable methods
0669: continue;
0670: }
0671:
0672: if (SanityManager.DEBUG) {
0673: if (SanityManager.DEBUG_ON("MethodResolutionInfo")) {
0674: SanityManager.DEBUG("MethodResolutionInfo",
0675: "MRI - Match found ");
0676: }
0677: }
0678:
0679: /* Is this the first match? */
0680: if (candidateIndex == -1) {
0681: candidateIndex = i;
0682: if (SanityManager.DEBUG) {
0683: if (SanityManager
0684: .DEBUG_ON("MethodResolutionInfo")) {
0685: SanityManager
0686: .DEBUG("MethodResolutionInfo",
0687: "MRI - Current method is now candidate");
0688: }
0689: }
0690: continue;
0691: }
0692:
0693: /* Not the first match, so find out which one, if either one,
0694: * has the best match on the parameters. (No narrowing
0695: * conversions.) 15.11 of Java Language Specification.
0696: */
0697:
0698: Member candidateMethod = methods[candidateIndex];
0699:
0700: // If the candidate method is more specific than the current
0701: // method then the candidate method is still the maximally specific method
0702: // Note at this point we could still have a ambiguous situation.
0703:
0704: boolean candidateMoreOrEqual = isMethodMoreSpecificOrEqual(
0705: candidateMethod, currentMethod, isParam);
0706: boolean currentMoreOrEqual = isMethodMoreSpecificOrEqual(
0707: currentMethod, candidateMethod, isParam);
0708: if (candidateMoreOrEqual && !currentMoreOrEqual) {
0709: if (SanityManager.DEBUG) {
0710: if (SanityManager
0711: .DEBUG_ON("MethodResolutionInfo")) {
0712: SanityManager
0713: .DEBUG("MethodResolutionInfo",
0714: "MRI - Candidate is still maximally specific");
0715: }
0716: }
0717: methods[i] = null; // remove non-applicable methods
0718: continue;
0719: }
0720:
0721: // if the current method is more specific than the candidiate
0722: // method then it becomes the new maximally specific method
0723: // Note at this point we could still have a ambiguous situation.
0724:
0725: if (currentMoreOrEqual && !candidateMoreOrEqual) {
0726: if (SanityManager.DEBUG) {
0727: if (SanityManager
0728: .DEBUG_ON("MethodResolutionInfo")) {
0729: SanityManager
0730: .DEBUG("MethodResolutionInfo",
0731: "MRI - Current method is now candidate, replaced previous candidate");
0732: }
0733: }
0734: methods[candidateIndex] = null; // remove non-applicable methods
0735: candidateIndex = i;
0736: somethingChanged = true;
0737: continue;
0738: }
0739:
0740: /* We have seen an ambiguous situation; one of the cases may
0741: * tie on each parameter.
0742: */
0743: ambiguous = true;
0744:
0745: if (SanityManager.DEBUG) {
0746: if (SanityManager.DEBUG_ON("MethodResolutionInfo")) {
0747: SanityManager.DEBUG("MethodResolutionInfo",
0748: "MRI - Ambiguous match");
0749: }
0750: }
0751: }
0752: firstTimeAround = false;
0753: } while (ambiguous && somethingChanged);
0754:
0755: if (SanityManager.DEBUG) {
0756: if (SanityManager.DEBUG_ON("MethodResolutionInfo")) {
0757: SanityManager.DEBUG("MethodResolutionInfo",
0758: "MRI - End method resolution trace for "
0759: + methodName + "()" + "\nMRI - ");
0760: }
0761: }
0762:
0763: /* Throw an exception here if the method invocation ambiguous */
0764: if (ambiguous) {
0765: /* Put the parameter type names into a single string */
0766: String parmTypesString = "";
0767: for (int i = 0; i < paramClasses.length; i++) {
0768: if (i != 0)
0769: parmTypesString += ", ";
0770: parmTypesString += (paramClasses[i] == null ? "null"
0771: : paramClasses[i].getName());
0772: if (primParamClasses != null
0773: && primParamClasses[i] != null)
0774: parmTypesString += "("
0775: + primParamClasses[i].getName() + ")";
0776: }
0777:
0778: throw StandardException.newException(
0779: SQLState.LANG_AMBIGUOUS_METHOD_INVOCATION,
0780: receiverClass.getName(), methodName,
0781: parmTypesString);
0782: }
0783:
0784: if (candidateIndex == -1)
0785: return null;
0786:
0787: if (SanityManager.DEBUG) {
0788: if (methods[candidateIndex] == null)
0789: SanityManager.THROWASSERT("methods is null at index "
0790: + candidateIndex);
0791: }
0792: return methods[candidateIndex];
0793: }
0794:
0795: /**
0796: Get (load) the class for the given class name.
0797: This method converts any java language class name
0798: into a Class object. This includes cases like String[]
0799: and primitive types.
0800: This will attempt to load the class from the application set.
0801:
0802: @exception ClassNotFoundException Class cannot be found, or
0803: a SecurityException or LinkageException was thrown loading the class.
0804: */
0805: public Class getClass(String className)
0806: throws ClassNotFoundException {
0807:
0808: if ((className == null) || (className.length() == 0)) {
0809: return null;
0810: }
0811:
0812: int arrayDepth = 0;
0813: int classNameLength = className.length();
0814:
0815: int position = classNameLength - 2;
0816:
0817: while ((position >= 0)
0818: && className.substring(position, position + 2).equals(
0819: "[]")) {
0820: arrayDepth++;
0821: position -= 2;
0822: classNameLength -= 2;
0823: }
0824:
0825: if (classNameLength <= 0) {
0826: // a bogus class name, let Class.forName deal with the error.
0827: return Class.forName(className);
0828: }
0829:
0830: if (arrayDepth != 0)
0831: className = className.substring(0, classNameLength);
0832:
0833: Class baseClass = null;
0834:
0835: if (classNameLength >= 3 && classNameLength <= 7) {
0836: if ("int".equals(className))
0837: baseClass = Integer.TYPE;
0838: else if ("short".equals(className))
0839: baseClass = Short.TYPE;
0840: else if ("boolean".equals(className))
0841: baseClass = Boolean.TYPE;
0842: else if ("byte".equals(className))
0843: baseClass = Byte.TYPE;
0844: else if ("float".equals(className))
0845: baseClass = Float.TYPE;
0846: else if ("double".equals(className))
0847: baseClass = Double.TYPE;
0848: else if ("long".equals(className))
0849: baseClass = Long.TYPE;
0850: else if ("char".equals(className))
0851: baseClass = Character.TYPE;
0852: else if ("void".equals(className))
0853: baseClass = Void.TYPE;
0854: }
0855:
0856: if (baseClass == null) {
0857: baseClass = cf.loadApplicationClass(className);
0858: }
0859:
0860: if (arrayDepth == 0)
0861: return baseClass;
0862:
0863: // need to create an actual instance of the array type
0864: // and get its class from that. There is no other documented
0865: // way to do this. While a getName() on an array class
0866: // returns [[[Lclassname; format it's not consistent
0867: // with primitive types, e.g.
0868: //
0869: // Integer.TYPE.getName() returns "int"
0870: // Class.forName(new int[0] returns "[I"
0871: //
0872:
0873: if (arrayDepth == 1)
0874: return Array.newInstance(baseClass, 0).getClass();
0875:
0876: return Array.newInstance(baseClass, new int[arrayDepth])
0877: .getClass();
0878: }
0879:
0880: /**
0881: Is method/constructor T more or equally specific than method U.
0882:
0883: See the Java Language Specification section 15.11.2.2.
0884: */
0885: private boolean isMethodMoreSpecificOrEqual(Member T, Member U,
0886: boolean[] isParam) {
0887:
0888: Class[] TC;
0889: Class[] UC;
0890:
0891: if (T instanceof Method) {
0892: if (!classConvertableFromTo(T.getDeclaringClass(), U
0893: .getDeclaringClass(), true))
0894: return false;
0895:
0896: TC = ((Method) T).getParameterTypes();
0897: UC = ((Method) U).getParameterTypes();
0898: } else {
0899: TC = ((Constructor) T).getParameterTypes();
0900: UC = ((Constructor) U).getParameterTypes();
0901: }
0902:
0903: return signatureConvertableFromTo(TC, null, UC, isParam, true);
0904: }
0905:
0906: /**
0907: * Can we convert a signature from fromTypes(primFromTypes) to toTypes.
0908: * "mixTypes" is a flag to show if object/primitive type conversion is
0909: * possible; this is used for comparing two candidate methods in the
0910: * second pass of the two pass method resolution.
0911: *
0912: * @param fromTypes from types' classes
0913: * @param primFromTypes primitive from types or null
0914: * @param toTypes to types' classes
0915: * @param isParam is parameter (?) or not
0916: * @param mixTypes mixing object/primitive types for comparison
0917: **/
0918: private boolean signatureConvertableFromTo(Class[] fromTypes,
0919: Class[] primFromTypes, Class[] toTypes, boolean[] isParam,
0920: boolean mixTypes) {
0921:
0922: // In the case repeatLastParameter was true, then the two methods may have
0923: // different numbers of parameters. We need to compare only the non-repeated
0924: // parameters, which is the number of input parameters.
0925:
0926: int checkCount = fromTypes.length;
0927: if (toTypes.length < checkCount)
0928: checkCount = toTypes.length;
0929:
0930: for (int i = 0; i < checkCount; i++) {
0931:
0932: Class fromClass = fromTypes[i];
0933: Class toClass = toTypes[i];
0934:
0935: // this means an untyped null was passed in. Can only ever be in the
0936: // from side as the null can only be in the signature passed in by
0937: // the caller of findPublicMethod. Any signatures of existing methods
0938: // are always typed.
0939: if (fromClass == null) {
0940:
0941: // primitive types are only considered on
0942: // the 2nd pass
0943: if (toClass.isPrimitive()) {
0944: if ((primFromTypes == null) // first pass
0945: || (isParam != null && !isParam[i])) {
0946: return false;
0947: }
0948: }
0949: continue;
0950: }
0951:
0952: if ((!classConvertableFromTo(fromClass, toClass, mixTypes))
0953: &&
0954: // primitive type, if any, also doesn't work
0955: ((primFromTypes == null)
0956: || (primFromTypes[i] == null) || (!classConvertableFromTo(
0957: primFromTypes[i], toClass, mixTypes))))
0958: return false;
0959: }
0960:
0961: return true;
0962: }
0963:
0964: /**
0965: * Can we convert a fromClass to toClass.
0966: * "mixTypes" is a flag to show if object/primitive type conversion is
0967: * possible; this is used for comparing two candidate methods in the
0968: * second pass of the two pass method resolution.
0969: *
0970: * @param fromClass from class
0971: * @param toClass to class
0972: * @param mixTypes mixing object/primitive types for comparison
0973: **/
0974: protected boolean classConvertableFromTo(Class fromClass,
0975: Class toClass, boolean mixTypes) {
0976:
0977: if (toClass.isAssignableFrom(fromClass)) {
0978: return true;
0979: }
0980:
0981: // When comparing two candidate methods to see which one is closer,
0982: // we want to mix object type and primitive type, because they could
0983: // both be chosen in the second pass. But when deciding if a method
0984: // is qualified (to be a candidate), we do not want to mix types at
0985: // any time, the reason is that we can NOT do more than one step
0986: // conversion: for example, input parameter is BigDecimal, we convert
0987: // it to double for method resolution, we can NOT convert it again to
0988: // Double to match a method. "(paramTypes, primParamTypes)" already
0989: // includes all the one-step conversions. But at any time we do want
0990: // to see if two primitives are convertable.
0991: if ((!(toClass.isPrimitive() && fromClass.isPrimitive()))
0992: && (!mixTypes))
0993: return false;
0994:
0995: // There are nine predefined Class objects to represent the eight
0996: // primitive Java types and void. We also handle prim vs. non-prim
0997: // conversion of the same type. boolean and double are only convertable
0998: // to themseleves. void should never be seen here. In the second pass
0999: // we treat primitive type and the corrsponding non-primitive type
1000: // uniformly
1001:
1002: String fromName = fromClass.getName(), toName = toClass
1003: .getName();
1004: if ((fromClass == Boolean.TYPE)
1005: || fromName.equals(nonPrimTypeNames[0])) {
1006: if ((toClass == Boolean.TYPE)
1007: || toName.equals(nonPrimTypeNames[0]))
1008: return true;
1009: } else if ((fromClass == Byte.TYPE)
1010: || fromName.equals(nonPrimTypeNames[1])) {
1011: if ((toClass == Byte.TYPE)
1012: || toName.equals(nonPrimTypeNames[1])
1013: ||
1014: // we never need to see if toClass is of wider "object" type,
1015: // because a wider "object" type and a narrower "primitive"
1016: // type can never both be candidate, eg, "int" and "Long" can
1017: // never both accomodate the same parameter; while "long" and
1018: // "Integer" can.
1019: (toClass == Short.TYPE)
1020: || (toClass == Integer.TYPE)
1021: || (toClass == Long.TYPE)
1022: || (toClass == Float.TYPE)
1023: || (toClass == Double.TYPE))
1024: return true;
1025: } else if ((fromClass == Character.TYPE)
1026: || fromName.equals(nonPrimTypeNames[2])) {
1027: if ((toClass == Character.TYPE)
1028: || toName.equals(nonPrimTypeNames[2])
1029: || (toClass == Integer.TYPE)
1030: || (toClass == Long.TYPE)
1031: || (toClass == Float.TYPE)
1032: || (toClass == Double.TYPE))
1033: return true;
1034: } else if ((fromClass == Short.TYPE)
1035: || fromName.equals(nonPrimTypeNames[3])) {
1036: if ((toClass == Short.TYPE)
1037: || toName.equals(nonPrimTypeNames[3])
1038: || (toClass == Integer.TYPE)
1039: || (toClass == Long.TYPE)
1040: || (toClass == Float.TYPE)
1041: || (toClass == Double.TYPE))
1042: return true;
1043: } else if ((fromClass == Integer.TYPE)
1044: || fromName.equals(nonPrimTypeNames[4])) {
1045: if ((toClass == Integer.TYPE)
1046: || toName.equals(nonPrimTypeNames[4])
1047: || (toClass == Long.TYPE)
1048: || (toClass == Float.TYPE)
1049: || (toClass == Double.TYPE))
1050: return true;
1051: } else if ((fromClass == Long.TYPE)
1052: || fromName.equals(nonPrimTypeNames[5])) {
1053: if ((toClass == Long.TYPE)
1054: || toName.equals(nonPrimTypeNames[5])
1055: || (toClass == Float.TYPE)
1056: || (toClass == Double.TYPE))
1057: return true;
1058: } else if ((fromClass == Float.TYPE)
1059: || fromName.equals(nonPrimTypeNames[6])) {
1060: if ((toClass == Float.TYPE)
1061: || toName.equals(nonPrimTypeNames[6])
1062: || (toClass == Double.TYPE))
1063: return true;
1064: } else if ((fromClass == Double.TYPE)
1065: || fromName.equals(nonPrimTypeNames[7])) {
1066: if ((toClass == Double.TYPE)
1067: || toName.equals(nonPrimTypeNames[7]))
1068: return true;
1069: }
1070:
1071: return false;
1072: }
1073:
1074: /**
1075: * Translate a JVM-style type descriptor to a Java-language-style type
1076: * name.
1077: *
1078: * @param clazz The String that contains the JVM type name
1079: *
1080: * @return The Java-language-style type name
1081: */
1082: public static String readableClassName(Class clazz) {
1083: if (!clazz.isArray())
1084: return clazz.getName();
1085:
1086: int arrayDepth = 0;
1087: do {
1088: arrayDepth++;
1089: clazz = clazz.getComponentType();
1090: } while (clazz.isArray());
1091:
1092: StringBuffer sb = new StringBuffer(clazz.getName());
1093:
1094: for (int i = 0; i < arrayDepth; i++) {
1095: sb.append("[]");
1096: }
1097:
1098: return sb.toString();
1099: }
1100:
1101: /**
1102: * Determine whether or not the received class can be
1103: * loaded.
1104: *
1105: * @param className The name of the class in question
1106: * @return True if className can be loaded, false otherwise
1107: */
1108: public static boolean classIsLoadable(String className) {
1109: try {
1110:
1111: Class.forName(className);
1112: return true;
1113:
1114: } catch (ClassNotFoundException ce) {
1115: return false;
1116: } catch (LinkageError ce) {
1117: return false;
1118: }
1119: }
1120:
1121: /**
1122: * Get the declaring class for a method.
1123: *
1124: * @param method A Member describing a method
1125: *
1126: * @return A String with the declaring class
1127: *
1128: * @see Member#getDeclaringClass
1129: */
1130: public String getDeclaringClass(Member method) {
1131: return method.getDeclaringClass().getName();
1132: }
1133:
1134: }
|