// code originates from TypeUtilsTest.java in jtype (http://code.google.com/p/jtype/) and has been modified to suite
// the HV requirements and code style
/*
* Copyright 2009 IIZUKA Software Technologies Ltd
*
* Licensed 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.hibernate.validator.test.internal.util;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.io.Serializable;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor;
import org.hibernate.validator.internal.util.TypeHelper;
import org.hibernate.validator.testutil.TestForIssue;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* Tests {@code TypeUtils}.
*
* @author Mark Hobson
* @see org.hibernate.validator.internal.util.TypeHelper
*/
public class TypeHelperTest {
private static final Type[] DEFAULT_UPPER_BOUNDS = new Type[] { Object.class };
private static final Type[] DEFAULT_LOWER_BOUNDS = new Type[0];
private GenericDeclaration declaration;
private static class IntegerArrayList extends ArrayList<Integer> {
// simple subclass to fix generics
}
private static class IntegerKeyHashMap<V> extends HashMap<Integer, V> {
// simple subclass to fix generics
}
private static class StringsByIntegerHashMap extends IntegerKeyHashMap<String> {
// simple subclass to fix generics
}
@BeforeClass
public void beforeClass() throws Exception {
declaration = getClass().getConstructor();
}
@Test
public void isAssignableWithPrimitiveDouble() {
assertAssignable( Double.TYPE, Double.TYPE );
assertAsymmetricallyAssignable( Double.TYPE, Float.TYPE );
assertAsymmetricallyAssignable( Double.TYPE, Long.TYPE );
assertAsymmetricallyAssignable( Double.TYPE, Integer.TYPE );
assertAsymmetricallyAssignable( Double.TYPE, Character.TYPE );
assertAsymmetricallyAssignable( Double.TYPE, Short.TYPE );
assertAsymmetricallyAssignable( Double.TYPE, Byte.TYPE );
}
@Test
public void isAssignableWithPrimitiveFloat() {
assertAssignable( Float.TYPE, Float.TYPE );
assertAsymmetricallyAssignable( Float.TYPE, Long.TYPE );
assertAsymmetricallyAssignable( Float.TYPE, Integer.TYPE );
assertAsymmetricallyAssignable( Float.TYPE, Character.TYPE );
assertAsymmetricallyAssignable( Float.TYPE, Short.TYPE );
assertAsymmetricallyAssignable( Float.TYPE, Byte.TYPE );
}
@Test
public void isAssignableWithPrimitiveLong() {
assertAssignable( Long.TYPE, Long.TYPE );
assertAsymmetricallyAssignable( Long.TYPE, Integer.TYPE );
assertAsymmetricallyAssignable( Long.TYPE, Character.TYPE );
assertAsymmetricallyAssignable( Long.TYPE, Short.TYPE );
assertAsymmetricallyAssignable( Long.TYPE, Byte.TYPE );
}
@Test
public void isAssignableWithPrimitiveInt() {
assertAssignable( Integer.TYPE, Integer.TYPE );
assertAsymmetricallyAssignable( Integer.TYPE, Character.TYPE );
assertAsymmetricallyAssignable( Integer.TYPE, Short.TYPE );
assertAsymmetricallyAssignable( Integer.TYPE, Byte.TYPE );
}
@Test
public void isAssignableWithPrimitiveShort() {
assertAssignable( Short.TYPE, Short.TYPE );
assertAsymmetricallyAssignable( Short.TYPE, Byte.TYPE );
}
// JLS 4.10.2 Subtyping among Class and Interface Types
/**
* The direct superclasses of C.
*/
@Test
public void isAssignableWithDirectSuperclassFromParameterizedType() {
assertAssignable( AbstractList.class, TypeHelper.parameterizedType( ArrayList.class, Integer.class ) );
}
/**
* The direct superinterfaces of C.
*/
@Test
public void isAssignableWithDirectSuperinterfaceFromParameterizedType() {
assertAssignable( Collection.class, TypeHelper.parameterizedType( List.class, Integer.class ) );
}
/**
* The type Object, if C is an interface type with no direct superinterfaces.
*/
@Test
public void isAssignableWithObjectFromInterface() {
assertAssignable( Object.class, Iterable.class );
}
/**
* The raw type C.
*/
@Test
public void isAssignableWithRawTypeFromParameterizedType() {
assertAssignable( List.class, TypeHelper.parameterizedType( List.class, Integer.class ) );
}
// TODO: finish 4.10.2
// JLS 4.10.3 Subtyping among Array Types
/**
* If S and T are both reference types, then S[] >1 T[] iff S >1 T.
*/
@Test
public void isAssignableWithArrayClassFromDirectSubtypeArrayClass() {
assertAsymmetricallyAssignable( Number[].class, Integer[].class );
}
@Test
public void isAssignableWithArrayClassFromIndirectSubtypeArrayClass() {
assertAsymmetricallyAssignable( Object[].class, Integer[].class );
}
@Test
public void isAssignableWithArrayClassFromGenericArrayType() {
assertAssignable( Integer[].class, TypeHelper.genericArrayType( Integer.class ) );
}
@Test
public void isAssignableWithArrayClassFromDirectSubtypeGenericArrayType() {
assertAsymmetricallyAssignable( Number[].class, TypeHelper.genericArrayType( Integer.class ) );
}
@Test
public void isAssignableWithArrayClassFromIndirectSubtypeGenericArrayType() {
assertAsymmetricallyAssignable( Object[].class, TypeHelper.genericArrayType( Integer.class ) );
}
@Test
public void isAssignableWithGenericArrayTypeFromDirectSubtypeGenericArrayType() {
assertAsymmetricallyAssignable(
TypeHelper.genericArrayType( Number.class ),
TypeHelper.genericArrayType( Integer.class )
);
}
@Test
public void isAssignableWithGenericArrayTypeFromIndirectSubtypeGenericArrayType() {
assertAsymmetricallyAssignable(
TypeHelper.genericArrayType( Object.class ),
TypeHelper.genericArrayType( Integer.class )
);
}
@Test
public void isAssignableWithGenericArrayTypeFromArrayClass() {
assertAssignable( TypeHelper.genericArrayType( Integer.class ), Integer[].class );
}
@Test
public void isAssignableWithGenericArrayTypeFromDirectSubtypeArrayClass() {
assertAsymmetricallyAssignable( TypeHelper.genericArrayType( Number.class ), Integer[].class );
}
@Test
public void isAssignableWithGenericArrayTypeFromIndirectSubtypeArrayClass() {
assertAsymmetricallyAssignable( TypeHelper.genericArrayType( Object.class ), Integer[].class );
}
/**
* Object >1 Object[].
*/
@Test
public void isAssignableWithObjectFromObjectArrayClass() {
assertAsymmetricallyAssignable( Object.class, Object[].class );
}
@Test
public void isAssignableWithObjectFromArrayClass() {
assertAsymmetricallyAssignable( Object.class, Integer[].class );
}
@Test
public void isAssignableWithObjectFromObjectGenericArrayType() {
assertAsymmetricallyAssignable( Object.class, TypeHelper.genericArrayType( Object.class ) );
}
@Test
public void isAssignableWithObjectFromGenericArrayType() {
assertAsymmetricallyAssignable( Object.class, TypeHelper.genericArrayType( Integer.class ) );
}
/**
* Cloneable >1 Object[].
*/
@Test
public void isAssignableWithCloneableFromObjectArrayClass() {
assertAsymmetricallyAssignable( Cloneable.class, Object[].class );
}
@Test
public void isAssignableWithCloneableFromArrayClass() {
assertAsymmetricallyAssignable( Cloneable.class, Integer[].class );
}
@Test
public void isAssignableWithCloneableFromObjectGenericArrayType() {
assertAsymmetricallyAssignable( Cloneable.class, TypeHelper.genericArrayType( Object.class ) );
}
@Test
public void isAssignableWithCloneableFromGenericArrayType() {
assertAsymmetricallyAssignable( Cloneable.class, TypeHelper.genericArrayType( Integer.class ) );
}
/**
* java.io.Serializable >1 Object[].
*/
@Test
public void isAssignableWithSerializableFromObjectArrayClass() {
assertAsymmetricallyAssignable( Serializable.class, Object[].class );
}
@Test
public void isAssignableWithSerializableFromArrayClass() {
assertAsymmetricallyAssignable( Serializable.class, Integer[].class );
}
@Test
public void isAssignableWithSerializableFromObjectGenericArrayType() {
assertAsymmetricallyAssignable( Serializable.class, TypeHelper.genericArrayType( Object.class ) );
}
@Test
public void isAssignableWithSerializableFromGenericArrayType() {
assertAsymmetricallyAssignable( Serializable.class, TypeHelper.genericArrayType( Integer.class ) );
}
/**
* If p is a primitive type, then Object >1 p[].
*/
@Test
public void isAssignableWithObjectFromPrimitiveArray() {
assertAsymmetricallyAssignable( Object.class, int[].class );
}
/**
* If p is a primitive type, then Cloneable >1 p[].
*/
@Test
public void isAssignableWithCloneableFromPrimitiveArray() {
assertAsymmetricallyAssignable( Cloneable.class, int[].class );
}
/**
* If p is a primitive type, then java.io.Serializable >1 p[].
*/
@Test
public void isAssignableWithSerializableFromPrimitiveArray() {
assertAsymmetricallyAssignable( Serializable.class, int[].class );
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void isAssignableWithNullSupertype() {
assertAssignable( null, Integer.class );
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void isAssignableWithNullType() {
assertAssignable( Integer.class, null );
}
/**
* Tests that classes are assignable to their direct superclasses.
*
* {@literal Number <: Integer}
*/
@Test
public void isAssignableWithClassFromDirectSubclass() {
assertAsymmetricallyAssignable( Number.class, Integer.class );
}
/**
* Tests that classes are assignable to their indirect superclasses.
*
* {@literal Object <: Integer}
*/
@Test
public void isAssignableWithClassFromIndirectSubclass() {
assertAsymmetricallyAssignable( Object.class, Integer.class );
}
/**
* Tests that parameterized types are assignable to their raw types.
*
* {@literal List <: List<Integer>}
*/
@Test
public void isAssignableWithClassFromParameterizedType() {
assertAsymmetricallyAssignable( List.class, TypeHelper.parameterizedType( List.class, Integer.class ) );
}
/**
* Tests that parameterized types are assignable if their raw types are directly assignable.
*
* {@literal Collection<Integer> <: List<Integer>}
*/
@Test
public void isAssignableWithDirectlyAssignableParameterizedTypeRawTypes() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType( Collection.class, Integer.class ),
TypeHelper.parameterizedType( List.class, Integer.class )
);
}
/**
* Tests that parameterized types are assignable if their raw types are indirectly assignable.
*
* {@literal Collection<Integer> <: ArrayList<Integer>}
*/
@Test
public void isAssignableWithIndirectlyAssignableParameterizedTypeRawTypes() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType( Collection.class, Integer.class ),
TypeHelper.parameterizedType( ArrayList.class, Integer.class )
);
}
/**
* Tests that parameterized types are not assignable if their raw types are not assignable.
*
* {@literal List<Integer> !<: Set<Integer>}
*/
@Test
public void isAssignableWithUnassignableParameterizedTypeRawTypes() {
assertUnassignable(
TypeHelper.parameterizedType( List.class, Integer.class ),
TypeHelper.parameterizedType( Set.class, Integer.class )
);
assertUnassignable(
TypeHelper.parameterizedType( Set.class, Integer.class ),
TypeHelper.parameterizedType( List.class, Integer.class )
);
}
/**
* Tests that parameterized types are not assignable even if their type arguments are assignable.
*
* {@literal List<Number> !<: List<Integer>}
*/
@Test
public void isAssignableWithAssignableParameterizedTypeArguments() {
assertUnassignable(
TypeHelper.parameterizedType( List.class, Number.class ),
TypeHelper.parameterizedType( List.class, Integer.class )
);
assertUnassignable(
TypeHelper.parameterizedType( List.class, Integer.class ),
TypeHelper.parameterizedType( List.class, Number.class )
);
}
/**
* Tests that parameterized type arguments are assignable to wildcard types.
*
* {@literal List<?> <: List<Integer>}
*/
@Test
public void isAssignableWithWildcardParameterizedTypeFromParameterizedType() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType( List.class, wildcardType() ),
TypeHelper.parameterizedType( List.class, Integer.class )
);
}
/**
* Tests that parameterized type upper bounded wildcard type arguments are assignable to wildcard types.
*
* {@literal List<?> <: List<? extends Number>}
*/
@Test
public void isAssignableWithWildcardParameterizedTypeFromUpperBoundedWildcardParameterizedType() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType( List.class, wildcardType() ),
TypeHelper.parameterizedType(
List.class,
wildcardTypeUpperBounded( new Type[] { Number.class } )
)
);
}
/**
* Tests that parameterized type arguments are assignable to wildcard types on their upper bound.
*
* {@literal List<? extends Number> <: List<Number>}
*/
@Test
public void isAssignableWithUpperBoundedWildcardParameterizedTypeFromParameterizedType() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType(
List.class,
wildcardTypeLowerBounded( new Type[] { Number.class } )
),
TypeHelper.parameterizedType( List.class, Number.class )
);
}
/**
* Tests that parameterized type arguments are assignable to wildcard types within their upper bound.
*
* {@literal List<? extends Number> <: List<Integer>}
*/
@Test
public void isAssignableWithUpperBoundedWildcardParameterizedTypeFromInBoundsParameterizedType() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType(
List.class,
wildcardTypeUpperBounded( new Type[] { Number.class } )
),
TypeHelper.parameterizedType( List.class, Integer.class )
);
}
/**
* Tests that parameterized type arguments are not assignable to wildcard types outside of their upper bound.
*
* {@literal List<? extends Number> !<: List<Object>}
*/
@Test
public void isAssignableWithUpperBoundedWildcardParameterizedTypeFromOutOfBoundsParameterizedType() {
assertUnassignable(
TypeHelper.parameterizedType(
List.class,
wildcardTypeUpperBounded( new Type[] { Number.class } )
),
TypeHelper.parameterizedType( List.class, Object.class )
);
assertUnassignable(
TypeHelper.parameterizedType( List.class, Object.class ),
TypeHelper.parameterizedType(
List.class,
wildcardTypeLowerBounded( new Type[] { Number.class } )
)
);
}
/**
* {@literal List<? extends Number> <: List<? extends Integer>}
*/
@Test
public void isAssignableWithUpperBoundedWildcardParameterizedTypeFromInBoundsUpperBoundedWildcardParameterizedType() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType(
List.class,
wildcardTypeLowerBounded( new Type[] { Number.class } )
),
TypeHelper.parameterizedType(
List.class,
wildcardTypeLowerBounded( new Type[] { Integer.class } )
)
);
}
/**
* Tests that parameterized type arguments are assignable to wildcard types on their lower bound.
*
* {@literal List<? super Number> <: List<Number>}
*/
@Test
public void isAssignableWithLowerBoundedWildcardParameterizedTypeFromParameterizedType() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType(
List.class,
wildcardTypeLowerBounded( new Type[] { Number.class } )
),
TypeHelper.parameterizedType(
List.class,
Number.class
)
);
}
/**
* Tests that parameterized type arguments are assignable to wildcard types within their lower bound.
*
* {@literal List<? super Number> <: List<Object>}
*/
@Test
public void isAssignableWithLowerBoundedWildcardParameterizedTypeFromInBoundsParameterizedType() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType(
List.class,
wildcardTypeLowerBounded( new Type[] { Number.class } )
),
TypeHelper.parameterizedType(
List.class,
Object.class
)
);
}
/**
* Tests that parameterized type arguments are assignable to wildcard types outside of their lower bound.
*
* {@literal List<? super Number> !<: List<Integer>}
*/
@Test
public void isAssignableWithLowerBoundedWildcardParameterizedTypeFromOutOfBoundsParameterizedType() {
assertUnassignable(
TypeHelper.parameterizedType(
List.class,
wildcardTypeLowerBounded( new Type[] { Number.class } )
),
TypeHelper.parameterizedType(
List.class,
Integer.class
)
);
assertUnassignable(
TypeHelper.parameterizedType(
List.class,
Integer.class
),
TypeHelper.parameterizedType(
List.class,
wildcardTypeLowerBounded( new Type[] { Number.class } )
)
);
}
/**
* Tests that classes are assignable to parameterized supertypes.
*
* {@literal List<Integer> <: IntegerArrayList}
*/
@Test
public void isAssignableWithParameterizedTypeFromClassWithActualTypeArguments() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType( List.class, Integer.class ),
IntegerArrayList.class
);
}
@Test
public void isAssignableWithUnboundedWildcardParameterizedTypeFromClass() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType( List.class, wildcardType() ),
ArrayList.class
);
}
@Test
public void isAssignableWithUnboundedWildcardParameterizedTypeFromClassWithActualTypeArguments() {
assertAsymmetricallyAssignable(
TypeHelper.parameterizedType(
Map.class,
wildcardType(),
wildcardType()
),
StringsByIntegerHashMap.class
);
}
/**
* Tests that unbounded type variables are assignable to Object.
*
* {@literal Object <: T}
*/
@Test
public void isAssignableWithObjectFromTypeVariableWithNoBounds() {
assertAssignable( Object.class, typeVariable( declaration, "T" ) );
}
/**
* Tests that type variables with a single bound are assignable to their bound.
*
* {@literal Number <: T extends Number}
*/
@Test
public void isAssignableWithBoundFromTypeVariableWithBound() {
assertAssignable( Object.class, typeVariable( declaration, "T", Number.class ) );
}
/**
* Tests that type variables with a single bound are not assignable to subtypes of their bound.
*
* {@literal Integer !<: T extends Number}
*/
@Test
public void isAssignableWithTypeOutsideOfBoundFromTypeVariableWithBound() {
assertUnassignable( Integer.class, typeVariable( declaration, "T", Number.class ) );
}
/**
* Tests that type variables with a single bound are assignable to supertypes of their bound.
*
* {@literal Number <: T extends Integer}
*/
@Test
public void isAssignableWithTypeInsideOfBoundFromTypeVariableWithBound() {
assertAssignable( Number.class, typeVariable( declaration, "T", Integer.class ) );
}
/**
* Tests that type variables with multiple bounds are assignable to their bounds.
*
* {@literal Number, Collection <: T extends Number & Collection}
*/
@Test
public void isAssignableWithBoundsFromTypeVariableWithBounds() {
TypeVariable<?> type = typeVariable( declaration, "T", Number.class, Collection.class );
assertAssignable( Number.class, type );
assertAssignable( Collection.class, type );
}
/**
* Tests that type variables with multiple bounds are not assignable to supertypes of their bounds.
*
* {@literal Integer, Thread !<: T extends Number & Runnable}
*/
@Test
public void isAssignableWithTypeOutsideOfBoundsFromTypeVariableWithBounds() {
TypeVariable<?> type = typeVariable( declaration, "T", Number.class, Collection.class );
assertUnassignable( Integer.class, type );
assertUnassignable( List.class, type );
}
/**
* Tests that type variables with multiple bounds are assignable to subtypes of their bounds.
*
* {@literal Number, Collection <: T extends Integer & List}
*/
@Test
public void isAssignableWithTypeInsideOfBoundsFromTypeVariableWithBounds() {
TypeVariable<?> type = typeVariable( declaration, "T", Integer.class, List.class );
assertAssignable( Number.class, type );
assertAssignable( Collection.class, type );
}
/**
* Tests that unbounded wildcards are assignable to Object.
*
* {@literal Object <: ?}
*/
@Test
public void isAssignableWithObjectFromUnboundedWildcardType() {
assertAssignable( Object.class, wildcardType() );
}
/**
* Tests that upper bounded wildcards are assignable to their upper bound.
*
* {@literal Number <: ? extends Number}
*/
@Test
public void isAssignableWithBoundFromUpperBoundedWildcardType() {
assertAssignable( Number.class, wildcardTypeUpperBounded( new Type[] { Number.class } ) );
}
/**
* Tests that upper bounded wildcards are assignable to supertypes of their upper bound.
*
* {@literal Number <: ? extends Integer}
*/
@Test
public void isAssignableWithBoundSupertypeFromUpperBoundedWildcardType() {
assertAssignable( Number.class, wildcardTypeUpperBounded( new Type[] { Integer.class } ) );
}
/**
* Tests that upper bounded wildcards are not assignable to subtypes of their upper bound.
*
* {@literal Integer !<: ? extends Number}
*/
@Test
public void isAssignableWithBoundSubtypeFromUpperBoundedWildcardType() {
assertUnassignable( Integer.class, wildcardTypeUpperBounded( new Type[] { Number.class } ) );
}
/**
* Tests that lower bounded wildcards are assignable to Object.
*
* {@literal Object <: ? super Number}
*/
@Test
public void isAssignableWithObjectFromLowerBoundedWildcardType() {
assertAssignable( Object.class, wildcardTypeLowerBounded( new Type[] { Number.class } ) );
}
/**
* Tests that lower bounded wildcards are not assignable to their lower bound.
*
* {@literal Number !<: ? super Number}
*/
@Test
public void isAssignableWithBoundFromLowerBoundedWildcardType() {
assertUnassignable( Number.class, wildcardTypeLowerBounded( new Type[] { Number.class } ) );
}
/**
* Tests that lower bounded wildcards are not assignable to supertypes of their lower bound.
*
* {@literal Number !<: ? super Integer}
*/
@Test
public void isAssignableWithBoundSupertypeFromLowerBoundedWildcardType() {
assertUnassignable( Number.class, wildcardTypeLowerBounded( new Type[] { Integer.class } ) );
}
/**
* Tests that upper bounded wildcards are not assignable to subtypes of their upper bound.
*
* {@literal Integer <: ? super Number}
*/
@Test
public void isAssignableWithBoundSubtypeFromLowerBoundedWildcardType() {
assertUnassignable( Integer.class, wildcardTypeLowerBounded( new Type[] { Number.class } ) );
}
@Test(expectedExceptions = NullPointerException.class)
public void isInstanceWithNullType() {
TypeHelper.isInstance( null, 123 );
}
@Test
public void isInstanceWithClass() {
assertTrue( TypeHelper.isInstance( Integer.class, 123 ) );
}
@Test
public void isInstanceWithClassAndSubclass() {
assertTrue( TypeHelper.isInstance( Number.class, 123 ) );
}
@Test
public void isInstanceWithClassAndSuperclass() {
assertFalse( TypeHelper.isInstance( Number.class, new Object() ) );
}
@Test
public void isInstanceWithClassAndDisjointClass() {
assertFalse( TypeHelper.isInstance( Integer.class, 123L ) );
}
@Test
public void isInstanceWithClassAndNull() {
assertFalse( TypeHelper.isInstance( Integer.class, null ) );
}
@Test
public void isInstanceWithClassArray() {
assertTrue( TypeHelper.isInstance( Integer[].class, new Integer[0] ) );
}
@Test
public void isInstanceWithClassArrayAndSubclassArray() {
assertTrue( TypeHelper.isInstance( Number[].class, new Integer[0] ) );
}
@Test
public void isInstanceWithClassArrayAndSuperclassArray() {
assertFalse( TypeHelper.isInstance( Number[].class, new Object[0] ) );
}
@Test
public void isInstanceWithClassArrayAndDisjointClass() {
assertFalse( TypeHelper.isInstance( Integer[].class, new Long[0] ) );
}
@Test
public void isArrayWithNull() {
assertFalse( TypeHelper.isArray( null ) );
}
@Test
public void isArrayWithClass() {
assertFalse( TypeHelper.isArray( Integer.class ) );
}
@Test
public void isArrayWithClassArray() {
assertTrue( TypeHelper.isArray( Integer[].class ) );
}
@Test
public void isArrayWithGenericArrayType() {
assertTrue( TypeHelper.isArray( TypeHelper.genericArrayType( Integer.class ) ) );
}
@Test
public void isArrayWithParameterizedType() {
assertFalse( TypeHelper.isArray( TypeHelper.parameterizedType( List.class, Integer.class ) ) );
}
@Test
public void testTypeDiscovery() {
List<ConstraintValidatorDescriptor<Positive>> validatorDescriptors = new ArrayList<>();
validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( PositiveConstraintValidator.class ) );
Map<Type, ConstraintValidatorDescriptor<Positive>> validatorsTypes = TypeHelper
.getValidatorTypes( Positive.class, validatorDescriptors );
assertEquals( validatorsTypes.get( Integer.class ).getValidatorClass(), PositiveConstraintValidator.class );
assertNull( validatorsTypes.get( String.class ) );
}
@Test
@TestForIssue(jiraKey = "HV-1032")
public void testTypeHelperDoesntGoIntoInfiniteLoop() {
Type parentEntityType = ChildEntity.class.getGenericSuperclass();
ParameterizedType childEntityType = TypeHelper.parameterizedType( ChildEntity.class, ChildEntity.class.getTypeParameters() );
assertTrue( TypeHelper.isAssignable( parentEntityType, childEntityType ) );
}
private static void assertAsymmetricallyAssignable(Type supertype, Type type) {
assertAssignable( supertype, type );
assertUnassignable( type, supertype );
}
private static void assertAssignable(Type supertype, Type type) {
assertTrue( TypeHelper.isAssignable( supertype, type ), "Expected " + type + " assignable to " + supertype );
}
private static void assertUnassignable(Type supertype, Type type) {
assertFalse(
TypeHelper.isAssignable( supertype, type ), "Expected " + type + " not assignable to " + supertype
);
}
private static WildcardType wildcardTypeUpperBounded(final Type[] upperBounds) {
return wildcardType( upperBounds, DEFAULT_LOWER_BOUNDS );
}
private static WildcardType wildcardTypeLowerBounded(final Type[] lowerBounds) {
return wildcardType( DEFAULT_UPPER_BOUNDS, lowerBounds );
}
private static WildcardType wildcardType() {
return wildcardType( DEFAULT_UPPER_BOUNDS, DEFAULT_LOWER_BOUNDS );
}
private static WildcardType wildcardType(final Type[] upperBounds, final Type[] lowerBounds) {
return new WildcardType() {
@Override
public Type[] getUpperBounds() {
return upperBounds;
}
@Override
public Type[] getLowerBounds() {
return lowerBounds;
}
};
}
private static TypeVariable<GenericDeclaration> typeVariable(final GenericDeclaration declaration,
final String name,
final Type... bounds) {
Class<?>[] interfaces = { TypeVariable.class };
// HV-871 Implementing TypeVariable via a dynamic proxy to ensure this code can be compiled with Java 7 and 8;
// New methods have been added to the TypeVariable interface in Java 8; To ensure compatibility with Java 7, we
// don't directly implement these as they use types which themselves have been added in Java 8
@SuppressWarnings("unchecked")
TypeVariable<GenericDeclaration> typeVariable = (TypeVariable<GenericDeclaration>) Proxy.newProxyInstance(
TypeHelperTest.class.getClassLoader(),
interfaces,
new TypeVariableImpl( bounds, declaration, name )
);
return typeVariable;
}
private static class TypeVariableImpl implements InvocationHandler {
private final Type[] bounds;
GenericDeclaration declaration;
String name;
public TypeVariableImpl(Type[] bounds, GenericDeclaration declaration, String name) {
this.bounds = bounds;
this.declaration = declaration;
this.name = name;
}
public Type[] getBounds() {
if ( bounds == null || bounds.length == 0 ) {
return new Type[] { Object.class };
}
return bounds;
}
public GenericDeclaration getGenericDeclaration() {
return declaration;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "TypeVariableImpl [bounds=" + Arrays.toString( bounds )
+ ", declaration=" + declaration + ", name=" + name + "]";
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ( "getBounds".equals( method.getName() ) ) {
return getBounds();
}
else if ( "getGenericDeclaration".equals( method.getName() ) ) {
return getGenericDeclaration();
}
else if ( "getName".equals( method.getName() ) ) {
return getName();
}
else if ( "toString".equals( method.getName() ) ) {
return toString();
}
else {
throw new UnsupportedOperationException( "Method " + method + " is not implemented." );
}
}
}
}