0001: /*
0002: * FuncSig.java --
0003: *
0004: * This class implements the internal representation of a Java
0005: * method or constructor signature.
0006: *
0007: * Copyright (c) 1997 Sun Microsystems, Inc.
0008: *
0009: * See the file "license.terms" for information on usage and
0010: * redistribution of this file, and for a DISCLAIMER OF ALL
0011: * WARRANTIES.
0012: *
0013: * RCS: @(#) $Id: FuncSig.java,v 1.15 2006/05/15 22:14:23 mdejong Exp $
0014: *
0015: */
0016:
0017: package tcl.lang;
0018:
0019: import tcl.lang.reflect.*;
0020: import java.lang.reflect.*;
0021: import java.util.*;
0022:
0023: /**
0024: * This class implements the internal representation of a Java method
0025: * or constructor signature. Because methods and constructors are very
0026: * similar to each other, the operations on method signatures and
0027: * constructor signatures are limped in this class of "function
0028: * signature."
0029: */
0030:
0031: class FuncSig implements InternalRep {
0032:
0033: // The class that a method signature is used against. In the case of a
0034: // static method call by java::call, targetCls is given by the <class>
0035: // argument to java::call. In the case of an instance method call,
0036: // targetCls is the class of the instance. targetCls is used to test
0037: // the validity of a cached FuncSig internal rep for method
0038: // signatures.
0039: //
0040: // targetCls is not used for class signatures.
0041:
0042: Class targetCls;
0043:
0044: // The PkgInvoker used to access the constructor or method.
0045:
0046: PkgInvoker pkgInvoker;
0047:
0048: // The constructor or method given by the field signature. You need to
0049: // apply the instanceof operator to determine whether it's a
0050: // Constructor or a Method.
0051: //
0052: // func may be a public, protected, package protected or private
0053: // member of the given class. Attempts to access func is subject to
0054: // the Java language access control rules. Public members can always
0055: // be accessed. Protected and package protected members can be
0056: // accessed only if a proper TclPkgInvoker class exists. Private
0057: // members can never be accessed.
0058: //
0059: // If the signature is a method signature and the specified method has
0060: // been overloaded, then func will point to the "most public" instance
0061: // of that method. The order of public-ness is ranked as the following
0062: // (a larger number means more public):
0063: //
0064: // RANK METHOD ACCESS TYPE CLASS ACCESS TYPE
0065: // 0 private any
0066: // 1 package protected protected
0067: // 1 protected protected
0068: // 1 public protected
0069: // 1 package protected public
0070: // 1 protected public
0071: // 2 public public
0072:
0073: Object func;
0074:
0075: // Stores all accessible instance methods for a Java class
0076: // Note that we use a Hashtable instead of a HashMap here
0077: // since these fields could be accessed from multiple
0078: // threads and the Hashtable class is synchronized.
0079:
0080: static Hashtable instanceMethodTable = new Hashtable();
0081: static Hashtable staticMethodTable = new Hashtable();
0082:
0083: /*
0084: *----------------------------------------------------------------------
0085: *
0086: * FuncSig --
0087: *
0088: * Creates a new FuncSig instance.
0089: *
0090: * Side effects:
0091: * Member fields are initialized.
0092: *
0093: *----------------------------------------------------------------------
0094: */
0095:
0096: FuncSig(Class cls, // Initial value for targetCls.
0097: PkgInvoker p, // Initial value for pkgInvoker.
0098: Object f) // Initial value for func.
0099: {
0100: targetCls = cls;
0101: pkgInvoker = p;
0102: func = f;
0103: }
0104:
0105: /*
0106: *----------------------------------------------------------------------
0107: *
0108: * duplicate --
0109: *
0110: * Make a copy of an object's internal representation.
0111: *
0112: * Results:
0113: * Returns a newly allocated instance of the appropriate type.
0114: *
0115: * Side effects:
0116: * None.
0117: *
0118: *----------------------------------------------------------------------
0119: */
0120:
0121: public InternalRep duplicate() {
0122: return new FuncSig(targetCls, pkgInvoker, func);
0123: }
0124:
0125: /**
0126: * Implement this no-op for the InternalRep interface.
0127: */
0128:
0129: public void dispose() {
0130: }
0131:
0132: /*
0133: *----------------------------------------------------------------------
0134: *
0135: * get --
0136: *
0137: * Returns the FuncSig internal representation of the constructor
0138: * or method that matches with the signature and the parameters.
0139: *
0140: * Results:
0141: * The FuncSig given by the signature. An exception will be
0142: * raised if the constructor provided is for an abstract
0143: * class, or if the class is not accessible.
0144: *
0145: * Side effects:
0146: * None.
0147: *
0148: *----------------------------------------------------------------------
0149: */
0150:
0151: static FuncSig get(Interp interp, // Current interpreter.
0152: Class cls, // If null, we are looking for a constructor
0153: // in signature. If non-null, we are looking
0154: // for a method of this class in signature.
0155: TclObject signature, // Method/constructor signature.
0156: TclObject[] argv, // Arguments.
0157: int startIdx, // Index of the first argument in argv
0158: int count, // Number of arguments to pass to the
0159: // constructor.
0160: boolean isStatic) // True if signature is for a static Method.
0161: throws TclException {
0162: boolean isConstructor = (cls == null);
0163:
0164: /* FIXME: commented out Method caching system
0165: * we comment out this code because it causes
0166: * the AmbiguousSignature-2.1 test to fail under Tcl Blend.
0167: * a new caching system might help but it is unclear if
0168: * determining a cache "match" is any less work then just searching.
0169:
0170:
0171: InternalRep rep = signature.getInternalRep();
0172:
0173: // If a valid FuncSig internal rep is already cached, return it
0174: // right away.
0175:
0176: if (rep instanceof FuncSig) {
0177: FuncSig tmp = (FuncSig)rep;
0178: Object func = tmp.func;
0179:
0180: if (isConstructor) {
0181: if ((func instanceof Constructor)
0182: && (((Constructor)func).getParameterTypes().length
0183: == count)) {
0184: return tmp;
0185: }
0186: } else {
0187: if ((func instanceof Method) && (tmp.targetCls == cls)
0188: && (((Method)func).getParameterTypes().length == count)) {
0189: return tmp;
0190: }
0191: }
0192: }
0193: */
0194:
0195: // Look up the constructor or method using the string rep of the
0196: // signature object.
0197: Object match;
0198: int sigLength = TclList.getLength(interp, signature);
0199: String methodName = null;
0200: TclObject class_or_method;
0201:
0202: if (sigLength == 0) {
0203: throw new TclException(interp, "bad signature \""
0204: + signature + "\"");
0205: } else if (sigLength == 1) {
0206: class_or_method = signature;
0207: } else {
0208: class_or_method = TclList.index(interp, signature, 0);
0209: }
0210:
0211: if (isConstructor) {
0212: cls = JavaInvoke.getClassByName(interp, class_or_method
0213: .toString());
0214: } else {
0215: methodName = class_or_method.toString();
0216: }
0217:
0218: if ((isConstructor || isStatic)
0219: && !PkgInvoker.isAccessible(cls)) {
0220: JavaInvoke.notAccessibleError(interp, cls);
0221: }
0222:
0223: if (isConstructor && Modifier.isAbstract(cls.getModifiers())) {
0224: throw new TclException(interp, "Class \""
0225: + JavaInfoCmd.getNameFromClass(cls)
0226: + "\" is abstract");
0227: }
0228:
0229: if ((sigLength > 1) || (sigLength == 1 && count == 0)) {
0230: // We come to here if one of the following two cases in true:
0231: //
0232: // [1] (sigLength > 1): A signature has been given.
0233: // [2] (sigLength == 1 && count == 0): A signature of no
0234: // parameters is implied.
0235: //
0236: // In both cases, we search for a method that matches exactly
0237: // with the signature.
0238:
0239: int sigNumArgs = sigLength - 1;
0240: Class[] paramTypes = new Class[sigNumArgs];
0241:
0242: for (int i = 0; i < sigNumArgs; i++) {
0243: String clsName = TclList
0244: .index(interp, signature, i + 1).toString();
0245: paramTypes[i] = JavaInvoke.getClassByName(interp,
0246: clsName);
0247: }
0248:
0249: if (isConstructor) {
0250: try {
0251: match = getAccessibleConstructor(cls, paramTypes);
0252: } catch (NoSuchMethodException e) {
0253: if (sigLength > 1) {
0254: throw new TclException(interp,
0255: "no accessible constructor \""
0256: + signature + "\"");
0257: } else {
0258: throw new TclException(interp,
0259: "can't find accessible constructor with "
0260: + count
0261: + " argument(s) for class \""
0262: + JavaInfoCmd
0263: .getNameFromClass(cls)
0264: + "\"");
0265: }
0266: }
0267: } else {
0268: match = lookupMethod(interp, cls, methodName,
0269: paramTypes, signature, isStatic);
0270: }
0271: } else {
0272: match = matchSignature(interp, cls, signature, methodName,
0273: isConstructor, argv, startIdx, count, isStatic);
0274: }
0275:
0276: FuncSig sig = new FuncSig(cls, PkgInvoker.getPkgInvoker(cls),
0277: match);
0278: //signature.setInternalRep(sig);
0279:
0280: return sig;
0281: }
0282:
0283: // lookupMethod attempts to find an exact match for the method name
0284: // based on the types (Java Class objects) of the arguments to the
0285: // method. If an exact match can not be found it will raise a TclException.
0286:
0287: static Method lookupMethod(Interp interp, // the tcl interpreter
0288: Class cls, // the Java objects class
0289: String methodName, // name of method
0290: Class[] paramTypes, // the Class object arguments
0291: TclObject signature, // used for error reporting
0292: boolean isStatic // True if signature is for a static Method.
0293: ) throws TclException {
0294: Method[] methods;
0295: boolean foundSameName = false;
0296:
0297: if (isStatic)
0298: methods = getAccessibleStaticMethods(cls);
0299: else
0300: methods = getAccessibleInstanceMethods(cls);
0301:
0302: // FIXME : searching Java methods for method name match
0303: // searching through ALL the methods is really slow
0304: // there really should be a better way to do this
0305: // as it needs to be done on every method invocation
0306:
0307: for (int i = 0; i < methods.length; i++) {
0308: if (!methodName.equals(methods[i].getName())) {
0309: continue;
0310: }
0311:
0312: foundSameName = true;
0313:
0314: Class[] pt = methods[i].getParameterTypes();
0315: if (pt.length != paramTypes.length) {
0316: continue;
0317: }
0318:
0319: boolean good = true;
0320: for (int j = 0; j < pt.length; j++) {
0321: if (pt[j] != paramTypes[j]) {
0322: good = false;
0323: break;
0324: }
0325: }
0326: if (good) {
0327: return methods[i];
0328: }
0329: }
0330:
0331: if (paramTypes.length > 0 || !foundSameName) {
0332: throw new TclException(interp, "no accessible"
0333: + (isStatic ? " static " : " ") + "method \""
0334: + signature + "\" in class "
0335: + JavaInfoCmd.getNameFromClass(cls));
0336: } else {
0337: throw new TclException(interp, "can't find accessible"
0338: + (isStatic ? " static " : " ") + "method \""
0339: + signature + "\" with " + paramTypes.length
0340: + " argument(s) for class \""
0341: + JavaInfoCmd.getNameFromClass(cls) + "\"");
0342: }
0343: }
0344:
0345: // This method will attempt to find a match for a signature
0346: // if an exact match can not be found then it will use
0347: // the types of the argument objects to "guess" what
0348: // method was intended by the user. If no match can be
0349: // found after "guessing" then a TclException will be raised.
0350:
0351: static Object matchSignature(Interp interp, // the tcl interpreter
0352: Class cls, // the Java objects class
0353: TclObject signature, // used for error reporting
0354: String methodName, // name of method, can be null
0355: boolean isConstructor, // duh
0356: TclObject[] argv, // arguments to Method or Constructor
0357: int startIdx, // Index of the first argument in argv
0358: int argv_count, // set to -1 if JFK was killed by the FBI
0359: boolean isStatic // True if signature is for a static Method.
0360: ) throws TclException {
0361: Object[] funcs;
0362: boolean foundSameName = false;
0363: ArrayList match_list = new ArrayList();
0364: int i, j;
0365:
0366: final boolean debug = false;
0367:
0368: if (isConstructor) {
0369: funcs = getAccessibleConstructors(cls);
0370: } else {
0371: if (isStatic)
0372: funcs = getAccessibleStaticMethods(cls);
0373: else
0374: funcs = getAccessibleInstanceMethods(cls);
0375: }
0376:
0377: for (i = 0; i < funcs.length; i++) {
0378: Class[] paramTypes;
0379: if (isConstructor) {
0380: paramTypes = ((Constructor) funcs[i])
0381: .getParameterTypes();
0382: } else {
0383: Method method = (Method) funcs[i];
0384: if (!methodName.equals(method.getName())) {
0385: continue;
0386: }
0387: foundSameName = true;
0388:
0389: paramTypes = method.getParameterTypes();
0390: }
0391:
0392: if (paramTypes.length == argv_count) {
0393: match_list.add(funcs[i]);
0394: }
0395: }
0396:
0397: // If there is only a single remaining match then we can return it now
0398:
0399: if (match_list.size() == 1) {
0400: // debug print the single match
0401: //System.out.println("single match : " + match_list.get(0));
0402:
0403: return match_list.get(0);
0404: } else if (match_list.size() > 1) {
0405:
0406: Class[] argv_classes = new Class[argv_count];
0407: Class[] match_classes;
0408:
0409: // get the object types for the method arguments in argv
0410: for (i = 0; i < argv_count; i++) {
0411: TclObject tobj = argv[startIdx + i];
0412:
0413: boolean isJavaObj = true;
0414: Class c = null;
0415: try {
0416: c = ReflectObject.getClass(interp, tobj);
0417: } catch (TclException e) {
0418: isJavaObj = false;
0419: }
0420:
0421: if (isJavaObj) {
0422: argv_classes[i] = c;
0423: } else {
0424: argv_classes[i] = String.class;
0425: }
0426: }
0427:
0428: if (debug) {
0429: // debug print argv types
0430:
0431: System.out.println("multiple matches for method "
0432: + methodName);
0433:
0434: System.out.print("argv is ");
0435:
0436: for (i = 0; i < argv_count; i++) {
0437: Class c = argv_classes[i];
0438: System.out.print(((c == null) ? "null"
0439: : JavaInfoCmd.getNameFromClass(c)));
0440: System.out.print(" ");
0441: }
0442: System.out.println();
0443:
0444: // debug print possible match types
0445:
0446: for (i = 0; i < match_list.size(); i++) {
0447:
0448: if (isConstructor) {
0449: match_classes = ((Constructor) match_list
0450: .get(i)).getParameterTypes();
0451: } else {
0452: match_classes = ((Method) match_list.get(i))
0453: .getParameterTypes();
0454: }
0455:
0456: System.out.print("match " + i + " is ");
0457:
0458: for (j = 0; j < match_classes.length; j++) {
0459: Class c = match_classes[j];
0460: System.out.print(JavaInfoCmd
0461: .getNameFromClass(c));
0462: System.out.print(" ");
0463: }
0464:
0465: System.out.println();
0466: }
0467: } // end if (debug)
0468:
0469: // try to match the argument types and the
0470: // match types exactly by comparing Class objects
0471:
0472: for (i = 0; i < match_list.size(); i++) {
0473:
0474: if (isConstructor) {
0475: match_classes = ((Constructor) match_list.get(i))
0476: .getParameterTypes();
0477: } else {
0478: match_classes = ((Method) match_list.get(i))
0479: .getParameterTypes();
0480: }
0481:
0482: boolean exact = true;
0483: for (j = 0; j < argv_count; j++) {
0484: if (match_classes[j] != argv_classes[j]) {
0485: exact = false;
0486: break;
0487: }
0488: }
0489:
0490: if (exact) {
0491: if (debug) {
0492: System.out.println("exact match at " + i);
0493: } // end if (debug)
0494: return match_list.get(i);
0495: }
0496: }
0497:
0498: // loop from the end of the list to the begining and
0499: // remove those signatures that are not assignable
0500: // take special care not to remove signatures that
0501: // have an object decended from java.lang.Object
0502: // in the same position as a null Class in argv_classes.
0503: // This means a null argument will not match a built in type.
0504:
0505: for (i = match_list.size() - 1; i >= 0; i--) {
0506:
0507: if (isConstructor) {
0508: match_classes = ((Constructor) match_list.get(i))
0509: .getParameterTypes();
0510: } else {
0511: match_classes = ((Method) match_list.get(i))
0512: .getParameterTypes();
0513: }
0514:
0515: // If any of the arguments are not assignable to the method arguments,
0516: // then remove this method from the list of matches.
0517:
0518: for (j = 0; j < argv_count; j++) {
0519: if (!JavaInvoke.isAssignable(match_classes[j],
0520: argv_classes[j])) {
0521:
0522: if (debug) {
0523: System.out
0524: .println("removing non assignable match "
0525: + i);
0526: } // end if (debug)
0527:
0528: match_list.remove(i);
0529: break; // go on to next Method
0530: }
0531: }
0532:
0533: }
0534:
0535: if (debug) {
0536: // debug print match_list after isAssignabelFrom test
0537:
0538: if (match_list.size() > 0) {
0539: System.out.println("isAssignableFrom() matches");
0540: }
0541:
0542: for (i = 0; i < match_list.size(); i++) {
0543:
0544: if (isConstructor) {
0545: match_classes = ((Constructor) match_list
0546: .get(i)).getParameterTypes();
0547: } else {
0548: match_classes = ((Method) match_list.get(i))
0549: .getParameterTypes();
0550: }
0551:
0552: System.out.print("match " + i + " is ");
0553:
0554: for (j = 0; j < argv_count; j++) {
0555: Class c = match_classes[j];
0556: System.out.print(JavaInfoCmd
0557: .getNameFromClass(c));
0558: System.out.print(" ");
0559: }
0560:
0561: System.out.println();
0562: }
0563: } // end if (debug)
0564:
0565: // If there is only a single remaining match then we can return it now
0566:
0567: if (match_list.size() == 1) {
0568: return match_list.get(0);
0569: }
0570:
0571: // at this point match_list should have only those signatures
0572: // that can take the argument types from argv with widining conversion
0573:
0574: // to figure out which method we should call we need to determine
0575: // which signatures are "better" then others where "better" is defined
0576: // as the shortest number of steps up the inheritance or interface tree
0577:
0578: if (match_list.size() > 1) {
0579:
0580: // the first thing we need to do is get the inheritance info
0581: // of the arguments to the method. From this we will create
0582: // an array of Class objects used to match against the possible
0583: // Method objects that we could invoke with this class name
0584:
0585: // as an example if we invoked a Method that took one
0586: // String argument then the argv_classes_lookup array would
0587: // end up as a 1 x 4 array with the java.lang.Class object
0588: // of the argument at [0,0] in the array. The inheritance
0589: // tree of the object would be represented by the [0,X]
0590: // members of the array (X depends on number of parents)
0591: // Class objects and Interface objects are stored and
0592: // null is used to keep track of the end of each Class
0593:
0594: // Example argument : {String}
0595: // Example array : {String,Serializable,null,Object}
0596: // Example info : String implements 1 interface = Serializable
0597: // Example info : String has 1 parent = Object
0598:
0599: Class[][] argv_classes_lookup = new Class[argv_count][];
0600:
0601: // we use a vector to store up all of the Class objects
0602: // that make up the inheritance tree for a particular class
0603:
0604: ArrayList class_list = new ArrayList();
0605:
0606: // for each argument to the method we loop up the inheritance tree
0607: // to find out if there is a superclass argv class that exactly matches
0608:
0609: for (i = 0; i < argv_count; i++) {
0610: Class c = argv_classes[i]; // Start with class type of argument
0611:
0612: if (c == null) {
0613: continue; // Skip lookup for argument when Class type is null
0614: }
0615:
0616: // loop over the first elements of the argv_classes_lookup to find out
0617: // if we have already looked up this class object and if we have just
0618: // use the array we found last time instead of doing the lookup again
0619:
0620: // Note that we would not need to do this if we cached the lookups
0621:
0622: for (j = 0; j < i; j++) {
0623: if (c == argv_classes_lookup[j][0]) {
0624: if (debug) {
0625: System.out
0626: .println("using argv_classes_lookup shortcut");
0627: } // end if (debug)
0628: argv_classes_lookup[i] = argv_classes_lookup[j];
0629: continue;
0630: }
0631: }
0632:
0633: // loop up the inheritance tree starting from c
0634:
0635: while (c != null) {
0636: // add a null to the front of the vector
0637: class_list.add(null);
0638:
0639: // add this Class and its Interfaces to the vector
0640: addInterfaces(c, class_list);
0641:
0642: c = c.getSuperclass();
0643: }
0644:
0645: // now remove the first element of the vector (it is null)
0646: class_list.remove(0);
0647:
0648: Class[] classes = new Class[class_list.size()];
0649: for (j = 0; j < classes.length; j++) {
0650: classes[j] = (Class) class_list.get(j);
0651: }
0652:
0653: argv_classes_lookup[i] = classes;
0654:
0655: class_list.clear();
0656: }
0657:
0658: if (debug) {
0659: // debug print the argv_classes_lookup array
0660:
0661: System.out.println("argv_classes_lookup array");
0662:
0663: for (i = 0; i < argv_count; i++) {
0664:
0665: Class[] classes = argv_classes_lookup[i];
0666:
0667: if (classes == null) {
0668: System.out.println("{ null }");
0669: continue;
0670: }
0671:
0672: System.out.print("{ ");
0673:
0674: for (j = 0; j < classes.length; j++) {
0675:
0676: if (classes[j] == null) {
0677: System.out.print("null");
0678: } else {
0679: System.out.print(JavaInfoCmd
0680: .getNameFromClass(classes[j]));
0681: }
0682:
0683: System.out.print(' ');
0684: }
0685:
0686: System.out.println("}");
0687:
0688: }
0689: } // end if (debug)
0690:
0691: int[] super _steps = new int[match_list.size()];
0692: int[] total_steps = new int[match_list.size()];
0693: boolean[] trim_matches = new boolean[match_list.size()];
0694: int min_super _step;
0695: int min_total_step;
0696: Class min_class;
0697:
0698: // iterate over the arguments then the Methods
0699: // as opposed to Methods then over the arguments
0700:
0701: for (j = 0; j < argv_count; j++) {
0702:
0703: // we need to keep track of the smallest # of jumps up the
0704: // inheritance tree as well as the total min for the one
0705: // special case where an implemented interface inherits
0706: // from another interface that also matches the signature
0707:
0708: min_super _step = Integer.MAX_VALUE;
0709: min_total_step = Integer.MAX_VALUE;
0710:
0711: // define min_class as base object before we loop
0712: min_class = Object.class;
0713:
0714: // iterate over the matched methods to find the
0715: // minimum steps for this argument
0716:
0717: for (i = 0; i < match_list.size(); i++) {
0718:
0719: if (isConstructor) {
0720: match_classes = ((Constructor) match_list
0721: .get(i)).getParameterTypes();
0722: } else {
0723: match_classes = ((Method) match_list.get(i))
0724: .getParameterTypes();
0725: }
0726:
0727: Class match_to = match_classes[j];
0728:
0729: // Class objects we will compare the match_to Class against
0730: // the index (j) gives us the Class array for argv[j]
0731: Class[] arg_classes = argv_classes_lookup[j];
0732:
0733: // If the argument type is null then skip to the next
0734: // argument and max the steps so they do not get removed
0735: if (arg_classes == null) {
0736: super _steps[i] = Integer.MAX_VALUE;
0737: total_steps[i] = Integer.MAX_VALUE;
0738: continue;
0739: }
0740:
0741: Class c;
0742: int super _step = 0;
0743: int total_step = 0;
0744:
0745: for (; total_step < arg_classes.length; total_step++) {
0746: c = arg_classes[total_step];
0747:
0748: if (c == null) {
0749: super _step++; // null means we have gone up to the superclass
0750: } else if (c == match_to) {
0751: super _steps[i] = super _step; // # of super classes up
0752: total_steps[i] = total_step; // total # of visible classes
0753:
0754: // when we define the min for an argument we must make
0755: // sure that three precidence rules are followed
0756: // 1: an interface can replace another interface as the min
0757: // 2: an interface can replace the class Object
0758: // 3: a class can replace an interface or a class
0759:
0760: // thus if we have already found a non Object min_class
0761: // it can not be replaced by an interface
0762:
0763: if (super _step <= min_super _step) {
0764:
0765: if (!c.isInterface()
0766: || min_class == Object.class
0767: || min_class.isInterface()) {
0768:
0769: if (debug) {
0770: //System.out.println("redefing min");
0771: //System.out.println("min_class was " + min_class);
0772: //System.out.println("min_class is now " + c);
0773: } // end if (debug)
0774:
0775: min_class = c;
0776:
0777: min_super _step = super _step;
0778:
0779: // check min_total_step only AFTER a min_super_step
0780: // or equal to min_super_step has been found
0781:
0782: if (total_step < min_total_step) {
0783: min_total_step = total_step;
0784: }
0785: }
0786: }
0787:
0788: break;
0789: }
0790: }
0791: }
0792:
0793: if (debug) {
0794: // debug print the super_step array and the total_step array
0795:
0796: System.out.println("step arrays for argument "
0797: + j);
0798:
0799: for (int loop = 0; loop < match_list.size(); loop++) {
0800: System.out.println("(" + super _steps[loop]
0801: + "," + total_steps[loop] + ")");
0802: }
0803:
0804: System.out.println("min_super_step = "
0805: + min_super _step);
0806: System.out.println("min_total_step = "
0807: + min_total_step);
0808: } // end if (debug)
0809:
0810: // from the step info we know the minumum so we can
0811: // remove those values that are "worse" then the min
0812:
0813: for (i = match_list.size() - 1; i >= 0; i--) {
0814:
0815: if (super _steps[i] > min_super _step
0816: || (super _steps[i] == min_super _step && total_steps[i] > min_total_step)) {
0817:
0818: if (debug) {
0819: System.out.println("will trim method "
0820: + i);
0821: } // end if (debug)
0822:
0823: trim_matches[i] = true; //trim this match # later
0824: }
0825:
0826: }
0827:
0828: // we should be able to short circut this so that we do
0829: // not waste loops when they are not needed
0830:
0831: // if all the methods have been trimmed then we do not
0832: // need to loop to the next argument
0833:
0834: }
0835:
0836: // remove the methods that were marked for deletion
0837:
0838: for (i = match_list.size() - 1; i >= 0; i--) {
0839: if (trim_matches[i]) {
0840: match_list.remove(i);
0841: }
0842: }
0843:
0844: if (debug) {
0845: // Debug print remaining matches
0846:
0847: System.out.println("after super steps trim");
0848:
0849: for (i = 0; i < match_list.size(); i++) {
0850:
0851: if (isConstructor) {
0852: match_classes = ((Constructor) match_list
0853: .get(i)).getParameterTypes();
0854: } else {
0855: match_classes = ((Method) match_list.get(i))
0856: .getParameterTypes();
0857: }
0858:
0859: System.out.print("match " + i + " is ");
0860:
0861: for (j = 0; j < argv_count; j++) {
0862: Class c = match_classes[j];
0863: System.out.print(JavaInfoCmd
0864: .getNameFromClass(c));
0865: System.out.print(" ");
0866: }
0867:
0868: System.out.println();
0869: }
0870: } // end if (debug)
0871:
0872: } // end if (match_list.size() > 1)
0873:
0874: // if there is only one item left in the match_list return it
0875:
0876: if (match_list.size() == 1) {
0877: return match_list.get(0);
0878: } else {
0879: // if we have 0 or >1 remaining matches then
0880: // we were unable to find the "best" match so raise an error
0881: // if possible, tell user what matches made the sig ambiguous.
0882:
0883: //System.out.println("match_list.size() is " + match_list.size());
0884:
0885: StringBuffer sb = new StringBuffer(100);
0886: sb.append("ambiguous ");
0887: if (isConstructor) {
0888: sb.append("constructor");
0889: } else {
0890: sb.append("method");
0891: }
0892: sb.append(" signature");
0893:
0894: if (match_list.size() == 0) {
0895:
0896: // FIXME : better error message for no signature matches case
0897: // We really should tell the user which methods we could have
0898: // matched but did not for one reason or another. The tricky
0899: // part is knowing what matches should be shown, perhaps all?
0900:
0901: //sb.append(" \"" + signature + "\"");
0902: sb.append(", could not choose between ");
0903:
0904: // Get all the signatures that match this name and number or args
0905:
0906: if (isConstructor) {
0907: funcs = getAccessibleConstructors(cls);
0908: } else {
0909: if (isStatic)
0910: funcs = getAccessibleStaticMethods(cls);
0911: else
0912: funcs = getAccessibleInstanceMethods(cls);
0913: }
0914:
0915: for (i = 0; i < funcs.length; i++) {
0916: Class[] paramTypes;
0917: if (isConstructor) {
0918: paramTypes = ((Constructor) funcs[i])
0919: .getParameterTypes();
0920: } else {
0921: Method method = (Method) funcs[i];
0922: if (!methodName.equals(method.getName())) {
0923: continue;
0924: }
0925: foundSameName = true;
0926:
0927: paramTypes = method.getParameterTypes();
0928: }
0929:
0930: if (paramTypes.length == argv_count) {
0931: match_list.add(funcs[i]);
0932: }
0933: }
0934:
0935: } else {
0936:
0937: // iterate over remaining possible matches and add to error message
0938:
0939: sb.append(", assignable signatures are ");
0940:
0941: }
0942:
0943: TclObject siglist = TclList.newInstance();
0944: siglist.preserve();
0945:
0946: for (i = 0; i < match_list.size(); i++) {
0947: TclObject cur_siglist = TclList.newInstance();
0948: cur_siglist.preserve();
0949:
0950: if (isConstructor) {
0951: Constructor con = ((Constructor) match_list
0952: .get(i));
0953: TclList.append(interp, cur_siglist, TclString
0954: .newInstance(con.getName()));
0955:
0956: //System.out.println("appending constructor name " + con.getName());
0957:
0958: match_classes = con.getParameterTypes();
0959: } else {
0960: Method meth = ((Method) match_list.get(i));
0961: TclList.append(interp, cur_siglist, TclString
0962: .newInstance(meth.getName()));
0963:
0964: //System.out.println("appending method name " + meth.getName());
0965:
0966: match_classes = meth.getParameterTypes();
0967: }
0968:
0969: for (j = 0; j < argv_count; j++) {
0970: Class c = match_classes[j];
0971: TclList.append(interp, cur_siglist, TclString
0972: .newInstance(JavaInfoCmd
0973: .getNameFromClass(c)));
0974: //System.out.println("appending class name " + c.getName());
0975: }
0976:
0977: TclList.append(interp, siglist, cur_siglist);
0978: cur_siglist.release();
0979: }
0980:
0981: sb.append(siglist.toString());
0982: siglist.release();
0983:
0984: throw new TclException(interp, sb.toString());
0985: }
0986:
0987: } // end else if (match_list.size() > 1)
0988:
0989: // if we got to here then we could not find a matching method so raise error
0990:
0991: if (isConstructor) {
0992: throw new TclException(interp,
0993: "can't find accessible constructor with "
0994: + argv_count + " argument(s) for class \""
0995: + JavaInfoCmd.getNameFromClass(cls) + "\"");
0996: } else {
0997: if (!foundSameName) {
0998: throw new TclException(interp, "no accessible"
0999: + (isStatic ? " static " : " ") + "method \""
1000: + signature + "\" in class "
1001: + JavaInfoCmd.getNameFromClass(cls));
1002: } else {
1003: throw new TclException(interp, "can't find accessible"
1004: + (isStatic ? " static " : " ") + "method \""
1005: + signature + "\" with " + argv_count
1006: + " argument(s) for class \""
1007: + JavaInfoCmd.getNameFromClass(cls) + "\"");
1008: }
1009: }
1010: }
1011:
1012: // Helper method to recursively add interfaces of a class to the vector
1013:
1014: private static void addInterfaces(Class cls, ArrayList alist) {
1015: alist.add(cls);
1016:
1017: Class[] interfaces = cls.getInterfaces();
1018:
1019: for (int i = 0; i < interfaces.length; i++) {
1020: addInterfaces(interfaces[i], alist);
1021: }
1022: }
1023:
1024: /*
1025: *----------------------------------------------------------------------
1026: *
1027: * getAccessibleConstructors --
1028: *
1029: * Returns all constructors that can be invoked for a given class.
1030: *
1031: * Results:
1032: * An array of all the accessible constructors in the class.
1033: *
1034: * Side effects:
1035: * None.
1036: *
1037: *----------------------------------------------------------------------
1038: */
1039:
1040: static Constructor[] getAccessibleConstructors(Class cls) // The class to query.
1041: {
1042: if (PkgInvoker.usesDefaultInvoker(cls)) {
1043: return cls.getConstructors();
1044: } else {
1045: Constructor[] constructors = cls.getDeclaredConstructors();
1046: ArrayList alist = null;
1047: boolean skipped_any = false;
1048:
1049: for (int i = 0; i < constructors.length; i++) {
1050: Constructor c = constructors[i];
1051: if (PkgInvoker.isAccessible(c)) {
1052: if (alist == null) {
1053: alist = new ArrayList(constructors.length);
1054: }
1055: alist.add(c);
1056: } else {
1057: skipped_any = true;
1058: }
1059: }
1060:
1061: if (skipped_any) {
1062: if (alist == null) {
1063: constructors = new Constructor[0];
1064: } else {
1065: constructors = new Constructor[alist.size()];
1066: for (int i = 0; i < constructors.length; i++) {
1067: constructors[i] = (Constructor) alist.get(i);
1068: }
1069: }
1070: }
1071: return constructors;
1072: }
1073: }
1074:
1075: /*
1076: *----------------------------------------------------------------------
1077: *
1078: * getAccessibleConstructor --
1079: *
1080: * Returns an accessable constructors for the given class
1081: * that accepts the given arguments.
1082: *
1083: * Results:
1084: * A constructor object, raises a NoSuchMethodException
1085: * if an accessible constructor cannot be found.
1086: *
1087: * Side effects:
1088: * None.
1089: *
1090: *----------------------------------------------------------------------
1091: */
1092:
1093: static Constructor getAccessibleConstructor(Class cls, // The class to query.
1094: Class[] parameterTypes) // The constructor arguments types
1095: throws NoSuchMethodException {
1096: if (PkgInvoker.usesDefaultInvoker(cls)) {
1097: return cls.getConstructor(parameterTypes);
1098: } else {
1099: Constructor constructor = cls
1100: .getDeclaredConstructor(parameterTypes);
1101: if (!PkgInvoker.isAccessible(constructor)) {
1102: throw new NoSuchMethodException();
1103: }
1104: return constructor;
1105: }
1106: }
1107:
1108: /*
1109: *----------------------------------------------------------------------
1110: *
1111: * getAccessibleInstanceMethods --
1112: *
1113: * Returns all instance methods that can be invoked for a given class.
1114: *
1115: * Results:
1116: * An array of all the accessible instance methods in the class and the
1117: * superclasses of the class. If a method is overloaded, only the
1118: * "most public" instance of that method is included in the
1119: * array. A method is considered accessible if it has public
1120: * access or if it does not have private access and the package has
1121: * a custom PkgInvoker. See comments above the "func" member
1122: * variable for more details.
1123: *
1124: * Side effects:
1125: * The array of methods are saved in a hashtable for faster access
1126: * in the future.
1127: *
1128: *----------------------------------------------------------------------
1129: */
1130:
1131: static Method[] getAccessibleInstanceMethods(Class cls) // The class to query.
1132: {
1133: Method[] methods = (Method[]) instanceMethodTable.get(cls);
1134: if (methods != null) {
1135: return methods;
1136: }
1137:
1138: // Avoid using Class.getMethods() because it includes
1139: // static members and it does not account for interfaces
1140: // which need to include methods from the Object class.
1141:
1142: ArrayList alist = new ArrayList();
1143:
1144: for (Class c = cls; c != null;) {
1145: methods = c.getDeclaredMethods();
1146: mergeInstanceMethods(c, methods, alist);
1147:
1148: Class interfaces[] = c.getInterfaces();
1149: for (int i = 0; i < interfaces.length; i++) {
1150: mergeInstanceMethods(interfaces[i], interfaces[i]
1151: .getMethods(), alist);
1152: }
1153:
1154: if (c.isInterface()) {
1155: c = Object.class; // if cls is an interface add Object methods
1156: } else {
1157: c = c.getSuperclass();
1158: }
1159: }
1160:
1161: sortMethods(alist);
1162: methods = new Method[alist.size()];
1163: for (int i = 0; i < methods.length; i++) {
1164: methods[i] = (Method) alist.get(i);
1165: }
1166: instanceMethodTable.put(cls, methods);
1167:
1168: return methods;
1169: }
1170:
1171: /*
1172: *----------------------------------------------------------------------
1173: *
1174: * getAccessibleStaticMethods --
1175: *
1176: * Returns all static methods that can be invoked for a given class.
1177: *
1178: * Results:
1179: * An array of all the accessible static methods in the class.
1180: * A method is considered accessible if it has public
1181: * access or if it is not private and the package has
1182: * a custom PkgInvoker. See comments above the "func" member
1183: * variable for more details.
1184: *
1185: * Side effects:
1186: * None.
1187: *
1188: *----------------------------------------------------------------------
1189: */
1190:
1191: static Method[] getAccessibleStaticMethods(Class cls) // The class to query.
1192: {
1193: Method[] methods = (Method[]) staticMethodTable.get(cls);
1194: if (methods != null) {
1195: return methods;
1196: }
1197:
1198: // When searching for static methods in this class,
1199: // call getDeclaredMethods() and filter out those
1200: // methods that are not static or are not accessible.
1201: // This should be quicker than calling getMethods()
1202: // since that returns both static and instance methods
1203: // for the class and its superclasses.
1204:
1205: methods = cls.getDeclaredMethods();
1206: ArrayList alist = new ArrayList();
1207:
1208: for (int i = 0; i < methods.length; i++) {
1209: Method m = methods[i];
1210: if (Modifier.isStatic(m.getModifiers())
1211: && PkgInvoker.isAccessible(m)) {
1212: alist.add(m);
1213: }
1214: }
1215:
1216: sortMethods(alist);
1217: methods = new Method[alist.size()];
1218: for (int i = 0; i < methods.length; i++) {
1219: methods[i] = (Method) alist.get(i);
1220: }
1221: staticMethodTable.put(cls, methods);
1222:
1223: return methods;
1224: }
1225:
1226: /*
1227: *----------------------------------------------------------------------
1228: *
1229: * mergeInstanceMethods --
1230: *
1231: * Add instance methods declared by a super-class or an interface to
1232: * the list of accessible instance methods.
1233: *
1234: * Results:
1235: * None.
1236: *
1237: * Side effects:
1238: * Elements of methods[] are added to vec. If an instance of
1239: * an overloaded method is already in vec, it will be replaced
1240: * by a new instance only if the new instance has a higher rank.
1241: *
1242: *----------------------------------------------------------------------
1243: */
1244:
1245: private static void mergeInstanceMethods(Class c, Method methods[],
1246: ArrayList alist) {
1247: for (int i = 0; i < methods.length; i++) {
1248: boolean sameSigExists = false;
1249: Method newMeth = methods[i];
1250:
1251: if (newMeth == null)
1252: continue;
1253:
1254: // Don't merge static methods or inaccessible methods
1255: if (Modifier.isStatic(newMeth.getModifiers())
1256: || !PkgInvoker.isAccessible(newMeth)) {
1257: continue;
1258: }
1259:
1260: for (int j = 0; j < alist.size(); j++) {
1261: Method oldMeth = (Method) alist.get(j);
1262:
1263: if (methodSigEqual(oldMeth, newMeth)) {
1264: sameSigExists = true;
1265:
1266: Class oldCls = oldMeth.getDeclaringClass();
1267: int newRank = getMethodRank(c, newMeth);
1268: int oldRank = getMethodRank(oldCls, oldMeth);
1269:
1270: if (newRank > oldRank) {
1271: alist.set(j, newMeth);
1272: }
1273: break;
1274: }
1275: }
1276:
1277: if (!sameSigExists) {
1278: // We copy a method into the list only if no method
1279: // with the same signature is already in the list.
1280: // Otherwise the matching routine in the get()
1281: // procedure may run into "ambiguous method signature"
1282: // errors when it sees instances of overloaded
1283: // methods.
1284:
1285: alist.add(newMeth);
1286: }
1287: }
1288: }
1289:
1290: /*
1291: *----------------------------------------------------------------------
1292: *
1293: * methodSigEqual --
1294: *
1295: * Returns whether the two methods have the same signature.
1296: *
1297: * Results:
1298: * True if the method names and arguments are the same. False
1299: * otherwise
1300: *
1301: * Side effects:
1302: * None.
1303: *
1304: *----------------------------------------------------------------------
1305: */
1306:
1307: private static boolean methodSigEqual(Method method1, Method method2) {
1308: if (!method1.getName().equals(method2.getName())) {
1309: return false;
1310: }
1311:
1312: Class param1[] = method1.getParameterTypes();
1313: Class param2[] = method2.getParameterTypes();
1314:
1315: if (param1.length != param2.length) {
1316: return false;
1317: }
1318:
1319: for (int i = 0; i < param1.length; i++) {
1320: if (param1[i] != param2[i]) {
1321: return false;
1322: }
1323: }
1324:
1325: return true;
1326: }
1327:
1328: /*
1329: *----------------------------------------------------------------------
1330: *
1331: * sortMethods --
1332: *
1333: * This method will sort a list of Method objects. We need to sort
1334: * the methods so that the order of the methods does not depend on
1335: * the order the methods as returned by the JVM.
1336: *
1337: * Results:
1338: * None.
1339: *
1340: * Side effects:
1341: * The order of the elements in the List is changed.
1342: *
1343: *----------------------------------------------------------------------
1344: */
1345:
1346: private static void sortMethods(ArrayList alist) {
1347: final boolean debug = false; // set to true for debug output
1348: int insize = alist.size();
1349:
1350: if (debug) {
1351: System.out.println("Pre sort dump");
1352: for (int i = 0; i < alist.size(); i++) {
1353: Method m = (Method) alist.get(i);
1354: System.out.println("Method " + i + " is \t\""
1355: + getMethodDescription(m) + "\"");
1356: }
1357: }
1358:
1359: for (int i = 1; i < alist.size(); i++) {
1360: int c = i;
1361: Method cm = (Method) alist.get(c);
1362: String cms = getMethodDescription(cm);
1363:
1364: // loop down array swapping elements into sorted order
1365:
1366: for (int j = c - 1; j >= 0; j--) {
1367: Method jm = (Method) alist.get(j);
1368: String jms = getMethodDescription(jm);
1369:
1370: if (debug) {
1371: System.out.println("checking \"" + cms
1372: + "\" from index " + c);
1373: System.out.println("against \"" + jms
1374: + "\" from index " + j);
1375: System.out.println("compareTo() is "
1376: + cms.compareTo(jms));
1377: }
1378:
1379: if (cms.compareTo(jms) <= 0) {
1380: if (debug) {
1381: System.out.println("swapping " + c + " and "
1382: + j);
1383: }
1384:
1385: // Swap the Methods at c and j
1386: alist.set(c, jm);
1387: alist.set(j, cm);
1388:
1389: // Current becomes the value of j for next loop
1390: c = j;
1391: //cm = jm;
1392: //cms = jms;
1393:
1394: } else {
1395: if (debug) {
1396: System.out.println("no swap at index " + j);
1397: }
1398: break;
1399: }
1400: }
1401: }
1402:
1403: if (debug) {
1404: System.out.println("Post sort dump");
1405: for (int i = 0; i < alist.size(); i++) {
1406: Method m = (Method) alist.get(i);
1407: System.out.println("Method " + i + " is \t\""
1408: + getMethodDescription(m) + "\"");
1409: }
1410: }
1411:
1412: if (insize != alist.size()) {
1413: throw new RuntimeException("lost elements");
1414: }
1415: return;
1416: }
1417:
1418: /*
1419: *----------------------------------------------------------------------
1420: *
1421: * getMethodDescription --
1422: *
1423: * This helper method will return a string description of a method
1424: * and the arguments and return type of the methos. This helper
1425: * is only called by sortMethods().
1426: *
1427: * Results:
1428: * None.
1429: *
1430: * Side effects:
1431: * None.
1432: *
1433: *----------------------------------------------------------------------
1434: */
1435: private static String getMethodDescription(Method m) {
1436: StringBuffer sb = new StringBuffer(50);
1437:
1438: sb.append(m.getName());
1439:
1440: Class[] params = m.getParameterTypes();
1441:
1442: sb.append('(');
1443:
1444: for (int i = 0; i < params.length; i++) {
1445:
1446: sb.append(JavaInfoCmd.getNameFromClass(params[i]));
1447:
1448: if (i < (params.length - 1)) {
1449: sb.append(", ");
1450: }
1451: }
1452:
1453: sb.append(") returns ");
1454:
1455: Class ret = m.getReturnType();
1456:
1457: sb.append(JavaInfoCmd.getNameFromClass(ret));
1458:
1459: return sb.toString();
1460: }
1461:
1462: /*
1463: *----------------------------------------------------------------------
1464: *
1465: * getMethodRank --
1466: *
1467: * Returns the rank of "public-ness" of the method. See comments
1468: * above the "func" member variable for more details on public-ness
1469: * ranking.
1470: *
1471: * Results:
1472: * The rank of "public-ness" of the method.
1473: *
1474: * Side effects:
1475: * None.
1476: *
1477: *----------------------------------------------------------------------
1478: */
1479:
1480: private static int getMethodRank(Class declaringCls, // The class that declares the method.
1481: Method method) // Return the rank of this method.
1482: {
1483: int methMod = method.getModifiers();
1484:
1485: if (Modifier.isPrivate(methMod)) {
1486: return 0;
1487: }
1488:
1489: int clsMod = declaringCls.getModifiers();
1490:
1491: if (Modifier.isPublic(methMod) && Modifier.isPublic(clsMod)) {
1492: return 2;
1493: }
1494:
1495: return 0;
1496: }
1497:
1498: } // end FuncSig.
|