/**
  * Find an accessible constructor with compatible parameters. Compatible parameters mean that
  * every method parameter is assignable from the given parameters. In other words, it finds
  * constructor that will take the parameters given.
  *
  * <p>First it checks if there is constructor matching the exact signature. If no such, all the
  * constructors of the class are tested if their signatures are assignment compatible with the
  * parameter types. The first matching constructor is returned.
  *
  * @param cls find constructor for this class
  * @param parameterTypes find method with compatible parameters
  * @return a valid Constructor object. If there's no matching constructor, returns <code>null
  *     </code>.
  */
 public static Constructor getMatchingAccessibleConstructor(Class cls, Class[] parameterTypes) {
   // see if we can find the constructor directly
   // most of the time this works and it's much faster
   try {
     Constructor ctor = cls.getConstructor(parameterTypes);
     MemberUtils.setAccessibleWorkaround(ctor);
     return ctor;
   } catch (NoSuchMethodException e) {
     /* SWALLOW */
   }
   Constructor result = null;
   // search through all constructors
   Constructor[] ctors = cls.getConstructors();
   for (int i = 0; i < ctors.length; i++) {
     // compare parameters
     if (ClassUtils.isAssignable(parameterTypes, ctors[i].getParameterTypes(), true)) {
       // get accessible version of method
       Constructor ctor = getAccessibleConstructor(ctors[i]);
       if (ctor != null) {
         MemberUtils.setAccessibleWorkaround(ctor);
         if (result == null
             || MemberUtils.compareParameterTypes(
                     ctor.getParameterTypes(), result.getParameterTypes(), parameterTypes)
                 < 0) {
           result = ctor;
         }
       }
     }
   }
   return result;
 }
Beispiel #2
0
  /**
   * Gets an accessible <code>Field</code> by name breaking scope if requested.
   * Superclasses/interfaces will be considered.
   *
   * @param cls the class to reflect, must not be null
   * @param fieldName the field name to obtain
   * @param forceAccess whether to break scope restrictions using the <code>setAccessible</code>
   *     method. <code>False</code> will only match public fields.
   * @return the Field object
   * @throws IllegalArgumentException if the class or field name is null
   */
  public static Field getField(final Class cls, String fieldName, boolean forceAccess) {
    if (cls == null) {
      throw new IllegalArgumentException("The class must not be null");
    }
    if (fieldName == null) {
      throw new IllegalArgumentException("The field name must not be null");
    }
    // Sun Java 1.3 has a bugged implementation of getField hence we write the
    // code ourselves

    // getField() will return the Field object with the declaring class
    // set correctly to the class that declares the field. Thus requesting the
    // field on a subclass will return the field from the superclass.
    //
    // priority order for lookup:
    // searchclass private/protected/package/public
    // superclass protected/package/public
    //  private/different package blocks access to further superclasses
    // implementedinterface public

    // check up the superclass hierarchy
    for (Class acls = cls; acls != null; acls = acls.getSuperclass()) {
      try {
        Field field = acls.getDeclaredField(fieldName);
        // getDeclaredField checks for non-public scopes as well
        // and it returns accurate results
        if (!Modifier.isPublic(field.getModifiers())) {
          if (forceAccess) {
            field.setAccessible(true);
          } else {
            continue;
          }
        }
        return field;
      } catch (NoSuchFieldException ex) {
        // ignore
      }
    }
    // check the public interface case. This must be manually searched for
    // incase there is a public supersuperclass field hidden by a private/package
    // superclass field.
    Field match = null;
    for (Iterator intf = ClassUtils.getAllInterfaces(cls).iterator(); intf.hasNext(); ) {
      try {
        Field test = ((Class) intf.next()).getField(fieldName);
        if (match != null) {
          throw new IllegalArgumentException(
              "Reference to field "
                  + fieldName
                  + " is ambiguous relative to "
                  + cls
                  + "; a matching field exists on two or more implemented interfaces.");
        }
        match = test;
      } catch (NoSuchFieldException ex) {
        // ignore
      }
    }
    return match;
  }