/*
* Copyright (C) 2011 The Guava Authors
*
* 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 com.google.common.reflect;
import static org.junit.contrib.truth.Truth.ASSERT;
import com.google.common.testing.EqualsTester;
import com.google.common.testing.NullPointerTester;
import com.google.common.testing.NullPointerTester.Visibility;
import com.google.common.testing.SerializableTester;
import junit.framework.TestCase;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
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.HashMap;
import java.util.List;
import java.util.Map;
/**
* Tests for {@link Types}.
*
* @author Ben Yu
*/
public class TypesTest extends TestCase {
public void testNewParameterizedType_ownerTypeImplied() throws Exception {
ParameterizedType jvmType = (ParameterizedType)
new TypeCapture<Map.Entry<String, Integer>>() {}.capture();
ParameterizedType ourType = Types.newParameterizedType(
Map.Entry.class, String.class, Integer.class);
assertEquals(jvmType, ourType);
assertEquals(Map.class, ourType.getOwnerType());
}
public void testNewParameterizedType() {
ParameterizedType jvmType = (ParameterizedType)
new TypeCapture<HashMap<String, int[][]>>() {}.capture();
ParameterizedType ourType = Types.newParameterizedType(
HashMap.class, String.class, int[][].class);
new EqualsTester()
.addEqualityGroup(jvmType, ourType)
.testEquals();
assertEquals(jvmType.toString(), ourType.toString());
assertEquals(jvmType.hashCode(), ourType.hashCode());
assertEquals(HashMap.class, ourType.getRawType());
ASSERT.that(ourType.getActualTypeArguments())
.hasContentsInOrder(jvmType.getActualTypeArguments());
assertEquals(Arrays.asList(
String.class,
Types.newArrayType(Types.newArrayType(int.class))),
Arrays.asList(ourType.getActualTypeArguments()));
assertEquals(null, ourType.getOwnerType());
}
public void testNewParameterizedType_nonStaticLocalClass() {
class LocalClass<T> {}
Type jvmType = new LocalClass<String>() {}.getClass().getGenericSuperclass();
Type ourType = Types.newParameterizedType(LocalClass.class, String.class);
assertEquals(jvmType, ourType);
}
public void testNewParameterizedType_staticLocalClass() {
doTestNewParameterizedType_staticLocalClass();
}
private static void doTestNewParameterizedType_staticLocalClass() {
class LocalClass<T> {}
Type jvmType = new LocalClass<String>() {}.getClass().getGenericSuperclass();
Type ourType = Types.newParameterizedType(LocalClass.class, String.class);
assertEquals(jvmType, ourType);
}
public void testNewParameterizedTypeWithOwner() {
ParameterizedType jvmType = (ParameterizedType)
new TypeCapture<Map.Entry<String, int[][]>>() {}.capture();
ParameterizedType ourType = Types.newParameterizedTypeWithOwner(
Map.class, Map.Entry.class, String.class, int[][].class);
new EqualsTester()
.addEqualityGroup(jvmType, ourType)
.addEqualityGroup(new TypeCapture<Map.Entry<String, String>>() {}.capture())
.addEqualityGroup(new TypeCapture<Map<String, Integer>>() {}.capture())
.testEquals();
assertEquals(jvmType.toString(), ourType.toString());
assertEquals(Map.class, ourType.getOwnerType());
assertEquals(Map.Entry.class, ourType.getRawType());
ASSERT.that(ourType.getActualTypeArguments())
.hasContentsInOrder(jvmType.getActualTypeArguments());
}
public void testNewParameterizedType_serializable() {
SerializableTester.reserializeAndAssert(Types.newParameterizedType(
Map.Entry.class, String.class, Integer.class));
}
public void testNewParameterizedType_ownerMismatch() {
try {
Types.newParameterizedTypeWithOwner(
Number.class, List.class, String.class);
fail();
} catch (IllegalArgumentException expected) {}
}
public void testNewParameterizedType_ownerMissing() {
assertEquals(
Types.newParameterizedType(Map.Entry.class, String.class, Integer.class),
Types.newParameterizedTypeWithOwner(
null, Map.Entry.class, String.class, Integer.class));
}
public void testNewParameterizedType_invalidTypeParameters() {
try {
Types.newParameterizedTypeWithOwner(
Map.class, Map.Entry.class, String.class);
fail();
} catch (IllegalArgumentException expected) {}
}
public void testNewParameterizedType_primitiveTypeParameters() {
try {
Types.newParameterizedTypeWithOwner(
Map.class, Map.Entry.class, int.class, int.class);
fail();
} catch (IllegalArgumentException expected) {}
}
public void testNewArrayType() {
Type jvmType1 = new TypeCapture<List<String>[]>() {}.capture();
GenericArrayType ourType1 = (GenericArrayType) Types.newArrayType(
Types.newParameterizedType(List.class, String.class));
Type jvmType2 = new TypeCapture<List[]>() {}.capture();
Type ourType2 = Types.newArrayType(List.class);
new EqualsTester()
.addEqualityGroup(jvmType1, ourType1)
.addEqualityGroup(jvmType2, ourType2)
.testEquals();
assertEquals(new TypeCapture<List<String>>() {}.capture(),
ourType1.getGenericComponentType());
assertEquals(jvmType1.toString(), ourType1.toString());
assertEquals(jvmType2.toString(), ourType2.toString());
}
public void testNewArrayTypeOfArray() {
Type jvmType = new TypeCapture<int[][]>() {}.capture();
Type ourType = Types.newArrayType(int[].class);
assertEquals(jvmType.toString(), ourType.toString());
new EqualsTester()
.addEqualityGroup(jvmType, ourType)
.testEquals();
}
public void testNewArrayType_primitive() {
Type jvmType = new TypeCapture<int[]>() {}.capture();
Type ourType = Types.newArrayType(int.class);
assertEquals(jvmType.toString(), ourType.toString());
new EqualsTester()
.addEqualityGroup(jvmType, ourType)
.testEquals();
}
public void testNewArrayType_upperBoundedWildcard() {
Type wildcard = Types.subtypeOf(Number.class);
assertEquals(Types.subtypeOf(Number[].class), Types.newArrayType(wildcard));
}
public void testNewArrayType_lowerBoundedWildcard() {
Type wildcard = Types.supertypeOf(Number.class);
assertEquals(Types.supertypeOf(Number[].class), Types.newArrayType(wildcard));
}
public void testNewArrayType_serializable() {
SerializableTester.reserializeAndAssert(
Types.newArrayType(int[].class));
}
private static class WithWildcardType {
@SuppressWarnings("unused")
void withoutBound(List<?> list) {}
@SuppressWarnings("unused")
void withObjectBound(List<? extends Object> list) {}
@SuppressWarnings("unused")
void withUpperBound(List<? extends int[][]> list) {}
@SuppressWarnings("unused")
void withLowerBound(List<? super String[][]> list) {}
static WildcardType getWildcardType(String methodName) throws Exception {
ParameterizedType parameterType = (ParameterizedType)
WithWildcardType.class
.getDeclaredMethod(methodName, List.class)
.getGenericParameterTypes()[0];
return (WildcardType) parameterType.getActualTypeArguments()[0];
}
}
public void testNewWildcardType() throws Exception {
WildcardType noBoundJvmType =
WithWildcardType.getWildcardType("withoutBound");
WildcardType objectBoundJvmType =
WithWildcardType.getWildcardType("withObjectBound");
WildcardType upperBoundJvmType =
WithWildcardType.getWildcardType("withUpperBound");
WildcardType lowerBoundJvmType =
WithWildcardType.getWildcardType("withLowerBound");
WildcardType objectBound =
Types.subtypeOf(Object.class);
WildcardType upperBound =
Types.subtypeOf(int[][].class);
WildcardType lowerBound =
Types.supertypeOf(String[][].class);
assertEqualWildcardType(noBoundJvmType, objectBound);
assertEqualWildcardType(objectBoundJvmType, objectBound);
assertEqualWildcardType(upperBoundJvmType, upperBound);
assertEqualWildcardType(lowerBoundJvmType, lowerBound);
new EqualsTester()
.addEqualityGroup(
noBoundJvmType, objectBoundJvmType, objectBound)
.addEqualityGroup(upperBoundJvmType, upperBound)
.addEqualityGroup(lowerBoundJvmType, lowerBound)
.testEquals();
}
public void testNewWildcardType_primitiveTypeBound() {
try {
Types.subtypeOf(int.class);
fail();
} catch (IllegalArgumentException expected) {}
}
public void testNewWildcardType_serializable() {
SerializableTester.reserializeAndAssert(
Types.supertypeOf(String.class));
SerializableTester.reserializeAndAssert(
Types.subtypeOf(String.class));
SerializableTester.reserializeAndAssert(
Types.subtypeOf(Object.class));
}
private static void assertEqualWildcardType(
WildcardType expected, WildcardType actual) {
assertEquals(expected.toString(), actual.toString());
assertEquals(actual.toString(), expected.hashCode(), actual.hashCode());
ASSERT.that(actual.getLowerBounds())
.hasContentsInOrder(expected.getLowerBounds());
ASSERT.that(actual.getUpperBounds())
.hasContentsInOrder(expected.getUpperBounds());
}
private static class WithTypeVariable {
@SuppressWarnings("unused")
<T> void withoutBound(List<T> list) {}
@SuppressWarnings("unused")
<T extends Object> void withObjectBound(List<T> list) {}
@SuppressWarnings("unused")
<T extends Number & CharSequence> void withUpperBound(List<T> list) {}
static TypeVariable<?> getTypeVariable(String methodName) throws Exception {
ParameterizedType parameterType = (ParameterizedType)
WithTypeVariable.class
.getDeclaredMethod(methodName, List.class)
.getGenericParameterTypes()[0];
return (TypeVariable<?>) parameterType.getActualTypeArguments()[0];
}
}
public void testNewTypeVariable() throws Exception {
TypeVariable<?> noBoundJvmType =
WithTypeVariable.getTypeVariable("withoutBound");
TypeVariable<?> objectBoundJvmType =
WithTypeVariable.getTypeVariable("withObjectBound");
TypeVariable<?> upperBoundJvmType =
WithTypeVariable.getTypeVariable("withUpperBound");
TypeVariable<?> noBound = withBounds(noBoundJvmType);
TypeVariable<?> objectBound = withBounds(objectBoundJvmType, Object.class);
TypeVariable<?> upperBound = withBounds(
upperBoundJvmType, Number.class, CharSequence.class);
assertEqualTypeVariable(noBoundJvmType, noBound);
assertEqualTypeVariable(noBoundJvmType,
withBounds(noBoundJvmType, Object.class));
assertEqualTypeVariable(objectBoundJvmType, objectBound);
assertEqualTypeVariable(upperBoundJvmType, upperBound);
new EqualsTester()
.addEqualityGroup(noBoundJvmType, noBound)
.addEqualityGroup(objectBoundJvmType, objectBound)
.addEqualityGroup(
upperBoundJvmType, upperBound,
withBounds(upperBoundJvmType, CharSequence.class)) // bounds ignored
.testEquals();
}
public void testNewTypeVariable_primitiveTypeBound() {
try {
Types.newTypeVariable(List.class, "E", int.class);
fail();
} catch (IllegalArgumentException expected) {}
}
public void testNewTypeVariable_serializable() throws Exception {
try {
SerializableTester.reserialize(Types.newTypeVariable(List.class, "E"));
fail();
} catch (RuntimeException expected) {}
}
private static <D extends GenericDeclaration> TypeVariable<D> withBounds(
TypeVariable<D> typeVariable, Type... bounds) {
return Types.newTypeVariable(
typeVariable.getGenericDeclaration(), typeVariable.getName(), bounds);
}
private static void assertEqualTypeVariable(
TypeVariable<?> expected, TypeVariable<?> actual) {
assertEquals(expected.toString(), actual.toString());
assertEquals(expected.getName(), actual.getName());
assertEquals(
expected.getGenericDeclaration(), actual.getGenericDeclaration());
assertEquals(actual.toString(), expected.hashCode(), actual.hashCode());
ASSERT.that(actual.getBounds()).hasContentsInOrder(expected.getBounds());
}
/**
* Working with arrays requires defensive code. Verify that we clone the
* type array for both input and output.
*/
public void testNewParameterizedTypeImmutability() {
Type[] typesIn = { String.class, Integer.class };
ParameterizedType parameterizedType
= Types.newParameterizedType(Map.class, typesIn);
typesIn[0] = null;
typesIn[1] = null;
Type[] typesOut = parameterizedType.getActualTypeArguments();
typesOut[0] = null;
typesOut[1] = null;
assertEquals(String.class, parameterizedType.getActualTypeArguments()[0]);
assertEquals(Integer.class, parameterizedType.getActualTypeArguments()[1]);
}
public void testNewParameterizedTypeWithWrongNumberOfTypeArguments() {
try {
Types.newParameterizedType(
Map.class, String.class, Integer.class, Long.class);
fail();
} catch(IllegalArgumentException expected) {}
}
public void testContainsTypeVariable_class() {
assertFalse(Types.containsTypeVariable(String.class));
assertFalse(Types.containsTypeVariable(String[].class));
assertFalse(Types.containsTypeVariable(int[].class));
}
public void testContainsTypeVariable_parameterizedType() {
assertFalse(Types.containsTypeVariable(new TypeCapture<Iterable<String>>() {}.capture()));
}
public void testContainsTypeVariable_wildcardType() {
assertFalse(Types.containsTypeVariable(
new TypeCapture<Iterable<? extends String>>() {}.capture()));
assertFalse(Types.containsTypeVariable(
new TypeCapture<Iterable<? super String>>() {}.capture()));
}
public void testContainsTypeVariable_genericArrayType() {
assertFalse(Types.containsTypeVariable(
new TypeCapture<Iterable<? extends String>[]>() {}.capture()));
}
public <T> void testContainsTypeVariable_withTypeVariable() {
assertTrue(Types.containsTypeVariable(new TypeCapture<T>() {}.capture()));
assertTrue(Types.containsTypeVariable(new TypeCapture<T[]>() {}.capture()));
assertTrue(Types.containsTypeVariable(new TypeCapture<Iterable<T>>() {}.capture()));
assertTrue(Types.containsTypeVariable(new TypeCapture<Map<String, T>>() {}.capture()));
assertTrue(Types.containsTypeVariable(
new TypeCapture<Map<String, ? extends T>>() {}.capture()));
assertTrue(Types.containsTypeVariable(
new TypeCapture<Map<String, ? super T[]>>() {}.capture()));
}
public void testToString() {
assertEquals(int[].class.getName(), Types.toString(int[].class));
assertEquals(int[][].class.getName(), Types.toString(int[][].class));
assertEquals(String[].class.getName(), Types.toString(String[].class));
Type elementType = List.class.getTypeParameters()[0];
assertEquals(elementType.toString(), Types.toString(elementType));
}
public void testNullPointers() {
new NullPointerTester()
.setDefault(Type[].class, new Type[]{ Map.class })
.setDefault(Type.class, String.class)
.setDefault(GenericDeclaration.class, Types.class)
.testStaticMethods(Types.class, Visibility.PACKAGE);
}
}