You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2018/01/01 14:49:03 UTC
juneau git commit: Fuzzy arg matching.
Repository: juneau
Updated Branches:
refs/heads/master 6e794947b -> 6ac075fc2
Fuzzy arg matching.
Project: http://git-wip-us.apache.org/repos/asf/juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/juneau/commit/6ac075fc
Tree: http://git-wip-us.apache.org/repos/asf/juneau/tree/6ac075fc
Diff: http://git-wip-us.apache.org/repos/asf/juneau/diff/6ac075fc
Branch: refs/heads/master
Commit: 6ac075fc2e14e2fa73058a5a12c6c91472e69ccb
Parents: 6e79494
Author: JamesBognar <ja...@apache.org>
Authored: Mon Jan 1 09:48:59 2018 -0500
Committer: JamesBognar <ja...@apache.org>
Committed: Mon Jan 1 09:48:59 2018 -0500
----------------------------------------------------------------------
.../org/apache/juneau/utils/ClassUtilsTest.java | 62 ++++++++++
.../java/org/apache/juneau/BeanContext.java | 20 +--
.../java/org/apache/juneau/BeanSession.java | 20 +--
.../main/java/org/apache/juneau/Context.java | 40 +++---
.../java/org/apache/juneau/PropertyStore.java | 50 ++++----
.../org/apache/juneau/internal/ClassUtils.java | 124 +++++++++++++++----
6 files changed, 241 insertions(+), 75 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/juneau/blob/6ac075fc/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
index dd268e2..e567bc5 100755
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
@@ -318,4 +318,66 @@ public class ClassUtilsTest {
int a1;
int b1;
}
+
+ //====================================================================================================
+ // Fuzzy constructor args
+ //====================================================================================================
+ @Test
+ public void newInstanceWithFuzzyArgs() throws Exception {
+ FA t = null;
+
+ t = ClassUtils.newInstance(FA.class, FA.class, true);
+ assertEquals(1, t.c);
+
+ t = ClassUtils.newInstance(FA.class, FA.class, true, "foo");
+ assertEquals(2, t.c);
+
+ t = ClassUtils.newInstance(FA.class, FA.class, true, 123, "foo");
+ assertEquals(3, t.c);
+
+ t = ClassUtils.newInstance(FA.class, FA.class, true, "foo", 123);
+ assertEquals(3, t.c);
+
+ FB t2 = null;
+
+ try {
+ t2 = ClassUtils.newInstance(FB.class, FB.class, true);
+ fail();
+ } catch (Exception e) {
+ assertEquals("Could not instantiate class org.apache.juneau.utils.ClassUtilsTest$FB", e.getMessage());
+ }
+
+ t2 = ClassUtils.newInstance(FB.class, FB.class, true, "foo");
+ assertEquals(1, t2.c);
+
+ t2 = ClassUtils.newInstance(FB.class, FB.class, true, 123, "foo");
+ assertEquals(1, t2.c);
+
+ t2 = ClassUtils.newInstance(FB.class, FB.class, true, "foo", 123);
+ assertEquals(1, t2.c);
+ }
+
+ public static class FA {
+ int c;
+
+ public FA() {
+ c = 1;
+ }
+
+ public FA(String foo) {
+ c = 2;
+ }
+
+ public FA(int foo, String bar) {
+ c = 3;
+ }
+ }
+
+ public static class FB {
+ int c;
+
+ public FB(String foo) {
+ c = 1;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/juneau/blob/6ac075fc/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
index 4b76241..a0779f4 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
@@ -1741,8 +1741,10 @@ public class BeanContext extends Context {
* @param c2
* The class to instantiate.
* Can also be an instance of the class.
- * @param allowNoArgs
- * If constructor with specified args cannot be found, use the no-args constructor.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* The arguments to pass to the constructor.
* @return
@@ -1750,8 +1752,8 @@ public class BeanContext extends Context {
* @throws
* RuntimeException if constructor could not be found or called.
*/
- public <T> T newInstance(Class<T> c, Object c2, boolean allowNoArgs, Object...args) {
- return ClassUtils.newInstance(c, c2, allowNoArgs, args);
+ public <T> T newInstance(Class<T> c, Object c2, boolean fuzzyArgs, Object...args) {
+ return ClassUtils.newInstance(c, c2, fuzzyArgs, args);
}
/**
@@ -1765,8 +1767,10 @@ public class BeanContext extends Context {
* @param c2
* The class to instantiate.
* Can also be an instance of the class.
- * @param allowNoArgs
- * If constructor with specified args cannot be found, use the no-args constructor.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* The arguments to pass to the constructor.
* @return
@@ -1774,8 +1778,8 @@ public class BeanContext extends Context {
* @throws
* RuntimeException if constructor could not be found or called.
*/
- public <T> T newInstanceFromOuter(Object outer, Class<T> c, Object c2, boolean allowNoArgs, Object...args) {
- return ClassUtils.newInstanceFromOuter(outer, c, c2, allowNoArgs, args);
+ public <T> T newInstanceFromOuter(Object outer, Class<T> c, Object c2, boolean fuzzyArgs, Object...args) {
+ return ClassUtils.newInstanceFromOuter(outer, c, c2, fuzzyArgs, args);
}
/**
http://git-wip-us.apache.org/repos/asf/juneau/blob/6ac075fc/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
index 34a9022..cd59eaa 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
@@ -1007,8 +1007,10 @@ public class BeanSession extends Session {
* @param c2
* The class to instantiate.
* Can also be an instance of the class.
- * @param allowNoArgs
- * If constructor with specified args cannot be found, use the no-args constructor.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* The arguments to pass to the constructor.
* @return
@@ -1016,8 +1018,8 @@ public class BeanSession extends Session {
* @throws
* RuntimeException if constructor could not be found or called.
*/
- public <T> T newInstance(Class<T> c, Object c2, boolean allowNoArgs, Object...args) {
- return ctx.newInstance(c, c2, allowNoArgs, args);
+ public <T> T newInstance(Class<T> c, Object c2, boolean fuzzyArgs, Object...args) {
+ return ctx.newInstance(c, c2, fuzzyArgs, args);
}
/**
@@ -1031,8 +1033,10 @@ public class BeanSession extends Session {
* @param c2
* The class to instantiate.
* Can also be an instance of the class.
- * @param allowNoArgs
- * If constructor with specified args cannot be found, use the no-args constructor.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* The arguments to pass to the constructor.
* @return
@@ -1040,8 +1044,8 @@ public class BeanSession extends Session {
* @throws
* RuntimeException if constructor could not be found or called.
*/
- public <T> T newInstanceFromOuter(Object outer, Class<T> c, Object c2, boolean allowNoArgs, Object...args) {
- return ctx.newInstanceFromOuter(outer, c, c2, allowNoArgs, args);
+ public <T> T newInstanceFromOuter(Object outer, Class<T> c, Object c2, boolean fuzzyArgs, Object...args) {
+ return ctx.newInstanceFromOuter(outer, c, c2, fuzzyArgs, args);
}
/**
http://git-wip-us.apache.org/repos/asf/juneau/blob/6ac075fc/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
index 427f9a7..5d761e4 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
@@ -263,15 +263,17 @@ public abstract class Context {
* @param def
* The default value if the property doesn't exist.
* <br>Can either be an instance of <code>T</code>, or a <code>Class<? <jk>extends</jk> T></code>.
- * @param allowNoArgs
- * Look for no-arg constructors when instantiating a class.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* Arguments to pass to the constructor.
* Constructors matching the arguments are always used before no-arg constructors.
* @return A new property instance.
*/
- public <T> T getInstanceProperty(String key, Class<T> type, Object def, boolean allowNoArgs, Object...args) {
- return propertyStore.getInstanceProperty(key, type, def, allowNoArgs, args);
+ public <T> T getInstanceProperty(String key, Class<T> type, Object def, boolean fuzzyArgs, Object...args) {
+ return propertyStore.getInstanceProperty(key, type, def, fuzzyArgs, args);
}
/**
@@ -283,15 +285,17 @@ public abstract class Context {
* @param def
* The default value if the property doesn't exist.
* <br>Can either be an instance of <code>T</code>, or a <code>Class<? <jk>extends</jk> T></code>.
- * @param allowNoArgs
- * Look for no-arg constructors when instantiating a class.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* Arguments to pass to the constructor.
* Constructors matching the arguments are always used before no-arg constructors.
* @return A new property instance.
*/
- public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, boolean allowNoArgs, Object...args) {
- return propertyStore.getInstanceProperty(key, outer, type, def, allowNoArgs, args);
+ public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, boolean fuzzyArgs, Object...args) {
+ return propertyStore.getInstanceProperty(key, outer, type, def, fuzzyArgs, args);
}
/**
@@ -312,15 +316,17 @@ public abstract class Context {
* @param key The property name.
* @param type The class type of the property.
* @param def The default object to return if the property doesn't exist.
- * @param allowNoArgs
- * Look for no-arg constructors when instantiating a class.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* Arguments to pass to the constructor.
* Constructors matching the arguments are always used before no-arg constructors.
* @return A new property instance.
*/
- public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, boolean allowNoArgs, Object...args) {
- return propertyStore.getInstanceArrayProperty(key, type, def, allowNoArgs, args);
+ public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, boolean fuzzyArgs, Object...args) {
+ return propertyStore.getInstanceArrayProperty(key, type, def, fuzzyArgs, args);
}
/**
@@ -330,15 +336,17 @@ public abstract class Context {
* @param outer The outer object if the class we're instantiating is an inner class.
* @param type The class type of the property.
* @param def The default object to return if the property doesn't exist.
- * @param allowNoArgs
- * Look for no-arg constructors when instantiating a class.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* Arguments to pass to the constructor.
* Constructors matching the arguments are always used before no-arg constructors.
* @return A new property instance.
*/
- public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, boolean allowNoArgs, Object...args) {
- return propertyStore.getInstanceArrayProperty(key, outer, type, def, allowNoArgs, args);
+ public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, boolean fuzzyArgs, Object...args) {
+ return propertyStore.getInstanceArrayProperty(key, outer, type, def, fuzzyArgs, args);
}
/**
http://git-wip-us.apache.org/repos/asf/juneau/blob/6ac075fc/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStore.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStore.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStore.java
index cce39b6..39700e0 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStore.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStore.java
@@ -408,15 +408,17 @@ public final class PropertyStore {
* @param def
* The default value if the property doesn't exist.
* <br>Can either be an instance of <code>T</code>, or a <code>Class<? <jk>extends</jk> T></code>.
- * @param allowNoArgs
- * Look for no-arg constructors when instantiating a class.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* Arguments to pass to the constructor.
* Constructors matching the arguments are always used before no-arg constructors.
* @return A new property instance.
*/
- public <T> T getInstanceProperty(String key, Class<T> type, Object def, boolean allowNoArgs, Object...args) {
- return getInstanceProperty(key, null, type, def, allowNoArgs, args);
+ public <T> T getInstanceProperty(String key, Class<T> type, Object def, boolean fuzzyArgs, Object...args) {
+ return getInstanceProperty(key, null, type, def, fuzzyArgs, args);
}
/**
@@ -428,21 +430,23 @@ public final class PropertyStore {
* @param def
* The default value if the property doesn't exist.
* <br>Can either be an instance of <code>T</code>, or a <code>Class<? <jk>extends</jk> T></code>.
- * @param allowNoArgs
- * Look for no-arg constructors when instantiating a class.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* Arguments to pass to the constructor.
* Constructors matching the arguments are always used before no-arg constructors.
* @return A new property instance.
*/
- public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, boolean allowNoArgs, Object...args) {
+ public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, boolean fuzzyArgs, Object...args) {
Property p = findProperty(key);
if (p != null)
- return p.asInstance(outer, type, allowNoArgs, args);
+ return p.asInstance(outer, type, fuzzyArgs, args);
if (def == null)
return null;
if (def instanceof Class)
- return ClassUtils.newInstance(type, def, allowNoArgs, args);
+ return ClassUtils.newInstance(type, def, fuzzyArgs, args);
if (type.isInstance(def))
return (T)def;
throw new ConfigException("Could not instantiate property ''{0}'' as type ''{1}'' with default value ''{2}''", key, type, def);
@@ -466,15 +470,17 @@ public final class PropertyStore {
* @param key The property name.
* @param type The class type of the property.
* @param def The default object to return if the property doesn't exist.
- * @param allowNoArgs
- * Look for no-arg constructors when instantiating a class.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* Arguments to pass to the constructor.
* Constructors matching the arguments are always used before no-arg constructors.
* @return A new property instance.
*/
- public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, boolean allowNoArgs, Object...args) {
- return getInstanceArrayProperty(key, null, type, def, allowNoArgs, args);
+ public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, boolean fuzzyArgs, Object...args) {
+ return getInstanceArrayProperty(key, null, type, def, fuzzyArgs, args);
}
/**
@@ -484,16 +490,18 @@ public final class PropertyStore {
* @param outer The outer object if the class we're instantiating is an inner class.
* @param type The class type of the property.
* @param def The default object to return if the property doesn't exist.
- * @param allowNoArgs
- * Look for no-arg constructors when instantiating a class.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* Arguments to pass to the constructor.
* Constructors matching the arguments are always used before no-arg constructors.
* @return A new property instance.
*/
- public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, boolean allowNoArgs, Object...args) {
+ public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, boolean fuzzyArgs, Object...args) {
Property p = findProperty(key);
- return p == null ? def : p.asInstanceArray(outer, type, allowNoArgs, args);
+ return p == null ? def : p.asInstanceArray(outer, type, fuzzyArgs, args);
}
/**
@@ -751,15 +759,15 @@ public final class PropertyStore {
}
}
- public <T> T asInstance(Object outer, Class<T> iType, boolean allowNoArgs, Object...args) {
+ public <T> T asInstance(Object outer, Class<T> iType, boolean fuzzyArgs, Object...args) {
if (type == STRING)
return ClassUtils.fromString(iType, value.toString());
else if (type == OBJECT || type == CLASS)
- return ClassUtils.newInstanceFromOuter(outer, iType, value, allowNoArgs, args);
+ return ClassUtils.newInstanceFromOuter(outer, iType, value, fuzzyArgs, args);
throw new ConfigException("Invalid property instantiation ''{0}'' to ''{1}'' on property ''{2}''", type, iType, name);
}
- public <T> T[] asInstanceArray(Object outer, Class<T> eType, boolean allowNoArgs, Object...args) {
+ public <T> T[] asInstanceArray(Object outer, Class<T> eType, boolean fuzzyArgs, Object...args) {
if (value instanceof Collection) {
Collection<?> l = (Collection<?>)value;
Object t = Array.newInstance(eType, l.size());
@@ -771,7 +779,7 @@ public final class PropertyStore {
else if (type == SET_STRING || type == LIST_STRING)
o2 = ClassUtils.fromString(eType, o.toString());
else if (type == SET_CLASS || type == LIST_CLASS || type == LIST_OBJECT)
- o2 = ClassUtils.newInstanceFromOuter(outer, eType, o, allowNoArgs, args);
+ o2 = ClassUtils.newInstanceFromOuter(outer, eType, o, fuzzyArgs, args);
if (o2 == null)
throw new ConfigException("Invalid property conversion ''{0}'' to ''{1}[]'' on property ''{2}''", type, eType, name);
Array.set(t, i++, o2);
http://git-wip-us.apache.org/repos/asf/juneau/blob/6ac075fc/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 81758bb..594d9d1 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -576,23 +576,42 @@ public final class ClassUtils {
* Finds a public constructor with the specified parameters without throwing an exception.
*
* @param c The class to search for a constructor.
+ * @param fuzzyArgs
+ * Use fuzzy-arg matching.
+ * Find a constructor that best matches the specified args.
* @param argTypes
* The argument types in the constructor.
* Can be subtypes of the actual constructor argument types.
* @return The matching constructor, or <jk>null</jk> if constructor could not be found.
*/
@SuppressWarnings("unchecked")
- public static <T> Constructor<T> findPublicConstructor(Class<T> c, Class<?>...argTypes) {
+ public static <T> Constructor<T> findPublicConstructor(Class<T> c, boolean fuzzyArgs, Class<?>...argTypes) {
ConstructorCacheEntry cce = CONSTRUCTOR_CACHE.get(c);
if (cce != null && argsMatch(cce.paramTypes, argTypes))
return (Constructor<T>)cce.constructor;
+
+ if (fuzzyArgs) {
+ int bestCount = -1;
+ Constructor<?> bestMatch = null;
+ for (Constructor<?> n : c.getConstructors()) {
+ int m = fuzzyArgsMatch(n.getParameterTypes(), argTypes);
+ if (m > bestCount) {
+ bestCount = m;
+ bestMatch = n;
+ }
+ }
+ if (bestCount >= 0)
+ CONSTRUCTOR_CACHE.put(c, new ConstructorCacheEntry(c, bestMatch));
+ return (Constructor<T>)bestMatch;
+ }
for (Constructor<?> n : c.getConstructors()) {
- if (isPublic(n) && argsMatch(n.getParameterTypes(), argTypes)) {
+ if (argsMatch(n.getParameterTypes(), argTypes)) {
CONSTRUCTOR_CACHE.put(c, new ConstructorCacheEntry(c, n));
return (Constructor<T>)n;
}
}
+
return null;
}
@@ -623,6 +642,27 @@ public final class ClassUtils {
return false;
}
+ /**
+ * Returns a number representing the number of arguments that match the specified parameters.
+ *
+ * @param paramTypes The parameters types specified on a method.
+ * @param argTypes The class types of the arguments being passed to the method.
+ * @return The number of matching arguments, or <code>-1</code> a parameter was found that isn't in the list of args.
+ */
+ public static int fuzzyArgsMatch(Class<?>[] paramTypes, Class<?>[] argTypes) {
+ int matches = 0;
+ outer: for (Class<?> p : paramTypes) {
+ p = getWrapperIfPrimitive(p);
+ for (Class<?> a : argTypes) {
+ if (isParentClass(p, a)) {
+ matches++;
+ continue outer;
+ }
+ }
+ return -1;
+ }
+ return matches;
+ }
/**
* Finds the public constructor that can take in the specified arguments.
@@ -634,7 +674,36 @@ public final class ClassUtils {
* arguments.
*/
public static <T> Constructor<T> findPublicConstructor(Class<T> c, Object...args) {
- return findPublicConstructor(c, getClasses(args));
+ return findPublicConstructor(c, false, getClasses(args));
+ }
+
+ /**
+ * Finds the public constructor that can take in the specified arguments.
+ *
+ * @param c The class we're trying to construct.
+ * @param args The argument types we want to pass into the constructor.
+ * @return
+ * The constructor, or <jk>null</jk> if a public constructor could not be found that takes in the specified
+ * arguments.
+ */
+ public static <T> Constructor<T> findPublicConstructor(Class<T> c, Class<?>...args) {
+ return findPublicConstructor(c, false, args);
+ }
+
+ /**
+ * Finds the public constructor that can take in the specified arguments.
+ *
+ * @param c The class we're trying to construct.
+ * @param fuzzyArgs
+ * Use fuzzy-arg matching.
+ * Find a constructor that best matches the specified args.
+ * @param args The arguments we want to pass into the constructor.
+ * @return
+ * The constructor, or <jk>null</jk> if a public constructor could not be found that takes in the specified
+ * arguments.
+ */
+ public static <T> Constructor<T> findPublicConstructor(Class<T> c, boolean fuzzyArgs, Object...args) {
+ return findPublicConstructor(c, fuzzyArgs, getClasses(args));
}
/**
@@ -718,8 +787,10 @@ public final class ClassUtils {
* @param c2
* The class to instantiate.
* Can also be an instance of the class.
- * @param allowNoArgs
- * If constructor with specified args cannot be found, use the no-args constructor.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* The arguments to pass to the constructor.
* @return
@@ -727,8 +798,8 @@ public final class ClassUtils {
* @throws
* RuntimeException if constructor could not be found or called.
*/
- public static <T> T newInstance(Class<T> c, Object c2, boolean allowNoArgs, Object...args) {
- return newInstanceFromOuter(null, c, c2, allowNoArgs, args);
+ public static <T> T newInstance(Class<T> c, Object c2, boolean fuzzyArgs, Object...args) {
+ return newInstanceFromOuter(null, c, c2, fuzzyArgs, args);
}
/**
@@ -742,8 +813,10 @@ public final class ClassUtils {
* @param c2
* The class to instantiate.
* Can also be an instance of the class.
- * @param allowNoArgs
- * If constructor with specified args cannot be found, use the no-args constructor.
+ * @param fuzzyArgs
+ * Use fuzzy constructor arg matching.
+ * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
+ * <br>No-arg constructors are also used if no other constructors are found.
* @param args
* The arguments to pass to the constructor.
* @return
@@ -752,7 +825,7 @@ public final class ClassUtils {
* RuntimeException if constructor could not be found or called.
*/
@SuppressWarnings("unchecked")
- public static <T> T newInstanceFromOuter(Object outer, Class<T> c, Object c2, boolean allowNoArgs, Object...args) {
+ public static <T> T newInstanceFromOuter(Object outer, Class<T> c, Object c2, boolean fuzzyArgs, Object...args) {
if (c2 == null)
return null;
if (c2 instanceof Class) {
@@ -760,22 +833,14 @@ public final class ClassUtils {
Class<?> c3 = (Class<?>)c2;
if (c3.isInterface() || isAbstract(c3))
return null;
- Constructor<?> con = findPublicConstructor(c3, args);
- if (con != null)
- return (T)con.newInstance(args);
- if (allowNoArgs && args.length != 0)
- con = findPublicConstructor(c3);
+ Constructor<?> con = findPublicConstructor(c3, fuzzyArgs, args);
if (con != null)
- return (T)con.newInstance();
+ return (T)con.newInstance(fuzzyArgs ? getMatchingArgs(con, args) : args);
if (outer != null) {
Object[] args2 = new AList<>().append(outer).appendAll(args).toArray();
- con = findPublicConstructor(c3, args2);
+ con = findPublicConstructor(c3, fuzzyArgs, args2);
if (con != null)
- return (T)con.newInstance(args2);
- if (allowNoArgs && args.length != 0)
- con = findPublicConstructor(c3);
- if (con != null)
- return (T)con.newInstance();
+ return (T)con.newInstance(fuzzyArgs ? getMatchingArgs(con, args) : args);
}
throw new FormattedRuntimeException("Could not instantiate class {0}. Constructor not found.", c.getName());
} catch (Exception e) {
@@ -788,6 +853,21 @@ public final class ClassUtils {
}
}
+ private static Object[] getMatchingArgs(Constructor<?> con, Object[] args) {
+ Class<?>[] paramTypes = con.getParameterTypes();
+ Object[] params = new Object[paramTypes.length];
+ for (int i = 0; i < paramTypes.length; i++) {
+ Class<?> pt = getWrapperIfPrimitive(paramTypes[i]);
+ for (int j = 0; j < args.length; j++) {
+ if (isParentClass(pt, args[j].getClass())) {
+ params[i] = args[j];
+ break;
+ }
+ }
+ }
+ return params;
+ }
+
/**
* Returns all the fields in the specified class and all parent classes.
*