Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 7B9E5200BA8 for ; Mon, 24 Oct 2016 23:02:21 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 7A230160AEB; Mon, 24 Oct 2016 21:02:21 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 0252F160AD7 for ; Mon, 24 Oct 2016 23:02:18 +0200 (CEST) Received: (qmail 90819 invoked by uid 500); 24 Oct 2016 21:02:18 -0000 Mailing-List: contact commits-help@bval.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@bval.apache.org Delivered-To: mailing list commits@bval.apache.org Received: (qmail 90810 invoked by uid 99); 24 Oct 2016 21:02:18 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 24 Oct 2016 21:02:18 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id 9E47B1A7BC5 for ; Mon, 24 Oct 2016 21:02:17 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -1.199 X-Spam-Level: X-Spam-Status: No, score=-1.199 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-2.999] autolearn=disabled Received: from mx1-lw-us.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id 7AE29yWhh32w for ; Mon, 24 Oct 2016 21:02:06 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-lw-us.apache.org (ASF Mail Server at mx1-lw-us.apache.org) with ESMTP id 995625F576 for ; Mon, 24 Oct 2016 21:02:05 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id A37ACE06AA for ; Mon, 24 Oct 2016 21:02:04 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id B455F3A0ED9 for ; Mon, 24 Oct 2016 21:02:03 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1766445 [2/3] - in /bval/trunk: ./ bundle/ bval-core/ bval-core/src/main/java/org/apache/bval/ bval-core/src/main/java/org/apache/bval/model/ bval-core/src/main/java/org/apache/bval/util/ bval-core/src/main/java/org/apache/bval/util/reflec... Date: Mon, 24 Oct 2016 21:02:02 -0000 To: commits@bval.apache.org From: struberg@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20161024210203.B455F3A0ED9@svn01-us-west.apache.org> archived-at: Mon, 24 Oct 2016 21:02:21 -0000 Added: bval/trunk/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java?rev=1766445&view=auto ============================================================================== --- bval/trunk/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java (added) +++ bval/trunk/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java Mon Oct 24 21:02:02 2016 @@ -0,0 +1,1603 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.bval.util.reflection; + +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.bval.util.Validate; + +/** + * Taken from commons-lang3. + * + *

Utility methods focusing on type inspection, particularly with regard to + * generics.

+ * + * @since 3.0 + */ +public class TypeUtils { + + /** + * {@link WildcardType} builder. + * @since 3.2 + */ + public static class WildcardTypeBuilder { + /** + * Constructor + */ + private WildcardTypeBuilder() { + } + + private Type[] upperBounds; + private Type[] lowerBounds; + + /** + * Specify upper bounds of the wildcard type to build. + * @param bounds to set + * @return {@code this} + */ + public WildcardTypeBuilder withUpperBounds(final Type... bounds) { + this.upperBounds = bounds; + return this; + } + + /** + * Specify lower bounds of the wildcard type to build. + * @param bounds to set + * @return {@code this} + */ + public WildcardTypeBuilder withLowerBounds(final Type... bounds) { + this.lowerBounds = bounds; + return this; + } + + /** + * {@inheritDoc} + */ + public WildcardType build() { + return new WildcardTypeImpl(upperBounds, lowerBounds); + } + } + + /** + * ParameterizedType implementation class. + * @since 3.2 + */ + private static final class ParameterizedTypeImpl implements ParameterizedType { + private final Class raw; + private final Type useOwner; + private final Type[] typeArguments; + + /** + * Constructor + * @param raw type + * @param useOwner owner type to use, if any + * @param typeArguments formal type arguments + */ + private ParameterizedTypeImpl(final Class raw, final Type useOwner, final Type[] typeArguments) { + this.raw = raw; + this.useOwner = useOwner; + this.typeArguments = typeArguments.clone(); + } + + /** + * {@inheritDoc} + */ + @Override + public Type getRawType() { + return raw; + } + + /** + * {@inheritDoc} + */ + @Override + public Type getOwnerType() { + return useOwner; + } + + /** + * {@inheritDoc} + */ + @Override + public Type[] getActualTypeArguments() { + return typeArguments.clone(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return TypeUtils.toString(this); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(final Object obj) { + return obj == this || obj instanceof ParameterizedType && TypeUtils.equals(this, ((ParameterizedType) obj)); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings( "deprecation" ) // ObjectUtils.hashCode(Object) has been deprecated in 3.2 + @Override + public int hashCode() { + int result = 71 << 4; + result |= raw.hashCode(); + result <<= 4; + result |= useOwner == null ? 0 : useOwner.hashCode(); + result <<= 8; + result |= Arrays.hashCode(typeArguments); + return result; + } + } + + /** + * WildcardType implementation class. + * @since 3.2 + */ + private static final class WildcardTypeImpl implements WildcardType { + private static final Type[] EMPTY_BOUNDS = new Type[0]; + + private final Type[] upperBounds; + private final Type[] lowerBounds; + + /** + * Constructor + * @param upperBounds of this type + * @param lowerBounds of this type + */ + private WildcardTypeImpl(final Type[] upperBounds, final Type[] lowerBounds) { + this.upperBounds = upperBounds != null ? upperBounds : EMPTY_BOUNDS; + this.lowerBounds = lowerBounds != null ? lowerBounds : EMPTY_BOUNDS; + } + + /** + * {@inheritDoc} + */ + @Override + public Type[] getUpperBounds() { + return upperBounds.clone(); + } + + /** + * {@inheritDoc} + */ + @Override + public Type[] getLowerBounds() { + return lowerBounds.clone(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return TypeUtils.toString(this); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(final Object obj) { + return obj == this || obj instanceof WildcardType && TypeUtils.equals(this, (WildcardType) obj); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + int result = 73 << 8; + result |= Arrays.hashCode(upperBounds); + result <<= 8; + result |= Arrays.hashCode(lowerBounds); + return result; + } + } + + /** + *

{@code TypeUtils} instances should NOT be constructed in standard + * programming. Instead, the class should be used as + * {@code TypeUtils.isAssignable(cls, toClass)}.

This + * constructor is public to permit tools that require a JavaBean instance to + * operate.

+ */ + private TypeUtils() { + } + + /** + *

Checks if the subject type may be implicitly cast to the target type + * following the Java generics rules. If both types are {@link Class} + * objects, the method returns the result of + * {@link Class#isAssignableFrom(Class)}.

+ * + * @param type the subject type to be assigned to the target type + * @param toType the target type + * @return {@code true} if {@code type} is assignable to {@code toType}. + */ + public static boolean isAssignable(final Type type, final Type toType) { + return isAssignable(type, toType, null); + } + + /** + *

Checks if the subject type may be implicitly cast to the target type + * following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toType the target type + * @param typeVarAssigns optional map of type variable assignments + * @return {@code true} if {@code type} is assignable to {@code toType}. + */ + private static boolean isAssignable(final Type type, final Type toType, + final Map, Type> typeVarAssigns) { + if (toType == null || toType instanceof Class) { + return isAssignable(type, (Class) toType); + } + + if (toType instanceof ParameterizedType) { + return isAssignable(type, (ParameterizedType) toType, typeVarAssigns); + } + + if (toType instanceof GenericArrayType) { + return isAssignable(type, (GenericArrayType) toType, typeVarAssigns); + } + + if (toType instanceof WildcardType) { + return isAssignable(type, (WildcardType) toType, typeVarAssigns); + } + + if (toType instanceof TypeVariable) { + return isAssignable(type, (TypeVariable) toType, typeVarAssigns); + } + + throw new IllegalStateException("found an unhandled type: " + toType); + } + + /** + *

Checks if the subject type may be implicitly cast to the target class + * following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toClass the target class + * @return {@code true} if {@code type} is assignable to {@code toClass}. + */ + private static boolean isAssignable(final Type type, final Class toClass) { + if (type == null) { + // consistency with ClassUtils.isAssignable() behavior + return toClass == null || !toClass.isPrimitive(); + } + + // only a null type can be assigned to null type which + // would have cause the previous to return true + if (toClass == null) { + return false; + } + + // all types are assignable to themselves + if (toClass.equals(type)) { + return true; + } + + if (type instanceof Class) { + // just comparing two classes + // also take primitives into account! + return isAssignable((Class) type, toClass); + } + + if (type instanceof ParameterizedType) { + // only have to compare the raw type to the class + return isAssignable(getRawType((ParameterizedType) type), toClass); + } + + // * + if (type instanceof TypeVariable) { + // if any of the bounds are assignable to the class, then the + // type is assignable to the class. + for (final Type bound : ((TypeVariable) type).getBounds()) { + if (isAssignable(bound, toClass)) { + return true; + } + } + + return false; + } + + // the only classes to which a generic array type can be assigned + // are class Object and array classes + if (type instanceof GenericArrayType) { + return toClass.equals(Object.class) + || toClass.isArray() + && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass + .getComponentType()); + } + + // wildcard types are not assignable to a class (though one would think + // "? super Object" would be assignable to Object) + if (type instanceof WildcardType) { + return false; + } + + throw new IllegalStateException("found an unhandled type: " + type); + } + + private static boolean isAssignable(Class cls, final Class toClass) { + if (toClass == null) { + return false; + } + // have to check for null, as isAssignableFrom doesn't + if (cls == null) { + return !toClass.isPrimitive(); + } + + if (cls.isPrimitive() && !toClass.isPrimitive()) { + cls = Reflection.primitiveToWrapper(cls); + if (cls == null) { + return false; + } + } + if (toClass.isPrimitive() && !cls.isPrimitive()) { + cls = Reflection.wrapperToPrimitive(cls); + if (cls == null) { + return false; + } + } + if (cls.equals(toClass)) { + return true; + } + if (cls.isPrimitive()) { + if (toClass.isPrimitive() == false) { + return false; + } + if (Integer.TYPE.equals(cls)) { + return Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Long.TYPE.equals(cls)) { + return Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Boolean.TYPE.equals(cls)) { + return false; + } + if (Double.TYPE.equals(cls)) { + return false; + } + if (Float.TYPE.equals(cls)) { + return Double.TYPE.equals(toClass); + } + if (Character.TYPE.equals(cls)) { + return Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Short.TYPE.equals(cls)) { + return Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Byte.TYPE.equals(cls)) { + return Short.TYPE.equals(toClass) + || Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + // should never get here + return false; + } + return toClass.isAssignableFrom(cls); + + } + + /** + *

Checks if the subject type may be implicitly cast to the target + * parameterized type following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toParameterizedType the target parameterized type + * @param typeVarAssigns a map with type variables + * @return {@code true} if {@code type} is assignable to {@code toType}. + */ + private static boolean isAssignable(final Type type, final ParameterizedType toParameterizedType, + final Map, Type> typeVarAssigns) { + if (type == null) { + return true; + } + + // only a null type can be assigned to null type which + // would have cause the previous to return true + if (toParameterizedType == null) { + return false; + } + + // all types are assignable to themselves + if (toParameterizedType.equals(type)) { + return true; + } + + // get the target type's raw type + final Class toClass = getRawType(toParameterizedType); + // get the subject type's type arguments including owner type arguments + // and supertype arguments up to and including the target class. + final Map, Type> fromTypeVarAssigns = getTypeArguments(type, toClass, null); + + // null means the two types are not compatible + if (fromTypeVarAssigns == null) { + return false; + } + + // compatible types, but there's no type arguments. this is equivalent + // to comparing Map< ?, ? > to Map, and raw types are always assignable + // to parameterized types. + if (fromTypeVarAssigns.isEmpty()) { + return true; + } + + // get the target type's type arguments including owner type arguments + final Map, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType, + toClass, typeVarAssigns); + + // now to check each type argument + for (final TypeVariable var : toTypeVarAssigns.keySet()) { + final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns); + final Type fromTypeArg = unrollVariableAssignments(var, fromTypeVarAssigns); + + if (toTypeArg == null && fromTypeArg instanceof Class) { + continue; + } + + // parameters must either be absent from the subject type, within + // the bounds of the wildcard type, or be an exact match to the + // parameters of the target type. + if (fromTypeArg != null + && !toTypeArg.equals(fromTypeArg) + && !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg, toTypeArg, + typeVarAssigns))) { + return false; + } + } + return true; + } + + /** + * Look up {@code var} in {@code typeVarAssigns} transitively, + * i.e. keep looking until the value found is not a type variable. + * @param var the type variable to look up + * @param typeVarAssigns the map used for the look up + * @return Type or {@code null} if some variable was not in the map + * @since 3.2 + */ + private static Type unrollVariableAssignments(TypeVariable var, final Map, Type> typeVarAssigns) { + Type result; + do { + result = typeVarAssigns.get(var); + if (result instanceof TypeVariable && !result.equals(var)) { + var = (TypeVariable) result; + continue; + } + break; + } while (true); + return result; + } + + /** + *

Checks if the subject type may be implicitly cast to the target + * generic array type following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toGenericArrayType the target generic array type + * @param typeVarAssigns a map with type variables + * @return {@code true} if {@code type} is assignable to + * {@code toGenericArrayType}. + */ + private static boolean isAssignable(final Type type, final GenericArrayType toGenericArrayType, + final Map, Type> typeVarAssigns) { + if (type == null) { + return true; + } + + // only a null type can be assigned to null type which + // would have cause the previous to return true + if (toGenericArrayType == null) { + return false; + } + + // all types are assignable to themselves + if (toGenericArrayType.equals(type)) { + return true; + } + + final Type toComponentType = toGenericArrayType.getGenericComponentType(); + + if (type instanceof Class) { + final Class cls = (Class) type; + + // compare the component types + return cls.isArray() + && isAssignable(cls.getComponentType(), toComponentType, typeVarAssigns); + } + + if (type instanceof GenericArrayType) { + // compare the component types + return isAssignable(((GenericArrayType) type).getGenericComponentType(), + toComponentType, typeVarAssigns); + } + + if (type instanceof WildcardType) { + // so long as one of the upper bounds is assignable, it's good + for (final Type bound : getImplicitUpperBounds((WildcardType) type)) { + if (isAssignable(bound, toGenericArrayType)) { + return true; + } + } + + return false; + } + + if (type instanceof TypeVariable) { + // probably should remove the following logic and just return false. + // type variables cannot specify arrays as bounds. + for (final Type bound : getImplicitBounds((TypeVariable) type)) { + if (isAssignable(bound, toGenericArrayType)) { + return true; + } + } + + return false; + } + + if (type instanceof ParameterizedType) { + // the raw type of a parameterized type is never an array or + // generic array, otherwise the declaration would look like this: + // Collection[]< ? extends String > collection; + return false; + } + + throw new IllegalStateException("found an unhandled type: " + type); + } + + /** + *

Checks if the subject type may be implicitly cast to the target + * wildcard type following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toWildcardType the target wildcard type + * @param typeVarAssigns a map with type variables + * @return {@code true} if {@code type} is assignable to + * {@code toWildcardType}. + */ + private static boolean isAssignable(final Type type, final WildcardType toWildcardType, + final Map, Type> typeVarAssigns) { + if (type == null) { + return true; + } + + // only a null type can be assigned to null type which + // would have cause the previous to return true + if (toWildcardType == null) { + return false; + } + + // all types are assignable to themselves + if (toWildcardType.equals(type)) { + return true; + } + + final Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType); + final Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType); + + if (type instanceof WildcardType) { + final WildcardType wildcardType = (WildcardType) type; + final Type[] upperBounds = getImplicitUpperBounds(wildcardType); + final Type[] lowerBounds = getImplicitLowerBounds(wildcardType); + + for (Type toBound : toUpperBounds) { + // if there are assignments for unresolved type variables, + // now's the time to substitute them. + toBound = substituteTypeVariables(toBound, typeVarAssigns); + + // each upper bound of the subject type has to be assignable to + // each + // upper bound of the target type + for (final Type bound : upperBounds) { + if (!isAssignable(bound, toBound, typeVarAssigns)) { + return false; + } + } + } + + for (Type toBound : toLowerBounds) { + // if there are assignments for unresolved type variables, + // now's the time to substitute them. + toBound = substituteTypeVariables(toBound, typeVarAssigns); + + // each lower bound of the target type has to be assignable to + // each + // lower bound of the subject type + for (final Type bound : lowerBounds) { + if (!isAssignable(toBound, bound, typeVarAssigns)) { + return false; + } + } + } + return true; + } + + for (final Type toBound : toUpperBounds) { + // if there are assignments for unresolved type variables, + // now's the time to substitute them. + if (!isAssignable(type, substituteTypeVariables(toBound, typeVarAssigns), + typeVarAssigns)) { + return false; + } + } + + for (final Type toBound : toLowerBounds) { + // if there are assignments for unresolved type variables, + // now's the time to substitute them. + if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns), type, + typeVarAssigns)) { + return false; + } + } + return true; + } + + /** + *

Checks if the subject type may be implicitly cast to the target type + * variable following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toTypeVariable the target type variable + * @param typeVarAssigns a map with type variables + * @return {@code true} if {@code type} is assignable to + * {@code toTypeVariable}. + */ + private static boolean isAssignable(final Type type, final TypeVariable toTypeVariable, + final Map, Type> typeVarAssigns) { + if (type == null) { + return true; + } + + // only a null type can be assigned to null type which + // would have cause the previous to return true + if (toTypeVariable == null) { + return false; + } + + // all types are assignable to themselves + if (toTypeVariable.equals(type)) { + return true; + } + + if (type instanceof TypeVariable) { + // a type variable is assignable to another type variable, if + // and only if the former is the latter, extends the latter, or + // is otherwise a descendant of the latter. + final Type[] bounds = getImplicitBounds((TypeVariable) type); + + for (final Type bound : bounds) { + if (isAssignable(bound, toTypeVariable, typeVarAssigns)) { + return true; + } + } + } + + if (type instanceof Class || type instanceof ParameterizedType + || type instanceof GenericArrayType || type instanceof WildcardType) { + return false; + } + + throw new IllegalStateException("found an unhandled type: " + type); + } + + /** + *

Find the mapping for {@code type} in {@code typeVarAssigns}.

+ * + * @param type the type to be replaced + * @param typeVarAssigns the map with type variables + * @return the replaced type + * @throws IllegalArgumentException if the type cannot be substituted + */ + private static Type substituteTypeVariables(final Type type, final Map, Type> typeVarAssigns) { + if (type instanceof TypeVariable && typeVarAssigns != null) { + final Type replacementType = typeVarAssigns.get(type); + + if (replacementType == null) { + throw new IllegalArgumentException("missing assignment type for type variable " + + type); + } + return replacementType; + } + return type; + } + + /** + *

Retrieves all the type arguments for this parameterized type + * including owner hierarchy arguments such as + * {@code Outer.Inner.DeepInner} . + * The arguments are returned in a + * {@link Map} specifying the argument type for each {@link TypeVariable}. + *

+ * + * @param type specifies the subject parameterized type from which to + * harvest the parameters. + * @return a {@code Map} of the type arguments to their respective type + * variables. + */ + public static Map, Type> getTypeArguments(final ParameterizedType type) { + return getTypeArguments(type, getRawType(type), null); + } + + /** + *

Gets the type arguments of a class/interface based on a subtype. For + * instance, this method will determine that both of the parameters for the + * interface {@link Map} are {@link Object} for the subtype + * {@link java.util.Properties Properties} even though the subtype does not + * directly implement the {@code Map} interface.

+ *

This method returns {@code null} if {@code type} is not assignable to + * {@code toClass}. It returns an empty map if none of the classes or + * interfaces in its inheritance hierarchy specify any type arguments.

+ *

A side effect of this method is that it also retrieves the type + * arguments for the classes and interfaces that are part of the hierarchy + * between {@code type} and {@code toClass}. So with the above + * example, this method will also determine that the type arguments for + * {@link java.util.Hashtable Hashtable} are also both {@code Object}. + * In cases where the interface specified by {@code toClass} is + * (indirectly) implemented more than once (e.g. where {@code toClass} + * specifies the interface {@link Iterable Iterable} and + * {@code type} specifies a parameterized type that implements both + * {@link Set Set} and {@link java.util.Collection Collection}), + * this method will look at the inheritance hierarchy of only one of the + * implementations/subclasses; the first interface encountered that isn't a + * subinterface to one of the others in the {@code type} to + * {@code toClass} hierarchy.

+ * + * @param type the type from which to determine the type parameters of + * {@code toClass} + * @param toClass the class whose type parameters are to be determined based + * on the subtype {@code type} + * @return a {@code Map} of the type assignments for the type variables in + * each type in the inheritance hierarchy from {@code type} to + * {@code toClass} inclusive. + */ + public static Map, Type> getTypeArguments(final Type type, final Class toClass) { + return getTypeArguments(type, toClass, null); + } + + /** + *

Return a map of the type arguments of @{code type} in the context of {@code toClass}.

+ * + * @param type the type in question + * @param toClass the class + * @param subtypeVarAssigns a map with type variables + * @return the {@code Map} with type arguments + */ + private static Map, Type> getTypeArguments(final Type type, final Class toClass, + final Map, Type> subtypeVarAssigns) { + if (type instanceof Class) { + return getTypeArguments((Class) type, toClass, subtypeVarAssigns); + } + + if (type instanceof ParameterizedType) { + return getTypeArguments((ParameterizedType) type, toClass, subtypeVarAssigns); + } + + if (type instanceof GenericArrayType) { + return getTypeArguments(((GenericArrayType) type).getGenericComponentType(), toClass + .isArray() ? toClass.getComponentType() : toClass, subtypeVarAssigns); + } + + // since wildcard types are not assignable to classes, should this just + // return null? + if (type instanceof WildcardType) { + for (final Type bound : getImplicitUpperBounds((WildcardType) type)) { + // find the first bound that is assignable to the target class + if (isAssignable(bound, toClass)) { + return getTypeArguments(bound, toClass, subtypeVarAssigns); + } + } + + return null; + } + + if (type instanceof TypeVariable) { + for (final Type bound : getImplicitBounds((TypeVariable) type)) { + // find the first bound that is assignable to the target class + if (isAssignable(bound, toClass)) { + return getTypeArguments(bound, toClass, subtypeVarAssigns); + } + } + + return null; + } + throw new IllegalStateException("found an unhandled type: " + type); + } + + /** + *

Return a map of the type arguments of a parameterized type in the context of {@code toClass}.

+ * + * @param parameterizedType the parameterized type + * @param toClass the class + * @param subtypeVarAssigns a map with type variables + * @return the {@code Map} with type arguments + */ + private static Map, Type> getTypeArguments( + final ParameterizedType parameterizedType, final Class toClass, + final Map, Type> subtypeVarAssigns) { + final Class cls = getRawType(parameterizedType); + + // make sure they're assignable + if (!isAssignable(cls, toClass)) { + return null; + } + + final Type ownerType = parameterizedType.getOwnerType(); + Map, Type> typeVarAssigns; + + if (ownerType instanceof ParameterizedType) { + // get the owner type arguments first + final ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType; + typeVarAssigns = getTypeArguments(parameterizedOwnerType, + getRawType(parameterizedOwnerType), subtypeVarAssigns); + } else { + // no owner, prep the type variable assignments map + typeVarAssigns = subtypeVarAssigns == null ? new HashMap, Type>() + : new HashMap, Type>(subtypeVarAssigns); + } + + // get the subject parameterized type's arguments + final Type[] typeArgs = parameterizedType.getActualTypeArguments(); + // and get the corresponding type variables from the raw class + final TypeVariable[] typeParams = cls.getTypeParameters(); + + // map the arguments to their respective type variables + for (int i = 0; i < typeParams.length; i++) { + final Type typeArg = typeArgs[i]; + typeVarAssigns.put(typeParams[i], typeVarAssigns.containsKey(typeArg) ? typeVarAssigns + .get(typeArg) : typeArg); + } + + if (toClass.equals(cls)) { + // target class has been reached. Done. + return typeVarAssigns; + } + + // walk the inheritance hierarchy until the target class is reached + return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns); + } + + /** + *

Return a map of the type arguments of a class in the context of @{code toClass}.

+ * + * @param cls the class in question + * @param toClass the context class + * @param subtypeVarAssigns a map with type variables + * @return the {@code Map} with type arguments + */ + private static Map, Type> getTypeArguments(Class cls, final Class toClass, + final Map, Type> subtypeVarAssigns) { + // make sure they're assignable + if (!isAssignable(cls, toClass)) { + return null; + } + + // can't work with primitives + if (cls.isPrimitive()) { + // both classes are primitives? + if (toClass.isPrimitive()) { + // dealing with widening here. No type arguments to be + // harvested with these two types. + return new HashMap, Type>(); + } + + // work with wrapper the wrapper class instead of the primitive + cls = Reflection.primitiveToWrapper(cls); + } + + // create a copy of the incoming map, or an empty one if it's null + final HashMap, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap, Type>() + : new HashMap, Type>(subtypeVarAssigns); + + // has target class been reached? + if (toClass.equals(cls)) { + return typeVarAssigns; + } + + // walk the inheritance hierarchy until the target class is reached + return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns); + } + + /** + *

Get the closest parent type to the + * super class specified by {@code superClass}.

+ * + * @param cls the class in question + * @param superClass the super class + * @return the closes parent type + */ + private static Type getClosestParentType(final Class cls, final Class superClass) { + // only look at the interfaces if the super class is also an interface + if (superClass.isInterface()) { + // get the generic interfaces of the subject class + final Type[] interfaceTypes = cls.getGenericInterfaces(); + // will hold the best generic interface match found + Type genericInterface = null; + + // find the interface closest to the super class + for (final Type midType : interfaceTypes) { + Class midClass = null; + + if (midType instanceof ParameterizedType) { + midClass = getRawType((ParameterizedType) midType); + } else if (midType instanceof Class) { + midClass = (Class) midType; + } else { + throw new IllegalStateException("Unexpected generic" + + " interface type found: " + midType); + } + + // check if this interface is further up the inheritance chain + // than the previously found match + if (isAssignable(midClass, superClass) + && isAssignable(genericInterface, (Type) midClass)) { + genericInterface = midType; + } + } + + // found a match? + if (genericInterface != null) { + return genericInterface; + } + } + + // none of the interfaces were descendants of the target class, so the + // super class has to be one, instead + return cls.getGenericSuperclass(); + } + + /** + *

Checks if the given value can be assigned to the target type + * following the Java generics rules.

+ * + * @param value the value to be checked + * @param type the target type + * @return {@code true} if {@code value} is an instance of {@code type}. + */ + public static boolean isInstance(final Object value, final Type type) { + if (type == null) { + return false; + } + + return value == null ? !(type instanceof Class) || !((Class) type).isPrimitive() + : isAssignable(value.getClass(), type, null); + } + + /** + *

This method strips out the redundant upper bound types in type + * variable types and wildcard types (or it would with wildcard types if + * multiple upper bounds were allowed).

Example, with the variable + * type declaration: + * + *

<K extends java.util.Collection<String> &
+     * java.util.List<String>>
+ * + *

+ * since {@code List} is a subinterface of {@code Collection}, + * this method will return the bounds as if the declaration had been: + *

+ * + *
<K extends java.util.List<String>>
+ * + * @param bounds an array of types representing the upper bounds of either + * {@link WildcardType} or {@link TypeVariable}, not {@code null}. + * @return an array containing the values from {@code bounds} minus the + * redundant types. + */ + public static Type[] normalizeUpperBounds(final Type[] bounds) { + Validate.notNull(bounds, "null value specified for bounds array"); + // don't bother if there's only one (or none) type + if (bounds.length < 2) { + return bounds; + } + + final Set types = new HashSet(bounds.length); + + for (final Type type1 : bounds) { + boolean subtypeFound = false; + + for (final Type type2 : bounds) { + if (type1 != type2 && isAssignable(type2, type1, null)) { + subtypeFound = true; + break; + } + } + + if (!subtypeFound) { + types.add(type1); + } + } + + return types.toArray(new Type[types.size()]); + } + + /** + *

Returns an array containing the sole type of {@link Object} if + * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it + * returns the result of {@link TypeVariable#getBounds()} passed into + * {@link #normalizeUpperBounds}.

+ * + * @param typeVariable the subject type variable, not {@code null} + * @return a non-empty array containing the bounds of the type variable. + */ + public static Type[] getImplicitBounds(final TypeVariable typeVariable) { + Validate.notNull(typeVariable, "typeVariable is null"); + final Type[] bounds = typeVariable.getBounds(); + + return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds); + } + + /** + *

Returns an array containing the sole value of {@link Object} if + * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise, + * it returns the result of {@link WildcardType#getUpperBounds()} + * passed into {@link #normalizeUpperBounds}.

+ * + * @param wildcardType the subject wildcard type, not {@code null} + * @return a non-empty array containing the upper bounds of the wildcard + * type. + */ + public static Type[] getImplicitUpperBounds(final WildcardType wildcardType) { + Validate.notNull(wildcardType, "wildcardType is null"); + final Type[] bounds = wildcardType.getUpperBounds(); + + return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds); + } + + /** + *

Returns an array containing a single value of {@code null} if + * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise, + * it returns the result of {@link WildcardType#getLowerBounds()}.

+ * + * @param wildcardType the subject wildcard type, not {@code null} + * @return a non-empty array containing the lower bounds of the wildcard + * type. + */ + public static Type[] getImplicitLowerBounds(final WildcardType wildcardType) { + Validate.notNull(wildcardType, "wildcardType is null"); + final Type[] bounds = wildcardType.getLowerBounds(); + + return bounds.length == 0 ? new Type[] { null } : bounds; + } + + /** + *

Transforms the passed in type to a {@link Class} object. Type-checking method of convenience.

+ * + * @param parameterizedType the type to be converted + * @return the corresponding {@code Class} object + * @throws IllegalStateException if the conversion fails + */ + private static Class getRawType(final ParameterizedType parameterizedType) { + final Type rawType = parameterizedType.getRawType(); + + // check if raw type is a Class object + // not currently necessary, but since the return type is Type instead of + // Class, there's enough reason to believe that future versions of Java + // may return other Type implementations. And type-safety checking is + // rarely a bad idea. + if (!(rawType instanceof Class)) { + throw new IllegalStateException("Wait... What!? Type of rawType: " + rawType); + } + + return (Class) rawType; + } + + /** + *

Get the raw type of a Java type, given its context. Primarily for use + * with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do + * not know the runtime type of {@code type}: if you know you have a + * {@link Class} instance, it is already raw; if you know you have a + * {@link ParameterizedType}, its raw type is only a method call away.

+ * + * @param type to resolve + * @param assigningType type to be resolved against + * @return the resolved {@link Class} object or {@code null} if + * the type could not be resolved + */ + public static Class getRawType(final Type type, final Type assigningType) { + if (type instanceof Class) { + // it is raw, no problem + return (Class) type; + } + + if (type instanceof ParameterizedType) { + // simple enough to get the raw type of a ParameterizedType + return getRawType((ParameterizedType) type); + } + + if (type instanceof TypeVariable) { + if (assigningType == null) { + return null; + } + + // get the entity declaring this type variable + final Object genericDeclaration = ((TypeVariable) type).getGenericDeclaration(); + + // can't get the raw type of a method- or constructor-declared type + // variable + if (!(genericDeclaration instanceof Class)) { + return null; + } + + // get the type arguments for the declaring class/interface based + // on the enclosing type + final Map, Type> typeVarAssigns = getTypeArguments(assigningType, + (Class) genericDeclaration); + + // enclosingType has to be a subclass (or subinterface) of the + // declaring type + if (typeVarAssigns == null) { + return null; + } + + // get the argument assigned to this type variable + final Type typeArgument = typeVarAssigns.get(type); + + if (typeArgument == null) { + return null; + } + + // get the argument for this type variable + return getRawType(typeArgument, assigningType); + } + + if (type instanceof GenericArrayType) { + // get raw component type + final Class rawComponentType = getRawType(((GenericArrayType) type) + .getGenericComponentType(), assigningType); + + // create array type from raw component type and return its class + return Array.newInstance(rawComponentType, 0).getClass(); + } + + // (hand-waving) this is not the method you're looking for + if (type instanceof WildcardType) { + return null; + } + + throw new IllegalArgumentException("unknown type: " + type); + } + + /** + * Learn whether the specified type denotes an array type. + * @param type the type to be checked + * @return {@code true} if {@code type} is an array class or a {@link GenericArrayType}. + */ + public static boolean isArrayType(final Type type) { + return type instanceof GenericArrayType || type instanceof Class && ((Class) type).isArray(); + } + + /** + * Get the array component type of {@code type}. + * @param type the type to be checked + * @return component type or null if type is not an array type + */ + public static Type getArrayComponentType(final Type type) { + if (type instanceof Class) { + final Class clazz = (Class) type; + return clazz.isArray() ? clazz.getComponentType() : null; + } + if (type instanceof GenericArrayType) { + return ((GenericArrayType) type).getGenericComponentType(); + } + return null; + } + + /** + * Get a type representing {@code type} with variable assignments "unrolled." + * + * @param typeArguments as from {@link TypeUtils#getTypeArguments(Type, Class)} + * @param type the type to unroll variable assignments for + * @return Type + * @since 3.2 + */ + public static Type unrollVariables(Map, Type> typeArguments, final Type type) { + if (typeArguments == null) { + typeArguments = Collections., Type> emptyMap(); + } + if (containsTypeVariables(type)) { + if (type instanceof TypeVariable) { + return unrollVariables(typeArguments, typeArguments.get(type)); + } + if (type instanceof ParameterizedType) { + final ParameterizedType p = (ParameterizedType) type; + final Map, Type> parameterizedTypeArguments; + if (p.getOwnerType() == null) { + parameterizedTypeArguments = typeArguments; + } else { + parameterizedTypeArguments = new HashMap, Type>(typeArguments); + parameterizedTypeArguments.putAll(TypeUtils.getTypeArguments(p)); + } + final Type[] args = p.getActualTypeArguments(); + for (int i = 0; i < args.length; i++) { + final Type unrolled = unrollVariables(parameterizedTypeArguments, args[i]); + if (unrolled != null) { + args[i] = unrolled; + } + } + return parameterizeWithOwner(p.getOwnerType(), (Class) p.getRawType(), args); + } + if (type instanceof WildcardType) { + final WildcardType wild = (WildcardType) type; + return wildcardType().withUpperBounds(unrollBounds(typeArguments, wild.getUpperBounds())) + .withLowerBounds(unrollBounds(typeArguments, wild.getLowerBounds())).build(); + } + } + return type; + } + + /** + * Local helper method to unroll variables in a type bounds array. + * + * @param typeArguments assignments {@link Map} + * @param bounds in which to expand variables + * @return {@code bounds} with any variables reassigned + * @since 3.2 + */ + private static Type[] unrollBounds(final Map, Type> typeArguments, final Type[] bounds) { + Type[] result = bounds; + int i = 0; + for (; i < result.length; i++) { + final Type unrolled = unrollVariables(typeArguments, result[i]); + if (unrolled == null) { + List types = Arrays.asList(result); + types.remove(i--); + result = types.toArray(new Type[types.size()]); + } else { + result[i] = unrolled; + } + } + return result; + } + + /** + * Learn, recursively, whether any of the type parameters associated with {@code type} are bound to variables. + * + * @param type the type to check for type variables + * @return boolean + * @since 3.2 + */ + public static boolean containsTypeVariables(final Type type) { + if (type instanceof TypeVariable) { + return true; + } + if (type instanceof Class) { + return ((Class) type).getTypeParameters().length > 0; + } + if (type instanceof ParameterizedType) { + for (final Type arg : ((ParameterizedType) type).getActualTypeArguments()) { + if (containsTypeVariables(arg)) { + return true; + } + } + return false; + } + if (type instanceof WildcardType) { + final WildcardType wild = (WildcardType) type; + return containsTypeVariables(TypeUtils.getImplicitLowerBounds(wild)[0]) + || containsTypeVariables(TypeUtils.getImplicitUpperBounds(wild)[0]); + } + return false; + } + + + /** + * Create a parameterized type instance. + * + * @param owner the owning type + * @param raw the raw class to create a parameterized type instance for + * @param typeArguments the types used for parameterization + * + * @return {@link ParameterizedType} + * @since 3.2 + */ + public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class raw, + final Type... typeArguments) { + Validate.notNull(raw, "raw class is null"); + final Type useOwner; + if (raw.getEnclosingClass() == null) { + Validate.isTrue(owner == null, "no owner allowed for top-level %s", raw); + useOwner = null; + } else if (owner == null) { + useOwner = raw.getEnclosingClass(); + } else { + Validate.isTrue(TypeUtils.isAssignable(owner, raw.getEnclosingClass()), + "%s is invalid owner type for parameterized %s", owner, raw); + useOwner = owner; + } + Validate.isTrue(raw.getTypeParameters().length == typeArguments.length, + "invalid number of type parameters specified: expected %d, got %d", raw.getTypeParameters().length, + typeArguments.length); + + return new ParameterizedTypeImpl(raw, useOwner, typeArguments); + } + + /** + * Get a {@link WildcardTypeBuilder}. + * @return {@link WildcardTypeBuilder} + * @since 3.2 + */ + public static WildcardTypeBuilder wildcardType() { + return new WildcardTypeBuilder(); + } + + /** + * Check equality of types. + * + * @param t1 the first type + * @param t2 the second type + * @return boolean + * @since 3.2 + */ + @SuppressWarnings( "deprecation" ) // ObjectUtils.equals(Object, Object) has been deprecated in 3.2 + public static boolean equals(final Type t1, final Type t2) { + if (t1 == t2) { + return true; + } + if (t1 == null || t2 == null) { + return false; + } + + if (t1.equals(t2)) { + return true; + } + if (t1 instanceof ParameterizedType) { + return equals((ParameterizedType) t1, t2); + } + if (t1 instanceof GenericArrayType) { + return equals((GenericArrayType) t1, t2); + } + if (t1 instanceof WildcardType) { + return equals((WildcardType) t1, t2); + } + return false; + } + + /** + * Learn whether {@code t} equals {@code p}. + * @param p LHS + * @param t RHS + * @return boolean + * @since 3.2 + */ + private static boolean equals(final ParameterizedType p, final Type t) { + if (t instanceof ParameterizedType) { + final ParameterizedType other = (ParameterizedType) t; + if (equals(p.getRawType(), other.getRawType()) && equals(p.getOwnerType(), other.getOwnerType())) { + return equals(p.getActualTypeArguments(), other.getActualTypeArguments()); + } + } + return false; + } + + /** + * Learn whether {@code t} equals {@code a}. + * @param a LHS + * @param t RHS + * @return boolean + * @since 3.2 + */ + private static boolean equals(final GenericArrayType a, final Type t) { + return t instanceof GenericArrayType + && equals(a.getGenericComponentType(), ((GenericArrayType) t).getGenericComponentType()); + } + + /** + * Learn whether {@code t} equals {@code w}. + * @param w LHS + * @param t RHS + * @return boolean + * @since 3.2 + */ + private static boolean equals(final WildcardType w, final Type t) { + if (t instanceof WildcardType) { + final WildcardType other = (WildcardType) t; + return equals(getImplicitLowerBounds(w), getImplicitLowerBounds(other)) + && equals(getImplicitUpperBounds(w), getImplicitUpperBounds(other)); + } + return false; + } + + /** + * Learn whether {@code t1} equals {@code t2}. + * @param t1 LHS + * @param t2 RHS + * @return boolean + * @since 3.2 + */ + private static boolean equals(final Type[] t1, final Type[] t2) { + if (t1.length == t2.length) { + for (int i = 0; i < t1.length; i++) { + if (!equals(t1[i], t2[i])) { + return false; + } + } + return true; + } + return false; + } + + /** + * Present a given type as a Java-esque String. + * + * @param type the type to create a String representation for, not {@code null} + * @return String + * @since 3.2 + */ + public static String toString(final Type type) { + Validate.notNull(type); + if (type instanceof Class) { + return classToString((Class) type); + } + if (type instanceof ParameterizedType) { + return parameterizedTypeToString((ParameterizedType) type); + } + if (type instanceof WildcardType) { + return wildcardTypeToString((WildcardType) type); + } + if (type instanceof TypeVariable) { + return typeVariableToString((TypeVariable) type); + } + if (type instanceof GenericArrayType) { + return genericArrayTypeToString((GenericArrayType) type); + } + throw new IllegalArgumentException(type.toString()); + } + + + /** + * Format a {@link Class} as a {@link String}. + * @param c {@code Class} to format + * @return String + * @since 3.2 + */ + private static String classToString(final Class c) { + final StringBuilder buf = new StringBuilder(); + + if (c.getEnclosingClass() != null) { + buf.append(classToString(c.getEnclosingClass())).append('.').append(c.getSimpleName()); + } else { + buf.append(c.getName()); + } + if (c.getTypeParameters().length > 0) { + buf.append('<'); + appendAllTo(buf, ", ", c.getTypeParameters()); + buf.append('>'); + } + return buf.toString(); + } + + /** + * Format a {@link TypeVariable} as a {@link String}. + * @param v {@code TypeVariable} to format + * @return String + * @since 3.2 + */ + private static String typeVariableToString(final TypeVariable v) { + final StringBuilder buf = new StringBuilder(v.getName()); + final Type[] bounds = v.getBounds(); + if (bounds.length > 0 && !(bounds.length == 1 && Object.class.equals(bounds[0]))) { + buf.append(" extends "); + appendAllTo(buf, " & ", v.getBounds()); + } + return buf.toString(); + } + + /** + * Format a {@link ParameterizedType} as a {@link String}. + * @param p {@code ParameterizedType} to format + * @return String + * @since 3.2 + */ + private static String parameterizedTypeToString(final ParameterizedType p) { + final StringBuilder buf = new StringBuilder(); + + final Type useOwner = p.getOwnerType(); + final Class raw = (Class) p.getRawType(); + final Type[] typeArguments = p.getActualTypeArguments(); + if (useOwner == null) { + buf.append(raw.getName()); + } else { + if (useOwner instanceof Class) { + buf.append(((Class) useOwner).getName()); + } else { + buf.append(useOwner.toString()); + } + buf.append('.').append(raw.getSimpleName()); + } + + appendAllTo(buf.append('<'), ", ", typeArguments).append('>'); + return buf.toString(); + } + + /** + * Format a {@link WildcardType} as a {@link String}. + * @param w {@code WildcardType} to format + * @return String + * @since 3.2 + */ + private static String wildcardTypeToString(final WildcardType w) { + final StringBuilder buf = new StringBuilder().append('?'); + final Type[] lowerBounds = w.getLowerBounds(); + final Type[] upperBounds = w.getUpperBounds(); + if (lowerBounds.length > 1 || lowerBounds.length == 1 && lowerBounds[0] != null) { + appendAllTo(buf.append(" super "), " & ", lowerBounds); + } else if (upperBounds.length > 1 || upperBounds.length == 1 && !Object.class.equals(upperBounds[0])) { + appendAllTo(buf.append(" extends "), " & ", upperBounds); + } + return buf.toString(); + } + + /** + * Format a {@link GenericArrayType} as a {@link String}. + * @param g {@code GenericArrayType} to format + * @return String + * @since 3.2 + */ + private static String genericArrayTypeToString(final GenericArrayType g) { + return String.format("%s[]", toString(g.getGenericComponentType())); + } + + /** + * Append {@code types} to @{code buf} with separator {@code sep}. + * @param buf destination + * @param sep separator + * @param types to append + * @return {@code buf} + * @since 3.2 + */ + private static StringBuilder appendAllTo(final StringBuilder buf, final String sep, final Type... types) { + if (types.length > 0) { + buf.append(toString(types[0])); + for (int i = 1; i < types.length; i++) { + buf.append(sep).append(toString(types[i])); + } + } + return buf; + } + +} Propchange: bval/trunk/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java ------------------------------------------------------------------------------ svn:eol-style = native Added: bval/trunk/bval-core/src/test/java/org/apache/bval/util/StringUtilsTest.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-core/src/test/java/org/apache/bval/util/StringUtilsTest.java?rev=1766445&view=auto ============================================================================== --- bval/trunk/bval-core/src/test/java/org/apache/bval/util/StringUtilsTest.java (added) +++ bval/trunk/bval-core/src/test/java/org/apache/bval/util/StringUtilsTest.java Mon Oct 24 21:02:02 2016 @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.bval.util; + +import org.junit.Assert; +import org.junit.Test; + +public class StringUtilsTest { + + @Test + public void testStringSplit() { + Assert.assertArrayEquals(new String[0], StringUtils.split(null)); + Assert.assertArrayEquals(new String[0], StringUtils.split("")); + Assert.assertArrayEquals(new String[0], StringUtils.split(" ")); + Assert.assertArrayEquals(new String[0], StringUtils.split(" \n ")); + Assert.assertArrayEquals(new String[]{"a", "bbb", "cccc"}, StringUtils.split(" a bbb cccc")); + } +} Propchange: bval/trunk/bval-core/src/test/java/org/apache/bval/util/StringUtilsTest.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: bval/trunk/bval-jsr/pom.xml URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/pom.xml?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/pom.xml (original) +++ bval/trunk/bval-jsr/pom.xml Mon Oct 24 21:02:02 2016 @@ -279,42 +279,6 @@ org.apache.maven.plugins - maven-shade-plugin - - - - org.apache.commons:commons-lang3 - - - - - org.apache.commons:commons-lang3 - - META-INF/maven/** - - - - - - org.apache.commons - org.apache.bval.jsr._oac - - - true - true - true - true - - - - - shade - - - - - - org.apache.maven.plugins maven-surefire-plugin Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java Mon Oct 24 21:02:02 2016 @@ -42,7 +42,6 @@ import javax.validation.executable.Valid import javax.validation.metadata.BeanDescriptor; import javax.validation.metadata.MethodType; -import org.apache.commons.lang3.Validate; import java.lang.reflect.Modifier; import java.lang.reflect.Type; @@ -53,6 +52,8 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.bval.util.Validate; + /** * CDI {@link Extension} for Apache BVal setup. */ Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationConstraintBuilder.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationConstraintBuilder.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationConstraintBuilder.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationConstraintBuilder.java Mon Oct 24 21:02:02 2016 @@ -21,8 +21,7 @@ package org.apache.bval.jsr; import org.apache.bval.jsr.groups.GroupsComputer; import org.apache.bval.jsr.xml.AnnotationProxyBuilder; import org.apache.bval.util.AccessStrategy; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.reflect.TypeUtils; +import org.apache.bval.util.reflection.TypeUtils; import org.apache.commons.weaver.privilizer.Privileged; import javax.validation.Constraint; @@ -226,7 +225,7 @@ final class AnnotationConstraintBuilder< garr = null; } - if (ArrayUtils.isEmpty(garr)) { + if (garr == null || garr.length == 0) { garr = GroupsComputer.DEFAULT_GROUP; } constraintValidation.setGroups(garr); Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationProcessor.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationProcessor.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationProcessor.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationProcessor.java Mon Oct 24 21:02:02 2016 @@ -22,8 +22,8 @@ import org.apache.bval.model.Features; import org.apache.bval.model.Meta; import org.apache.bval.model.MetaBean; import org.apache.bval.util.AccessStrategy; +import org.apache.bval.util.ObjectUtils; import org.apache.bval.util.reflection.Reflection; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; @@ -175,13 +175,13 @@ public final class AnnotationProcessor { return false; } AccessStrategy[] strategies = prop.getFeature(Features.Property.REF_CASCADE); - if (ArrayUtils.contains(strategies, access)) { + if (ObjectUtils.arrayContains(strategies, access)) { return false; } if (strategies == null) { strategies = new AccessStrategy[] { access }; } else { - strategies = ArrayUtils.add(strategies, access); + strategies = ObjectUtils.arrayAdd(strategies, access); } prop.putFeature(Features.Property.REF_CASCADE, strategies); return true; Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidationProvider.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidationProvider.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidationProvider.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidationProvider.java Mon Oct 24 21:02:02 2016 @@ -25,7 +25,7 @@ import javax.validation.spi.BootstrapSta import javax.validation.spi.ConfigurationState; import javax.validation.spi.ValidationProvider; -import org.apache.commons.lang3.ClassUtils; +import org.apache.bval.util.reflection.Reflection; /** * Description: Implementation of {@link ValidationProvider} for jsr @@ -81,8 +81,7 @@ public class ApacheValidationProvider im if (validatorFactoryClassname == null) { validatorFactoryClass = ApacheValidatorFactory.class; } else { - validatorFactoryClass = - ClassUtils.getClass(validatorFactoryClassname).asSubclass(ValidatorFactory.class); + validatorFactoryClass = Reflection.toClass(validatorFactoryClassname).asSubclass(ValidatorFactory.class); } } catch (ValidationException ex) { throw ex; Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java Mon Oct 24 21:02:02 2016 @@ -27,14 +27,12 @@ import org.apache.bval.jsr.xml.Annotatio import org.apache.bval.jsr.xml.MetaConstraint; import org.apache.bval.jsr.xml.ValidationMappingParser; import org.apache.bval.util.AccessStrategy; +import org.apache.bval.util.ObjectUtils; +import org.apache.bval.util.StringUtils; import org.apache.bval.util.reflection.Reflection; import org.apache.bval.xml.XMLMetaBeanBuilder; import org.apache.bval.xml.XMLMetaBeanFactory; import org.apache.bval.xml.XMLMetaBeanManager; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.ClassUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.reflect.ConstructorUtils; import org.apache.commons.weaver.privilizer.Privileged; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; @@ -68,6 +66,7 @@ import java.util.concurrent.ConcurrentMa */ @Privilizing(@CallTo(Reflection.class)) public class ApacheValidatorFactory implements ValidatorFactory, Cloneable { + private static volatile ApacheValidatorFactory DEFAULT_FACTORY; private static final ConstraintDefaults DEFAULT_CONSTRAINTS = new ConstraintDefaults(); @@ -350,7 +349,7 @@ public class ApacheValidatorFactory impl return newInstance(type); } try { - final Class cls = ClassUtils.getClass(type.getName() + "Impl"); + final Class cls = Reflection.toClass(type.getName() + "Impl"); if (type.isAssignableFrom(cls)) { @SuppressWarnings("unchecked") T result = (T) newInstance(cls); @@ -490,7 +489,7 @@ public class ApacheValidatorFactory impl } private static Class[] safeArray(Class... array) { - return ArrayUtils.isEmpty(array) ? ArrayUtils.EMPTY_CLASS_ARRAY : ArrayUtils.clone(array); + return array == null || array.length == 0 ? ObjectUtils. EMPTY_CLASS_ARRAY : array.clone(); } /** @@ -513,11 +512,11 @@ public class ApacheValidatorFactory impl @Privileged private F createMetaBeanFactory(final Class cls) { try { - Constructor c = ConstructorUtils.getMatchingAccessibleConstructor(cls, ApacheValidatorFactory.this.getClass()); + Constructor c = Reflection.getDeclaredConstructor(cls, ApacheValidatorFactory.this.getClass()); if (c != null) { return c.newInstance(this); } - c = ConstructorUtils.getMatchingAccessibleConstructor(cls, getClass()); + c = Reflection.getDeclaredConstructor(cls, getClass()); if (c != null) { return c.newInstance(this); } Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/BeanDescriptorImpl.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/BeanDescriptorImpl.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/BeanDescriptorImpl.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/BeanDescriptorImpl.java Mon Oct 24 21:02:02 2016 @@ -33,7 +33,6 @@ import org.apache.bval.model.MetaPropert import org.apache.bval.model.Validation; import org.apache.bval.util.AccessStrategy; import org.apache.bval.util.reflection.Reflection; -import org.apache.commons.lang3.ClassUtils; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; @@ -849,7 +848,7 @@ public class BeanDescriptorImpl extends } } else { annotationProcessor.processAnnotation( - annotation, null, ClassUtils.primitiveToWrapper((Class) access.getJavaType()), + annotation, null, Reflection.primitiveToWrapper((Class) access.getJavaType()), access, validations, true); } } Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ClassValidator.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ClassValidator.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ClassValidator.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ClassValidator.java Mon Oct 24 21:02:02 2016 @@ -63,11 +63,9 @@ import org.apache.bval.model.MetaPropert import org.apache.bval.model.Validation; import org.apache.bval.util.AccessStrategy; import org.apache.bval.util.ValidationHelper; +import org.apache.bval.util.ObjectUtils; import org.apache.bval.util.reflection.Reflection; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.ClassUtils; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.reflect.TypeUtils; +import org.apache.bval.util.reflection.TypeUtils; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; @@ -296,7 +294,7 @@ public class ClassValidator implements C return newInstance(type); } try { - final Class cls = ClassUtils.getClass(type.getName() + "Impl"); + final Class cls = Reflection.toClass(type.getName() + "Impl"); if (type.isAssignableFrom(cls)) { @SuppressWarnings("unchecked") final Class implClass = (Class) cls; @@ -1155,7 +1153,9 @@ public class ClassValidator implements C if (!cascade) { //TCK doesn't care what type a property is if there are no constraints to validate: FeaturesCapable meta = prop == null ? context.getMetaBean() : prop; - if (ArrayUtils.isEmpty(meta.getValidations())) { + + Validation[] validations = meta.getValidations(); + if (validations == null || validations.length == 0) { return Collections.> emptySet(); } } Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java Mon Oct 24 21:02:02 2016 @@ -17,7 +17,7 @@ package org.apache.bval.jsr; import org.apache.bval.util.reflection.Reflection; -import org.apache.commons.lang3.reflect.TypeUtils; +import org.apache.bval.util.reflection.TypeUtils; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java Mon Oct 24 21:02:02 2016 @@ -31,8 +31,8 @@ import java.util.logging.Logger; import javax.validation.ConstraintValidator; +import org.apache.bval.util.StringUtils; import org.apache.bval.util.reflection.Reflection; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; @@ -104,7 +104,7 @@ public class ConstraintDefaults { final List> classes = new LinkedList>(); for (String className : StringUtils.split((String) entry.getValue(), ',')) { try { - classes.add(Reflection.getClass(classloader, className.trim())); + classes.add(Reflection.toClass(className.trim(), classloader)); } catch (Exception e) { log.log(Level.SEVERE, String.format("Cannot find class %s", className), e); } Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDescriptorImpl.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDescriptorImpl.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDescriptorImpl.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDescriptorImpl.java Mon Oct 24 21:02:02 2016 @@ -22,9 +22,6 @@ import javax.validation.ConstraintTarget import javax.validation.Payload; import javax.validation.metadata.ConstraintDescriptor; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - import java.io.Serializable; import java.lang.annotation.Annotation; import java.util.List; @@ -158,47 +155,48 @@ public class ConstraintDescriptorImpl that = (ConstraintDescriptorImpl) o; + + if (reportAsSingleViolation != that.reportAsSingleViolation) { return false; } + if (annotation != null ? !annotation.equals(that.annotation) : that.annotation != null) { return false; } + if (groups != null ? !groups.equals(that.groups) : that.groups != null) { return false; } + if (payload != null ? !payload.equals(that.payload) : that.payload != null) { return false; } + if (constraintValidatorClasses != null ? !constraintValidatorClasses.equals(that.constraintValidatorClasses) : that.constraintValidatorClasses != null) { return false; } + if (attributes != null ? !attributes.equals(that.attributes) : that.attributes != null) { return false; } + if (composingConstraints != null ? !composingConstraints.equals(that.composingConstraints) : that.composingConstraints != null) { return false; } + if (validationAppliesTo != that.validationAppliesTo) { return false; } + return template != null ? template.equals(that.template) : that.template == null; + } + @Override public int hashCode() { return hashCode; } + /** + * generated hashCode on all fields except hashCode + */ private int computeHashCode() { - return new HashCodeBuilder(1, 31) - .append(annotation.annotationType()) - .append(groups) - .append(payload) - .append(constraintValidatorClasses) - .append(attributes) - .append(composingConstraints) - .append(reportAsSingleViolation) - .append(validationAppliesTo) - .append(template) - .build(); + int result = annotation != null ? annotation.hashCode() : 0; + result = 31 * result + (groups != null ? groups.hashCode() : 0); + result = 31 * result + (payload != null ? payload.hashCode() : 0); + result = 31 * result + (constraintValidatorClasses != null ? constraintValidatorClasses.hashCode() : 0); + result = 31 * result + (attributes != null ? attributes.hashCode() : 0); + result = 31 * result + (composingConstraints != null ? composingConstraints.hashCode() : 0); + result = 31 * result + (reportAsSingleViolation ? 1 : 0); + result = 31 * result + (validationAppliesTo != null ? validationAppliesTo.hashCode() : 0); + result = 31 * result + (template != null ? template.hashCode() : 0); + return result; } } Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidation.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidation.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidation.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidation.java Mon Oct 24 21:02:02 2016 @@ -24,10 +24,10 @@ import org.apache.bval.model.Validation; import org.apache.bval.model.ValidationContext; import org.apache.bval.model.ValidationListener; import org.apache.bval.util.AccessStrategy; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.ClassUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.reflect.TypeUtils; +import org.apache.bval.util.ObjectUtils; +import org.apache.bval.util.StringUtils; +import org.apache.bval.util.reflection.Reflection; +import org.apache.bval.util.reflection.TypeUtils; import javax.validation.ConstraintDefinitionException; import javax.validation.ConstraintTarget; @@ -84,7 +84,7 @@ public class ConstraintValidation owner, AccessStrategy access, boolean reportFromComposite, ConstraintTarget target) { this.attributes = new HashMap(); - this.validatorClasses = ArrayUtils.clone(validatorClasses); + this.validatorClasses = validatorClasses != null ? validatorClasses.clone() : null; this.annotation = annotation; this.owner = owner; this.access = access; @@ -237,7 +237,7 @@ public class ConstraintValidation ConstraintValidator getConstraintValidator( ConstraintValidatorFactory factory, A annotation, Class>[] constraintClasses, Class owner, AccessStrategy access) { - if (ArrayUtils.isNotEmpty(constraintClasses)) { + if (ObjectUtils.isNotEmpty(constraintClasses)) { final Type type = determineTargetedType(owner, access); /** @@ -413,7 +413,7 @@ public class ConstraintValidation ? ClassUtils.primitiveToWrapper((Class) type) : type; + return type instanceof Class ? Reflection.primitiveToWrapper((Class) type) : type; } /** Modified: bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java URL: http://svn.apache.org/viewvc/bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java?rev=1766445&r1=1766444&r2=1766445&view=diff ============================================================================== --- bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java (original) +++ bval/trunk/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java Mon Oct 24 21:02:02 2016 @@ -18,12 +18,12 @@ package org.apache.bval.jsr; import org.apache.bval.el.MessageEvaluator; import org.apache.bval.util.reflection.Reflection; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; import javax.validation.MessageInterpolator; +import java.util.Arrays; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; @@ -231,7 +231,7 @@ public class DefaultMessageInterpolator Object variable = annotationParameters.get(removeCurlyBrace(parameter)); if (variable != null) { if (variable.getClass().isArray()) { - resolvedParameterValue = ArrayUtils.toString(variable); + resolvedParameterValue = Arrays.toString((Object[]) variable); } else { resolvedParameterValue = variable.toString(); }