FuncSig.java :  » Scripting » jacl » tcl » lang » Java Open Source

Java Open Source » Scripting » jacl 
jacl » tcl » lang » FuncSig.java
/*
 * FuncSig.java --
 *
 *  This class implements the internal representation of a Java
 *  method or constructor signature.
 *
 * Copyright (c) 1997 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and
 * redistribution of this file, and for a DISCLAIMER OF ALL
 * WARRANTIES.
 *
 * RCS: @(#) $Id: FuncSig.java,v 1.15 2006/05/15 22:14:23 mdejong Exp $
 *
 */

package tcl.lang;

import tcl.lang.reflect.*;
import java.lang.reflect.*;
import java.util.*;

/**
 * This class implements the internal representation of a Java method
 * or constructor signature. Because methods and constructors are very
 * similar to each other, the operations on method signatures and
 * constructor signatures are limped in this class of "function
 * signature."
 */

class FuncSig implements InternalRep {

// The class that a method signature is used against. In the case of a
// static method call by java::call, targetCls is given by the <class>
// argument to java::call. In the case of an instance method call,
// targetCls is the class of the instance. targetCls is used to test
// the validity of a cached FuncSig internal rep for method
// signatures.
//
// targetCls is not used for class signatures.

Class targetCls;

// The PkgInvoker used to access the constructor or method.

PkgInvoker pkgInvoker;

// The constructor or method given by the field signature. You need to
// apply the instanceof operator to determine whether it's a
// Constructor or a Method.
//
// func may be a public, protected, package protected or private
// member of the given class. Attempts to access func is subject to
// the Java language access control rules. Public members can always
// be accessed. Protected and package protected members can be
// accessed only if a proper TclPkgInvoker class exists. Private
// members can never be accessed.
//
// If the signature is a method signature and the specified method has
// been overloaded, then func will point to the "most public" instance
// of that method. The order of public-ness is ranked as the following
// (a larger number means more public):
//
// RANK         METHOD ACCESS TYPE      CLASS ACCESS TYPE  
//   0      private      any
//   1      package protected  protected
//   1      protected    protected
//   1      public      protected
//   1      package protected  public
//   1      protected    public
//   2      public      public

Object func;

// Stores all accessible instance methods for a Java class
// Note that we use a Hashtable instead of a HashMap here
// since these fields could be accessed from multiple
// threads and the Hashtable class is synchronized.

static Hashtable instanceMethodTable = new Hashtable();
static Hashtable staticMethodTable = new Hashtable();


/*
 *----------------------------------------------------------------------
 *
 * FuncSig --
 *
 *  Creates a new FuncSig instance.
 *
 * Side effects:
 *  Member fields are initialized.
 *
 *----------------------------------------------------------------------
 */

FuncSig(
    Class cls,      // Initial value for targetCls.
    PkgInvoker p,    // Initial value for pkgInvoker.
    Object f)      // Initial value for func.
{
    targetCls = cls;
    pkgInvoker = p;
    func = f;
}

/*
 *----------------------------------------------------------------------
 *
 * duplicate --
 *
 *  Make a copy of an object's internal representation.
 *
 * Results:
 *  Returns a newly allocated instance of the appropriate type.
 *
 * Side effects:
 *  None.
 *
 *----------------------------------------------------------------------
 */

public InternalRep duplicate()
{
    return new FuncSig(targetCls, pkgInvoker, func);
}

/**
  * Implement this no-op for the InternalRep interface.
  */

public void dispose() {}


/*
 *----------------------------------------------------------------------
 *
 * get --
 *
 *  Returns the FuncSig internal representation of the constructor
 *  or method that matches with the signature and the parameters.
 *
 * Results:
 *  The FuncSig given by the signature. An exception will be
 *  raised if the constructor provided is for an abstract
 *  class, or if the class is not accessible.
 *
 * Side effects:
 *  None.
 *
 *----------------------------------------------------------------------
 */

static FuncSig
get(
    Interp interp,    // Current interpreter.  
    Class cls,      // If null, we are looking for a constructor
        // in signature. If non-null, we are looking
        // for a method of this class in signature.
    TclObject signature,  // Method/constructor signature.
    TclObject[] argv,    // Arguments.
    int startIdx,    // Index of the first argument in argv
    int count,      // Number of arguments to pass to the
        // constructor.
    boolean isStatic)    // True if signature is for a static Method.
throws
    TclException
{
    boolean isConstructor = (cls == null);

    /* FIXME: commented out Method caching system
     * we comment out this code because it causes
     * the AmbiguousSignature-2.1 test to fail under Tcl Blend.
     * a new caching system might help but it is unclear if
     * determining a cache "match" is any less work then just searching.


    InternalRep rep = signature.getInternalRep();

    // If a valid FuncSig internal rep is already cached, return it
    // right away.

    if (rep instanceof FuncSig) {
  FuncSig tmp = (FuncSig)rep;
  Object func = tmp.func;

  if (isConstructor) {
      if ((func instanceof Constructor)
        && (((Constructor)func).getParameterTypes().length
          == count)) {
    return tmp;
      }
  } else {
      if ((func instanceof Method) && (tmp.targetCls == cls)
        && (((Method)func).getParameterTypes().length == count)) {
    return tmp;
      }
  }
    }
    */


    // Look up the constructor or method using the string rep of the
    // signature object.

    Object match;
    int sigLength = TclList.getLength(interp, signature);
    String methodName = null;
    TclObject class_or_method;

    if (sigLength == 0) {
  throw new TclException(interp, "bad signature \"" + signature + "\"");
    } else if (sigLength == 1) {
  class_or_method = signature;
    } else {
  class_or_method = TclList.index(interp, signature, 0);
    }

    if (isConstructor) {
  cls = JavaInvoke.getClassByName(interp, class_or_method.toString() );
    } else {
  methodName = class_or_method.toString();
    }

    if ((isConstructor || isStatic) && !PkgInvoker.isAccessible(cls)) {
        JavaInvoke.notAccessibleError(interp, cls);
    }

    if (isConstructor && Modifier.isAbstract(cls.getModifiers())) {
        throw new TclException(interp, "Class \"" +
      JavaInfoCmd.getNameFromClass(cls) +
      "\" is abstract");
    }

    if ((sigLength > 1) || (sigLength == 1 && count == 0)) {
  // We come to here if one of the following two cases in true:
  //
  //     [1] (sigLength > 1): A signature has been given.
  //     [2] (sigLength == 1 && count == 0): A signature of no
  //         parameters is implied.
  //
  // In both cases, we search for a method that matches exactly
  // with the signature.

  int sigNumArgs = sigLength - 1;
  Class[] paramTypes = new Class[sigNumArgs];

  for (int i = 0; i < sigNumArgs; i++) {
      String clsName = TclList.index(interp, signature, i+1).toString();
      paramTypes[i] = JavaInvoke.getClassByName(interp, clsName);
  }

  if (isConstructor) {
      try {
    match = getAccessibleConstructor(cls, paramTypes);
      } catch (NoSuchMethodException e) {
        if (sigLength > 1) {
    throw new TclException(interp, "no accessible constructor \"" +
          signature + "\"");
        } else {
    throw new TclException(interp, "can't find accessible constructor with " +
          count + " argument(s) for class \"" +
            JavaInfoCmd.getNameFromClass(cls) +
            "\"");
        }
      }
  } else {
    match = lookupMethod(interp, cls, methodName, paramTypes,
            signature, isStatic);
  }
    } else {
      match = matchSignature(interp, cls, signature, methodName,
           isConstructor, argv, startIdx, count, isStatic);
    }

    FuncSig sig = new FuncSig(cls, PkgInvoker.getPkgInvoker(cls), match);
    //signature.setInternalRep(sig);

    return sig;
}







// lookupMethod attempts to find an exact match for the method name
// based on the types (Java Class objects) of the arguments to the
// method. If an exact match can not be found it will raise a TclException.

static Method
lookupMethod(
    Interp interp,           // the tcl interpreter
    Class cls,               // the Java objects class
    String methodName,       // name of method
    Class[] paramTypes,      // the Class object arguments
    TclObject  signature,    // used for error reporting
    boolean isStatic         // True if signature is for a static Method.
)
    throws TclException
{
  Method[] methods;
  boolean foundSameName = false;

  if (isStatic)
      methods = getAccessibleStaticMethods(cls);
  else
      methods = getAccessibleInstanceMethods(cls);


  // FIXME : searching Java methods for method name match
  // searching through ALL the methods is really slow
  // there really should be a better way to do this
  // as it needs to be done on every method invocation

  for (int i = 0; i < methods.length; i++) {
    if (! methodName.equals(methods[i].getName())) {
      continue;
    }
    
    foundSameName = true;
    
    Class[] pt = methods[i].getParameterTypes();
    if (pt.length != paramTypes.length) {
      continue;
    }
    
    boolean good = true;
    for (int j = 0; j < pt.length; j ++) {
      if (pt[j] != paramTypes[j]) {
  good = false;
  break;
      }
    }
    if (good) {
      return methods[i];
    }
  }


  if (paramTypes.length > 0 || !foundSameName) {
    throw new TclException(interp,
      "no accessible" + (isStatic?" static ":" ") + "method \"" +
      signature + "\" in class " +
      JavaInfoCmd.getNameFromClass(cls));
  } else {
    throw new TclException(interp,
      "can't find accessible" + (isStatic?" static ":" ") + "method \"" +
      signature + "\" with " + paramTypes.length +
      " argument(s) for class \"" +
      JavaInfoCmd.getNameFromClass(cls) +
      "\"");
  }
}









// This method will attempt to find a match for a signature
// if an exact match can not be found then it will use
// the types of the argument objects to "guess" what
// method was intended by the user. If no match can be
// found after "guessing" then a TclException will be raised.


static Object
matchSignature(
    Interp interp,           // the tcl interpreter
    Class cls,               // the Java objects class
    TclObject signature,     // used for error reporting
    String methodName,       // name of method, can be null
    boolean isConstructor,   // duh
    TclObject[] argv,        // arguments to Method or Constructor
    int startIdx,       // Index of the first argument in argv    
    int argv_count,          // set to -1 if JFK was killed by the FBI
    boolean isStatic         // True if signature is for a static Method.
)
    throws TclException
{
  Object[] funcs;
  boolean foundSameName = false;
  ArrayList match_list = new ArrayList();
  int i,j;

  final boolean debug = false;  

  if (isConstructor) {
    funcs = getAccessibleConstructors(cls);
  } else {
    if (isStatic)
        funcs = getAccessibleStaticMethods(cls);
    else
        funcs = getAccessibleInstanceMethods(cls);
  }

  for (i = 0; i < funcs.length; i++) {
    Class[] paramTypes;
    if (isConstructor) {
      paramTypes = ((Constructor)funcs[i]).getParameterTypes();
    } else {
      Method method = (Method)funcs[i];
      if (! methodName.equals(method.getName())) {
  continue;
      }
      foundSameName = true;
      
      paramTypes = method.getParameterTypes();
    }
     
    if (paramTypes.length == argv_count) {
      match_list.add(funcs[i]);
    }
  }  


  // If there is only a single remaining match then we can return it now

  if (match_list.size() == 1) {
     // debug print the single match
     //System.out.println("single match : " + match_list.get(0));

    return match_list.get(0);
  } else if (match_list.size() > 1) {
    
    Class[] argv_classes = new Class[argv_count];
    Class[] match_classes;
    
    
    // get the object types for the method arguments in argv
    for (i=0; i < argv_count; i++) {
      TclObject tobj = argv[startIdx + i];

      boolean isJavaObj = true;
      Class c = null;
      try {
          c = ReflectObject.getClass(interp, tobj);
      } catch (TclException e) {
          isJavaObj = false;
      }

      if (isJavaObj) {
  argv_classes[i] = c;
      } else {
  argv_classes[i] = String.class;
      }      
    }




    if (debug) {
    // debug print argv types
    
    System.out.println("multiple matches for method " + methodName);
    
    System.out.print("argv    is ");

    for (i=0; i < argv_count; i++) {
      Class c = argv_classes[i];
      System.out.print( ((c == null) ? "null" :
          JavaInfoCmd.getNameFromClass(c)) );
      System.out.print(" ");
    }
    System.out.println();


    // debug print possible match types

    for (i=0; i < match_list.size(); i++) {

      if (isConstructor) {
  match_classes = ((Constructor) match_list.get(i)).getParameterTypes();
      } else {
  match_classes = ((Method) match_list.get(i)).getParameterTypes();
      }

      System.out.print("match " + i + " is ");
      
      for (j=0; j < match_classes.length; j++) {
          Class c = match_classes[j];
          System.out.print( JavaInfoCmd.getNameFromClass(c) );
          System.out.print(" ");
      }

      System.out.println();
    }
    } // end if (debug)



    // try to match the argument types and the
    // match types exactly by comparing Class objects
    
    for (i=0; i < match_list.size(); i++) {

      if (isConstructor) {
  match_classes = ((Constructor) match_list.get(i)).getParameterTypes();
      } else {
  match_classes = ((Method) match_list.get(i)).getParameterTypes();
      }

      boolean exact = true;
      for (j=0; j < argv_count; j++) {
  if (match_classes[j] != argv_classes[j]) {
    exact = false;
    break;
  }
      }

      if (exact) {
        if (debug) {
  System.out.println("exact match at " + i);
        } // end if (debug)
  return match_list.get(i);
      }
    }




    // loop from the end of the list to the begining and
    // remove those signatures that are not assignable
    // take special care not to remove signatures that
    // have an object decended from java.lang.Object
    // in the same position as a null Class in argv_classes.
    // This means a null argument will not match a built in type.
    

    for (i = match_list.size()-1; i >= 0 ; i--) {
      
      if (isConstructor) {
  match_classes = ((Constructor) match_list.get(i)).getParameterTypes();
      } else {
  match_classes = ((Method) match_list.get(i)).getParameterTypes();
      }

      // If any of the arguments are not assignable to the method arguments,
      // then remove this method from the list of matches.

      for (j=0; j < argv_count; j++) {
  if (!JavaInvoke.isAssignable(match_classes[j], argv_classes[j])) {
    
          if (debug) {
          System.out.println("removing non assignable match " + i);
          } // end if (debug)

    match_list.remove(i);
    break; // go on to next Method
  }
      }

    }



    if (debug) {
    // debug print match_list after isAssignabelFrom test

    if (match_list.size() > 0) {
      System.out.println("isAssignableFrom() matches");
    }

    for (i=0; i < match_list.size(); i++) {

      if (isConstructor) {
  match_classes = ((Constructor) match_list.get(i)).getParameterTypes();
      } else {
  match_classes = ((Method) match_list.get(i)).getParameterTypes();
      }

      System.out.print("match " + i + " is ");
      
      for (j=0; j < argv_count; j++) {
  Class c = match_classes[j];
  System.out.print( JavaInfoCmd.getNameFromClass(c) );
  System.out.print(" ");
      }

      System.out.println();
    }
    } // end if (debug)


    // If there is only a single remaining match then we can return it now

    if (match_list.size() == 1) {
      return match_list.get(0);
    }

  


    // at this point match_list should have only those signatures
    // that can take the argument types from argv with widining conversion
    
    // to figure out which method we should call we need to determine
    // which signatures are "better" then others where "better" is defined
    // as the shortest number of steps up the inheritance or interface tree
    
    if (match_list.size() > 1) {
    
      // the first thing we need to do is get the inheritance info
      // of the arguments to the method. From this we will create
      // an array of Class objects used to match against the possible
      // Method objects that we could invoke with this class name

      // as an example if we invoked a Method that took one
      // String argument then the argv_classes_lookup array would
      // end up as a 1 x 4 array with the java.lang.Class object
      // of the argument at [0,0] in the array. The inheritance
      // tree of the object would be represented by the [0,X]
      // members of the array (X depends on number of parents)
      // Class objects and Interface objects are stored and
      // null is used to keep track of the end of each Class

      // Example argument : {String}
      // Example array : {String,Serializable,null,Object}
      // Example info : String implements 1 interface = Serializable
      // Example info : String has 1 parent = Object

      Class[][] argv_classes_lookup = new Class[argv_count][];
      

      // we use a vector to store up all of the Class objects
      // that make up the inheritance tree for a particular class

      ArrayList class_list = new ArrayList();


      // for each argument to the method we loop up the inheritance tree
      // to find out if there is a superclass argv class that exactly matches

      for (i=0; i < argv_count; i++) {
  Class c = argv_classes[i]; // Start with class type of argument

        if (c == null) {
            continue; // Skip lookup for argument when Class type is null
        }

  // loop over the first elements of the argv_classes_lookup to find out
  // if we have already looked up this class object and if we have just
        // use the array we found last time instead of doing the lookup again

        // Note that we would not need to do this if we cached the lookups

        for (j=0; j < i ; j++) {
            if (c == argv_classes_lookup[j][0]) {
                if (debug) {
                System.out.println("using argv_classes_lookup shortcut");
                } // end if (debug)
                argv_classes_lookup[i] = argv_classes_lookup[j];
                continue;
            }
        }


  // loop up the inheritance tree starting from c

  while (c != null) {
    // add a null to the front of the vector
    class_list.add(null);

    // add this Class and its Interfaces to the vector
    addInterfaces(c, class_list);

    c = c.getSuperclass();
  }


  // now remove the first element of the vector (it is null)
  class_list.remove(0);

  Class[] classes = new Class[class_list.size()];
  for (j=0; j < classes.length; j++) {
            classes[j] = (Class) class_list.get(j);
        }

  argv_classes_lookup[i] = classes;

  class_list.clear();
      }
      
      
      
      

      if (debug) {
      // debug print the argv_classes_lookup array

      System.out.println("argv_classes_lookup array");
      

      for (i=0; i < argv_count; i++) {

  Class[] classes = argv_classes_lookup[i];

  if (classes == null) {
      System.out.println("{ null }");
            continue;
        }

  System.out.print("{ ");

  for (j=0; j < classes.length; j++) {

    if (classes[j] == null) {
      System.out.print("null");
    } else {
      System.out.print( JavaInfoCmd.getNameFromClass(classes[j]) );
    }

    System.out.print(' ');
  }

  System.out.println("}");

      }
      } // end if (debug)



      int[] super_steps = new int[match_list.size()];
      int[] total_steps = new int[match_list.size()];
      boolean[] trim_matches = new boolean[match_list.size()];
      int min_super_step;
      int min_total_step;
      Class min_class;
      

      // iterate over the arguments then the Methods
      // as opposed to Methods then over the arguments

      for (j=0; j < argv_count; j++) {

  // we need to keep track of the smallest # of jumps up the
  // inheritance tree as well as the total min for the one
  // special case where an implemented interface inherits
  // from another interface that also matches the signature

  min_super_step = Integer.MAX_VALUE;
  min_total_step = Integer.MAX_VALUE;

  
  // define min_class as base object before we loop
  min_class = Object.class;

  
  // iterate over the matched methods to find the
  // minimum steps for this argument
  
  for (i=0; i < match_list.size(); i++) {

    if (isConstructor) {
      match_classes = ((Constructor) match_list.get(i)).getParameterTypes();
    } else {
      match_classes = ((Method) match_list.get(i)).getParameterTypes();
    }

    Class match_to = match_classes[j];
    
    // Class objects we will compare the match_to Class against
    // the index (j) gives us the Class array for argv[j]
    Class[] arg_classes = argv_classes_lookup[j];


          // If the argument type is null then skip to the next
          // argument and max the steps so they do not get removed
          if (arg_classes == null) {
        super_steps[i] = Integer.MAX_VALUE;
        total_steps[i] = Integer.MAX_VALUE;
              continue;
          }


    Class c;
    int super_step=0;
    int total_step=0;
    
    for ( ; total_step < arg_classes.length ; total_step++) {
      c = arg_classes[total_step];
      
      if (c == null) {
        super_step++; // null means we have gone up to the superclass
      } else if (c == match_to) {                
        super_steps[i] = super_step; // # of super classes up
        total_steps[i] = total_step; // total # of visible classes
        

        // when we define the min for an argument we must make
        // sure that three precidence rules are followed
        // 1: an interface can replace another interface as the min
        // 2: an interface can replace the class Object
        // 3: a class can replace an interface or a class

        // thus if we have already found a non Object min_class
        // it can not be replaced by an interface
        
        if (super_step <= min_super_step) {

    if (!c.isInterface() ||
        min_class == Object.class ||
        min_class.isInterface()) {

                  if (debug) {
      //System.out.println("redefing min");
      //System.out.println("min_class was " + min_class);
      //System.out.println("min_class is now " + c);
                  } // end if (debug)

      min_class = c;

      min_super_step = super_step;

      // check min_total_step only AFTER a min_super_step
      // or equal to min_super_step has been found
    
      if (total_step < min_total_step) {
        min_total_step = total_step;
      }
    }
        }

        break;
      }
    }
  }



        if (debug) {
  // debug print the super_step array and the total_step array
  
  System.out.println("step arrays for argument " + j);

  for (int loop=0; loop < match_list.size(); loop++) {
    System.out.println("(" + super_steps[loop] + "," + total_steps[loop] + ")");
  }

  System.out.println("min_super_step = " + min_super_step);
  System.out.println("min_total_step = " + min_total_step);
        } // end if (debug)



  // from the step info we know the minumum so we can
  // remove those values that are "worse" then the min

  for (i=match_list.size()-1; i >= 0; i--) {

    if (super_steps[i] > min_super_step ||
        (super_steps[i] == min_super_step &&
         total_steps[i] > min_total_step)) {

            if (debug) {
      System.out.println("will trim method " + i);
            } // end if (debug)

      trim_matches[i] = true; //trim this match # later
    }

  }





  // we should be able to short circut this so that we do
  // not waste loops when they are not needed


  // if all the methods have been trimmed then we do not
  // need to loop to the next argument

      }


      // remove the methods that were marked for deletion
      
      for (i=match_list.size()-1; i >= 0; i--) {
  if (trim_matches[i]) {
    match_list.remove(i);
  }
      }





      if (debug) {
      // Debug print remaining matches
      
      System.out.println("after super steps trim");
      
      for (i=0; i < match_list.size(); i++) {
  
  if (isConstructor) {
    match_classes = ((Constructor) match_list.get(i)).getParameterTypes();
  } else {
    match_classes = ((Method) match_list.get(i)).getParameterTypes();
  }
  
  System.out.print("match " + i + " is ");
  
  for (j=0; j < argv_count; j++) {
    Class c = match_classes[j];
    System.out.print( JavaInfoCmd.getNameFromClass(c) );
    System.out.print(" ");
  }
  
  System.out.println();
      }
      } // end if (debug)
     
      
    } // end if (match_list.size() > 1)
 
    
    
    // if there is only one item left in the match_list return it
    
    if (match_list.size() == 1) {
      return match_list.get(0);
    } else {
      // if we have 0 or >1 remaining matches then
      // we were unable to find the "best" match so raise an error
      // if possible, tell user what matches made the sig ambiguous.

      //System.out.println("match_list.size() is " + match_list.size());


      StringBuffer sb = new StringBuffer(100);
      sb.append("ambiguous ");
      if (isConstructor) {
          sb.append("constructor");
      } else {
          sb.append("method");
      }
      sb.append(" signature");


      if (match_list.size() == 0) {

    // FIXME : better error message for no signature matches case
    // We really should tell the user which methods we could have
    // matched but did not for one reason or another. The tricky
    // part is knowing what matches should be shown, perhaps all?

    //sb.append(" \"" + signature + "\"");
    sb.append(", could not choose between ");


          // Get all the signatures that match this name and number or args

          if (isConstructor) {
            funcs = getAccessibleConstructors(cls);
          } else {
            if (isStatic)
              funcs = getAccessibleStaticMethods(cls);
            else
              funcs = getAccessibleInstanceMethods(cls);
          }

          for (i = 0; i < funcs.length; i++) {
            Class[] paramTypes;
            if (isConstructor) {
              paramTypes = ((Constructor)funcs[i]).getParameterTypes();
            } else {
              Method method = (Method)funcs[i];
              if (! methodName.equals(method.getName())) {
          continue;
              }
              foundSameName = true;
      
              paramTypes = method.getParameterTypes();
            }
     
            if (paramTypes.length == argv_count) {
              match_list.add(funcs[i]);
            }
          }  


      } else {

        // iterate over remaining possible matches and add to error message

        sb.append(", assignable signatures are ");

      }



      TclObject siglist = TclList.newInstance();
      siglist.preserve();

      for (i=0; i < match_list.size(); i++) {
          TclObject cur_siglist = TclList.newInstance();
          cur_siglist.preserve();

          if (isConstructor) {
            Constructor con = ((Constructor) match_list.get(i));
            TclList.append(interp,cur_siglist,
                TclString.newInstance(con.getName()) );

            //System.out.println("appending constructor name " + con.getName());

      match_classes = con.getParameterTypes();
    } else {
            Method meth = ((Method) match_list.get(i));
            TclList.append(interp,cur_siglist,
                TclString.newInstance(meth.getName()) );

            //System.out.println("appending method name " + meth.getName());

      match_classes = meth.getParameterTypes();
    }
  
    for (j=0; j < argv_count; j++) {
      Class c = match_classes[j];
            TclList.append(interp,cur_siglist,
                TclString.newInstance(
              JavaInfoCmd.getNameFromClass(c)) );
            //System.out.println("appending class name " + c.getName());
    }

          TclList.append(interp,siglist,cur_siglist);
          cur_siglist.release();
      }

      sb.append(siglist.toString());
      siglist.release();

      throw new TclException(interp, sb.toString());
    }
    
  } // end else if (match_list.size() > 1)
  



  // if we got to here then we could not find a matching method so raise error

  if (isConstructor) {
    throw new TclException(interp, "can't find accessible constructor with " +
             argv_count + " argument(s) for class \"" +
             JavaInfoCmd.getNameFromClass(cls) +
             "\"");
  } else {
    if (!foundSameName) {
      throw new TclException(interp,
           "no accessible" + (isStatic?" static ":" ") +
           "method \"" + signature + "\" in class " +
           JavaInfoCmd.getNameFromClass(cls));
    } else {
      throw new TclException(interp,
           "can't find accessible" + (isStatic?" static ":" ") +
           "method \"" + signature + "\" with " +
           argv_count + " argument(s) for class \"" +
           JavaInfoCmd.getNameFromClass(cls) +
           "\"");
    }
  }
}






// Helper method to recursively add interfaces of a class to the vector

private static void addInterfaces(Class cls, ArrayList alist)
{
  alist.add(cls);

  Class[] interfaces = cls.getInterfaces();

  for (int i=0; i < interfaces.length ; i++) {
    addInterfaces(interfaces[i], alist);
  }
}





/*
 *----------------------------------------------------------------------
 *
 * getAccessibleConstructors --
 *
 *  Returns all constructors that can be invoked for a given class.
 *
 * Results:
 *  An array of all the accessible constructors in the class.
 *
 * Side effects:
 *  None.
 *
 *----------------------------------------------------------------------
 */

static Constructor[]
getAccessibleConstructors(
    Class cls)        // The class to query.
{
    if (PkgInvoker.usesDefaultInvoker(cls)) {
  return cls.getConstructors();
    } else {
  Constructor[] constructors = cls.getDeclaredConstructors();
  ArrayList alist = null;
  boolean skipped_any = false;

  for (int i=0; i < constructors.length; i++) {
      Constructor c = constructors[i];
      if (PkgInvoker.isAccessible(c)) {
          if (alist == null) {
              alist = new ArrayList(constructors.length);
          }
          alist.add(c);
      } else {
          skipped_any = true;
      }
  }

  if (skipped_any) {
            if (alist == null) {
                constructors = new Constructor[0];
            } else {
          constructors = new Constructor[alist.size()];
          for (int i=0; i < constructors.length; i++) {
                    constructors[i] = (Constructor) alist.get(i);
                }
            }
  }
  return constructors;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * getAccessibleConstructor --
 *
 *  Returns an accessable constructors for the given class
 *  that accepts the given arguments.
 *
 * Results:
 *  A constructor object, raises a NoSuchMethodException
 *  if an accessible constructor cannot be found.
 *
 * Side effects:
 *  None.
 *
 *----------------------------------------------------------------------
 */

static Constructor
getAccessibleConstructor(
    Class cls,        // The class to query.
    Class[] parameterTypes)    // The constructor arguments types
    throws NoSuchMethodException
{
    if (PkgInvoker.usesDefaultInvoker(cls)) {
  return cls.getConstructor(parameterTypes);
    } else {
  Constructor constructor =
      cls.getDeclaredConstructor(parameterTypes);
  if (!PkgInvoker.isAccessible(constructor)) {
      throw new NoSuchMethodException();
  }
  return constructor;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * getAccessibleInstanceMethods --
 *
 *  Returns all instance methods that can be invoked for a given class.
 *
 * Results:
 *  An array of all the accessible instance methods in the class and the
 *  superclasses of the class. If a method is overloaded, only the
 *  "most public" instance of that method is included in the
 *  array. A method is considered accessible if it has public
 *  access or if it does not have private access and the package has
 *  a custom PkgInvoker. See comments above the "func" member
 *  variable for more details.
 *
 * Side effects:
 *  The array of methods are saved in a hashtable for faster access
 *  in the future.
 *
 *----------------------------------------------------------------------
 */

static Method[]
getAccessibleInstanceMethods(
    Class cls)        // The class to query.
{
    Method[] methods = (Method[]) instanceMethodTable.get(cls);
    if (methods != null) {
  return methods;
    }

    // Avoid using Class.getMethods() because it includes
    // static members and it does not account for interfaces
    // which need to include methods from the Object class.

    ArrayList alist = new ArrayList();

    for (Class c = cls; c != null; ) {
  methods = c.getDeclaredMethods();
  mergeInstanceMethods(c, methods, alist);

  Class interfaces[] = c.getInterfaces();
  for (int i = 0; i < interfaces.length; i++) {
      mergeInstanceMethods(interfaces[i], interfaces[i].getMethods(), alist);
  }

  if (c.isInterface()) {
      c = Object.class; // if cls is an interface add Object methods
  } else {
      c = c.getSuperclass();
  }
    }

    sortMethods(alist);
    methods = new Method[alist.size()];
    for (int i=0; i < methods.length; i++) {
        methods[i] = (Method) alist.get(i);
    }
    instanceMethodTable.put(cls, methods);

    return methods;
}

/*
 *----------------------------------------------------------------------
 *
 * getAccessibleStaticMethods --
 *
 *  Returns all static methods that can be invoked for a given class.
 *
 * Results:
 *  An array of all the accessible static methods in the class.
 *  A method is considered accessible if it has public
 *  access or if it is not private and the package has
 *  a custom PkgInvoker. See comments above the "func" member
 *  variable for more details.
 *
 * Side effects:
 *  None.
 *
 *----------------------------------------------------------------------
 */

static Method[]
getAccessibleStaticMethods(
    Class cls)        // The class to query.
{
    Method[] methods = (Method[]) staticMethodTable.get(cls);
    if (methods != null) {
  return methods;
    }

    // When searching for static methods in this class,
    // call getDeclaredMethods() and filter out those
    // methods that are not static or are not accessible.
    // This should be quicker than calling getMethods()
    // since that returns both static and instance methods
    // for the class and its superclasses.

    methods = cls.getDeclaredMethods();
    ArrayList alist = new ArrayList();

    for (int i=0; i < methods.length; i++) {
  Method m = methods[i];
  if (Modifier.isStatic(m.getModifiers()) && PkgInvoker.isAccessible(m)) {
      alist.add(m);
  }
    }

    sortMethods(alist);
    methods = new Method[alist.size()];
    for (int i=0; i < methods.length; i++) {
        methods[i] = (Method) alist.get(i);
    }
    staticMethodTable.put(cls, methods);

    return methods;
}

/*
 *----------------------------------------------------------------------
 *
 * mergeInstanceMethods --
 *
 *  Add instance methods declared by a super-class or an interface to
 *  the list of accessible instance methods.
 *
 * Results:
 *  None.
 *
 * Side effects:
 *  Elements of methods[] are added to vec. If an instance of
 *  an overloaded method is already in vec, it will be replaced
 *  by a new instance only if the new instance has a higher rank.
 *
 *----------------------------------------------------------------------
 */

private static void 
mergeInstanceMethods(
    Class c,
    Method methods[],
    ArrayList alist)
{
    for (int i=0; i < methods.length; i++) {
  boolean sameSigExists = false;
  Method newMeth = methods[i];

  if (newMeth == null)
      continue;

  // Don't merge static methods or inaccessible methods
  if (Modifier.isStatic(newMeth.getModifiers()) ||
          !PkgInvoker.isAccessible(newMeth)) {
      continue;
  }

  for (int j=0; j < alist.size(); j++) {
      Method oldMeth = (Method) alist.get(j);

      if (methodSigEqual(oldMeth, newMeth)) {
    sameSigExists = true;

    Class oldCls = oldMeth.getDeclaringClass();
    int newRank = getMethodRank(c, newMeth);
    int oldRank = getMethodRank(oldCls, oldMeth);

    if (newRank > oldRank) {
        alist.set(j, newMeth);
    }
    break;
      }
  }
      
  if (!sameSigExists) {
      // We copy a method into the list only if no method
      // with the same signature is already in the list.
      // Otherwise the matching routine in the get()
      // procedure may run into "ambiguous method signature"
      // errors when it sees instances of overloaded
      // methods.

      alist.add(newMeth);
  }
    }
}

/*
 *----------------------------------------------------------------------
 *
 * methodSigEqual --
 *
 *  Returns whether the two methods have the same signature.
 *
 * Results:
 *  True if the method names and arguments are the same. False
 *  otherwise
 *
 * Side effects:
 *  None.
 *
 *----------------------------------------------------------------------
 */

private static boolean
methodSigEqual(
    Method method1,
    Method method2)
{
    if (!method1.getName().equals(method2.getName())) {
  return false;
    }

    Class param1[] = method1.getParameterTypes();
    Class param2[] = method2.getParameterTypes();

    if (param1.length != param2.length) {
  return false;
    }

    for (int i=0; i<param1.length; i++) {
  if (param1[i] != param2[i]) {
      return false;
  }
    }

    return true;
}

/*
 *----------------------------------------------------------------------
 *
 * sortMethods --
 *
 *  This method will sort a list of Method objects. We need to sort
 *      the methods so that the order of the methods does not depend on
 *      the order the methods as returned by the JVM.
 *
 * Results:
 *  None.
 *
 * Side effects:
 *      The order of the elements in the List is changed.
 *
 *----------------------------------------------------------------------
 */

private static void 
sortMethods(
    ArrayList alist)
{
    final boolean debug = false; // set to true for debug output
    int insize = alist.size();

    if (debug) {
  System.out.println("Pre sort dump");
  for (int i=0; i < alist.size(); i++) {
      Method m = (Method) alist.get(i);
      System.out.println("Method " + i + " is \t\"" + getMethodDescription(m) + "\"");
  }
    }

    for (int i=1; i < alist.size(); i++) {
  int c = i;
  Method cm = (Method) alist.get(c);
  String cms = getMethodDescription(cm);

  // loop down array swapping elements into sorted order

  for (int j = c - 1; j >= 0 ; j--) {
      Method jm = (Method) alist.get(j);
      String jms = getMethodDescription(jm);

      if (debug) {
          System.out.println("checking \"" + cms + "\" from index " + c);
          System.out.println("against  \"" + jms + "\" from index " + j);
          System.out.println("compareTo() is " + cms.compareTo(jms));
      }

      if (cms.compareTo(jms) <= 0) {
    if (debug) {
        System.out.println("swapping " + c + " and " + j);
    }
    
    // Swap the Methods at c and j
    alist.set(c, jm);
        alist.set(j, cm);

    // Current becomes the value of j for next loop
    c = j;
    //cm = jm;
    //cms = jms;

      } else {
                if (debug) {
        System.out.println("no swap at index " + j);
                }
    break;
            }
  }
    }

    if (debug) {
  System.out.println("Post sort dump");
  for (int i=0; i < alist.size(); i++) {
      Method m = (Method) alist.get(i);
      System.out.println("Method " + i + " is \t\"" +  getMethodDescription(m)+ "\"");
  }
    }

    if (insize != alist.size()) {
  throw new RuntimeException("lost elements");
    }
    return;
}

/*
 *----------------------------------------------------------------------
 *
 * getMethodDescription --
 *
 *  This helper method will return a string description of a method
 *      and the arguments and return type of the methos. This helper
 *      is only called by sortMethods().
 *
 * Results:
 *  None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
private static String getMethodDescription(Method m) {
    StringBuffer sb = new StringBuffer(50);

    sb.append(m.getName());

    Class[] params = m.getParameterTypes();

    sb.append('(');
    
    for (int i=0; i < params.length; i++) {

  sb.append(JavaInfoCmd.getNameFromClass(params[i]));

  if (i < (params.length - 1)) {
      sb.append(", ");
  }
    }

    sb.append(") returns ");

    Class ret = m.getReturnType();

    sb.append(JavaInfoCmd.getNameFromClass(ret));

    return sb.toString();
}


/*
 *----------------------------------------------------------------------
 *
 * getMethodRank --
 *
 *  Returns the rank of "public-ness" of the method. See comments
 *  above the "func" member variable for more details on public-ness
 *  ranking.
 *
 * Results:
 *  The rank of "public-ness" of the method.
 *
 * Side effects:
 *  None.
 *
 *----------------------------------------------------------------------
 */

private static int
getMethodRank(
    Class declaringCls,    // The class that declares the method.
    Method method)    // Return the rank of this method.
{
    int methMod = method.getModifiers();

    if (Modifier.isPrivate(methMod)) {
  return 0;
    }

    int clsMod = declaringCls.getModifiers();

    if (Modifier.isPublic(methMod) && Modifier.isPublic(clsMod)) {
  return 2;
    }

    return 0;
}

} // end FuncSig.

java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.