diff options
Diffstat (limited to 'guava/src/com/google/common/reflect')
11 files changed, 2628 insertions, 0 deletions
diff --git a/guava/src/com/google/common/reflect/AbstractInvocationHandler.java b/guava/src/com/google/common/reflect/AbstractInvocationHandler.java new file mode 100644 index 0000000..8453b7c --- /dev/null +++ b/guava/src/com/google/common/reflect/AbstractInvocationHandler.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2012 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 com.google.common.annotations.Beta; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +import javax.annotation.Nullable; + +/** + * Abstract implementation of {@link InvocationHandler} that handles {@link Object#equals}, + * {@link Object#hashCode} and {@link Object#toString}. + * + * @author Ben Yu + * @since 12.0 + */ +@Beta +public abstract class AbstractInvocationHandler implements InvocationHandler { + + private static final Object[] NO_ARGS = {}; + + /** + * {@inheritDoc} + * + * <p>{@link Object#equals}, {@link Object#hashCode} are implemented according to referential + * equality (the default behavior of {@link Object}). {@link Object#toString} delegates to + * {@link #toString} that can be overridden by subclasses. + */ + @Override public final Object invoke(Object proxy, Method method, @Nullable Object[] args) + throws Throwable { + if (args == null) { + args = NO_ARGS; + } + if (args.length == 0 && method.getName().equals("hashCode")) { + return System.identityHashCode(proxy); + } + if (args.length == 1 + && method.getName().equals("equals") + && method.getParameterTypes()[0] == Object.class) { + return proxy == args[0]; + } + if (args.length == 0 && method.getName().equals("toString")) { + return toString(); + } + return handleInvocation(proxy, method, args); + } + + /** + * {@link #invoke} delegates to this method upon any method invocation on the proxy instance, + * except {@link Object#equals}, {@link Object#hashCode} and {@link Object#toString}. The result + * will be returned as the proxied method's return value. + * + * <p>Unlike {@link #invoke}, {@code args} will never be null. When the method has no parameter, + * an empty array is passed in. + */ + protected abstract Object handleInvocation(Object proxy, Method method, Object[] args) + throws Throwable; + + /** + * The dynamic proxies' {@link Object#toString} will delegate to this method. Subclasses can + * override this to provide custom string representation of the proxies. + */ + @Override public String toString() { + return super.toString(); + } +} diff --git a/guava/src/com/google/common/reflect/ImmutableTypeToInstanceMap.java b/guava/src/com/google/common/reflect/ImmutableTypeToInstanceMap.java new file mode 100644 index 0000000..43e5e1e --- /dev/null +++ b/guava/src/com/google/common/reflect/ImmutableTypeToInstanceMap.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012 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 com.google.common.annotations.Beta; +import com.google.common.collect.ForwardingMap; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +/** + * A type-to-instance map backed by an {@link ImmutableMap}. See also {@link + * MutableTypeToInstanceMap}. + * + * @author Ben Yu + * @since 13.0 + */ +@Beta +public final class ImmutableTypeToInstanceMap<B> extends ForwardingMap<TypeToken<? extends B>, B> + implements TypeToInstanceMap<B> { + + /** Returns an empty type to instance map. */ + public static <B> ImmutableTypeToInstanceMap<B> of() { + return new ImmutableTypeToInstanceMap<B>(ImmutableMap.<TypeToken<? extends B>, B>of()); + } + + /** Returns a new builder. */ + public static <B> Builder<B> builder() { + return new Builder<B>(); + } + + /** + * A builder for creating immutable type-to-instance maps. Example: + * <pre> {@code + * + * static final ImmutableTypeToInstanceMap<Handler<?>> HANDLERS = + * ImmutableTypeToInstanceMap.<Handler<?>>builder() + * .put(new TypeToken<Handler<Foo>>() {}, new FooHandler()) + * .put(new TypeToken<Handler<Bar>>() {}, new SubBarHandler()) + * .build();}</pre> + * + * After invoking {@link #build()} it is still possible to add more entries + * and build again. Thus each map generated by this builder will be a superset + * of any map generated before it. + * + * @since 13.0 + */ + @Beta + public static final class Builder<B> { + private final ImmutableMap.Builder<TypeToken<? extends B>, B> mapBuilder + = ImmutableMap.builder(); + + private Builder() {} + + /** + * Associates {@code key} with {@code value} in the built map. Duplicate + * keys are not allowed, and will cause {@link #build} to fail. + */ + public <T extends B> Builder<B> put(Class<T> key, T value) { + mapBuilder.put(TypeToken.of(key), value); + return this; + } + + /** + * Associates {@code key} with {@code value} in the built map. Duplicate + * keys are not allowed, and will cause {@link #build} to fail. + */ + public <T extends B> Builder<B> put(TypeToken<T> key, T value) { + mapBuilder.put(key.rejectTypeVariables(), value); + return this; + } + + /** + * Returns a new immutable type-to-instance map containing the entries + * provided to this builder. + * + * @throws IllegalArgumentException if duplicate keys were added + */ + public ImmutableTypeToInstanceMap<B> build() { + return new ImmutableTypeToInstanceMap<B>(mapBuilder.build()); + } + } + + private final ImmutableMap<TypeToken<? extends B>, B> delegate; + + private ImmutableTypeToInstanceMap(ImmutableMap<TypeToken<? extends B>, B> delegate) { + this.delegate = delegate; + } + + @Override public <T extends B> T getInstance(TypeToken<T> type) { + return trustedGet(type.rejectTypeVariables()); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override public <T extends B> T putInstance(TypeToken<T> type, T value) { + throw new UnsupportedOperationException(); + } + + @Override public <T extends B> T getInstance(Class<T> type) { + return trustedGet(TypeToken.of(type)); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override public <T extends B> T putInstance(Class<T> type, T value) { + throw new UnsupportedOperationException(); + } + + @Override protected Map<TypeToken<? extends B>, B> delegate() { + return delegate; + } + + @SuppressWarnings("unchecked") // value could not get in if not a T + private <T extends B> T trustedGet(TypeToken<T> type) { + return (T) delegate.get(type); + } +} diff --git a/guava/src/com/google/common/reflect/MutableTypeToInstanceMap.java b/guava/src/com/google/common/reflect/MutableTypeToInstanceMap.java new file mode 100644 index 0000000..5f1249d --- /dev/null +++ b/guava/src/com/google/common/reflect/MutableTypeToInstanceMap.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 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 com.google.common.annotations.Beta; +import com.google.common.collect.ForwardingMap; +import com.google.common.collect.Maps; + +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * A mutable type-to-instance map. + * See also {@link ImmutableTypeToInstanceMap}. + * + * @author Ben Yu + * @since 13.0 + */ +@Beta +public final class MutableTypeToInstanceMap<B> extends ForwardingMap<TypeToken<? extends B>, B> + implements TypeToInstanceMap<B> { + + private final Map<TypeToken<? extends B>, B> backingMap = Maps.newHashMap(); + + @Nullable + @Override + public <T extends B> T getInstance(Class<T> type) { + return trustedGet(TypeToken.of(type)); + } + + @Nullable + @Override + public <T extends B> T putInstance(Class<T> type, @Nullable T value) { + return trustedPut(TypeToken.of(type), value); + } + + @Nullable + @Override + public <T extends B> T getInstance(TypeToken<T> type) { + return trustedGet(type.rejectTypeVariables()); + } + + @Nullable + @Override + public <T extends B> T putInstance(TypeToken<T> type, @Nullable T value) { + return trustedPut(type.rejectTypeVariables(), value); + } + + /** Not supported. Use {@link #putInstance} instead. */ + @Override public B put(TypeToken<? extends B> key, B value) { + throw new UnsupportedOperationException("Please use putInstance() instead."); + } + + /** Not supported. Use {@link #putInstance} instead. */ + @Override public void putAll(Map<? extends TypeToken<? extends B>, ? extends B> map) { + throw new UnsupportedOperationException("Please use putInstance() instead."); + } + + @Override protected Map<TypeToken<? extends B>, B> delegate() { + return backingMap; + } + + @SuppressWarnings("unchecked") // value could not get in if not a T + @Nullable + private <T extends B> T trustedPut(TypeToken<T> type, @Nullable T value) { + return (T) backingMap.put(type, value); + } + + @SuppressWarnings("unchecked") // value could not get in if not a T + @Nullable + private <T extends B> T trustedGet(TypeToken<T> type) { + return (T) backingMap.get(type); + } +} diff --git a/guava/src/com/google/common/reflect/Reflection.java b/guava/src/com/google/common/reflect/Reflection.java new file mode 100644 index 0000000..6b25f01 --- /dev/null +++ b/guava/src/com/google/common/reflect/Reflection.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005 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 com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; + +/** + * Static utilities relating to Java reflection. + * + * @since 12.0 + */ +@Beta +public final class Reflection { + + /** + * Returns the package name of {@code cls} according to the Java Language Specification (section + * 6.7). Unlike {@link Class#getPackage}, this method only parses the class name, without + * attempting to define the {@link Package} and hence load files. + */ + public static String getPackageName(Class<?> cls) { + return getPackageName(cls.getName()); + } + + /** + * Returns the package name of {@code classFullName} according to the Java Language Specification + * (section 6.7). Unlike {@link Class#getPackage}, this method only parses the class name, without + * attempting to define the {@link Package} and hence load files. + */ + public static String getPackageName(String classFullName) { + int lastDot = classFullName.lastIndexOf('.'); + if (lastDot < 0) { + return ""; + } else { + return classFullName.substring(0, lastDot); + } + } + + /** + * Ensures that the given classes are initialized, as described in + * <a href="http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.4.2"> + * JLS Section 12.4.2</a>. + * + * <p>WARNING: Normally it's a smell if a class needs to be explicitly initialized, because static + * state hurts system maintainability and testability. In cases when you have no choice while + * inter-operating with a legacy framework, this method helps to keep the code less ugly. + * + * @throws ExceptionInInitializerError if an exception is thrown during + * initialization of a class + */ + public static void initialize(Class<?>... classes) { + for (Class<?> clazz : classes) { + try { + Class.forName(clazz.getName(), true, clazz.getClassLoader()); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + } + } + + /** + * Returns a proxy instance that implements {@code interfaceType} by + * dispatching method invocations to {@code handler}. The class loader of + * {@code interfaceType} will be used to define the proxy class. To implement + * multiple interfaces or specify a class loader, use + * {@link Proxy#newProxyInstance}. + * + * @throws IllegalArgumentException if {@code interfaceType} does not specify + * the type of a Java interface + */ + public static <T> T newProxy( + Class<T> interfaceType, InvocationHandler handler) { + checkNotNull(interfaceType); + checkNotNull(handler); + checkArgument(interfaceType.isInterface()); + Object object = Proxy.newProxyInstance( + interfaceType.getClassLoader(), + new Class<?>[] { interfaceType }, + handler); + return interfaceType.cast(object); + } + + private Reflection() {} +} diff --git a/guava/src/com/google/common/reflect/TypeCapture.java b/guava/src/com/google/common/reflect/TypeCapture.java new file mode 100644 index 0000000..c686661 --- /dev/null +++ b/guava/src/com/google/common/reflect/TypeCapture.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 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 com.google.common.base.Preconditions.checkArgument; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * Captures the actual type of {@code T}. + * + * @author Ben Yu + */ +abstract class TypeCapture<T> { + + /** Returns the captured type. */ + final Type capture() { + Type superclass = getClass().getGenericSuperclass(); + checkArgument(superclass instanceof ParameterizedType, + "%s isn't parameterized", superclass); + return ((ParameterizedType) superclass).getActualTypeArguments()[0]; + } +} diff --git a/guava/src/com/google/common/reflect/TypeParameter.java b/guava/src/com/google/common/reflect/TypeParameter.java new file mode 100644 index 0000000..a6a46bc --- /dev/null +++ b/guava/src/com/google/common/reflect/TypeParameter.java @@ -0,0 +1,70 @@ +/* + * 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 com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; + +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +/** + * Captures a free type variable that can be used in {@link TypeToken#where}. + * For example: <pre> {@code + * + * static <T> TypeToken<List<T>> listOf(Class<T> elementType) { + * return new TypeToken<List<T>>() {} + * .where(new TypeParameter<T>() {}, elementType); + * } + * }</pre> + * + * @author Ben Yu + * @since 12.0 + */ +@Beta +public abstract class TypeParameter<T> extends TypeCapture<T> { + + final TypeVariable<?> typeVariable; + + private TypeParameter(TypeVariable<?> typeVariable) { + this.typeVariable = checkNotNull(typeVariable); + } + + protected TypeParameter() { + Type type = capture(); + checkArgument(type instanceof TypeVariable, "%s should be a type variable.", type); + this.typeVariable = (TypeVariable<?>) type; + } + + @Override public final int hashCode() { + return typeVariable.hashCode(); + } + + @Override public final boolean equals(Object o) { + if (o instanceof TypeParameter) { + TypeParameter<?> that = (TypeParameter<?>) o; + return typeVariable.equals(that.typeVariable); + } + return false; + } + + @Override public String toString() { + return typeVariable.toString(); + } +} diff --git a/guava/src/com/google/common/reflect/TypeResolver.java b/guava/src/com/google/common/reflect/TypeResolver.java new file mode 100644 index 0000000..0c42ef5 --- /dev/null +++ b/guava/src/com/google/common/reflect/TypeResolver.java @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2009 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 com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +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.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.annotation.Nullable; + +/** + * An object of this class encapsulates type mappings from type variables. Mappings are established + * with {@link #where} and types are resolved using {@link #resolveType}. + * + * <p>Note that usually type mappings are already implied by the static type hierarchy (for example, + * the {@code E} type variable declared by class {@code List} naturally maps to {@code String} in + * the context of {@code class MyStringList implements List<String>}. In such case, prefer to use + * {@link TypeToken#resolveType} since it's simpler and more type safe. This class should only be + * used when the type mapping isn't implied by the static type hierarchy, but provided through other + * means such as an annotation or external configuration file. + * + * @author Ben Yu + */ +class TypeResolver { + + private final ImmutableMap<TypeVariable<?>, Type> typeTable; + + public TypeResolver() { + this.typeTable = ImmutableMap.of(); + } + + private TypeResolver(ImmutableMap<TypeVariable<?>, Type> typeTable) { + this.typeTable = typeTable; + } + + static TypeResolver accordingTo(Type type) { + return new TypeResolver().where(TypeMappingIntrospector.getTypeMappings(type)); + } + + /** + * Returns a new {@code TypeResolver} with type variables in {@code formal} mapping to types in + * {@code actual}. + * + * <p>For example, if {@code formal} is a {@code TypeVariable T}, and {@code actual} is {@code + * String.class}, then {@code new TypeResolver().where(formal, actual)} will {@linkplain + * #resolveType resolve} {@code ParameterizedType List<T>} to {@code List<String>}, and resolve + * {@code Map<T, Something>} to {@code Map<String, Something>} etc. Similarly, {@code formal} and + * {@code actual} can be {@code Map<K, V>} and {@code Map<String, Integer>} respectively, or they + * can be {@code E[]} and {@code String[]} respectively, or even any arbitrary combination + * thereof. + * + * @param formal The type whose type variables or itself is mapped to other type(s). It's almost + * always a bug if {@code formal} isn't a type variable and contains no type variable. Make + * sure you are passing the two parameters in the right order. + * @param actual The type that the formal type variable(s) are mapped to. It can be or contain yet + * other type variables, in which case these type variables will be further resolved if + * corresponding mappings exist in the current {@code TypeResolver} instance. + */ + public final TypeResolver where(Type formal, Type actual) { + Map<TypeVariable<?>, Type> mappings = Maps.newHashMap(); + populateTypeMappings(mappings, formal, actual); + return where(mappings); + } + + /** Returns a new {@code TypeResolver} with {@code variable} mapping to {@code type}. */ + final TypeResolver where(Map<? extends TypeVariable<?>, ? extends Type> mappings) { + ImmutableMap.Builder<TypeVariable<?>, Type> builder = ImmutableMap.builder(); + builder.putAll(typeTable); + for (Map.Entry<? extends TypeVariable<?>, ? extends Type> mapping : mappings.entrySet()) { + TypeVariable<?> variable = mapping.getKey(); + Type type = mapping.getValue(); + checkArgument(!variable.equals(type), "Type variable %s bound to itself", variable); + builder.put(variable, type); + } + return new TypeResolver(builder.build()); + } + + private static void populateTypeMappings( + Map<TypeVariable<?>, Type> mappings, Type from, Type to) { + if (from.equals(to)) { + return; + } + if (from instanceof TypeVariable) { + mappings.put((TypeVariable<?>) from, to); + } else if (from instanceof GenericArrayType) { + populateTypeMappings(mappings, + ((GenericArrayType) from).getGenericComponentType(), + checkNonNullArgument(Types.getComponentType(to), "%s is not an array type.", to)); + } else if (from instanceof ParameterizedType) { + ParameterizedType fromParameterizedType = (ParameterizedType) from; + ParameterizedType toParameterizedType = expectArgument(ParameterizedType.class, to); + checkArgument(fromParameterizedType.getRawType().equals(toParameterizedType.getRawType()), + "Inconsistent raw type: %s vs. %s", from, to); + Type[] fromArgs = fromParameterizedType.getActualTypeArguments(); + Type[] toArgs = toParameterizedType.getActualTypeArguments(); + checkArgument(fromArgs.length == toArgs.length); + for (int i = 0; i < fromArgs.length; i++) { + populateTypeMappings(mappings, fromArgs[i], toArgs[i]); + } + } else if (from instanceof WildcardType) { + WildcardType fromWildcardType = (WildcardType) from; + WildcardType toWildcardType = expectArgument(WildcardType.class, to); + Type[] fromUpperBounds = fromWildcardType.getUpperBounds(); + Type[] toUpperBounds = toWildcardType.getUpperBounds(); + Type[] fromLowerBounds = fromWildcardType.getLowerBounds(); + Type[] toLowerBounds = toWildcardType.getLowerBounds(); + checkArgument( + fromUpperBounds.length == toUpperBounds.length + && fromLowerBounds.length == toLowerBounds.length, + "Incompatible type: %s vs. %s", from, to); + for (int i = 0; i < fromUpperBounds.length; i++) { + populateTypeMappings(mappings, fromUpperBounds[i], toUpperBounds[i]); + } + for (int i = 0; i < fromLowerBounds.length; i++) { + populateTypeMappings(mappings, fromLowerBounds[i], toLowerBounds[i]); + } + } else { + throw new IllegalArgumentException("No type mapping from " + from); + } + } + + /** + * Resolves all type variables in {@code type} and all downstream types and + * returns a corresponding type with type variables resolved. + */ + public final Type resolveType(Type type) { + if (type instanceof TypeVariable) { + return resolveTypeVariable((TypeVariable<?>) type); + } else if (type instanceof ParameterizedType) { + return resolveParameterizedType((ParameterizedType) type); + } else if (type instanceof GenericArrayType) { + return resolveGenericArrayType((GenericArrayType) type); + } else if (type instanceof WildcardType) { + WildcardType wildcardType = (WildcardType) type; + return new Types.WildcardTypeImpl( + resolveTypes(wildcardType.getLowerBounds()), + resolveTypes(wildcardType.getUpperBounds())); + } else { + // if Class<?>, no resolution needed, we are done. + return type; + } + } + + private Type[] resolveTypes(Type[] types) { + Type[] result = new Type[types.length]; + for (int i = 0; i < types.length; i++) { + result[i] = resolveType(types[i]); + } + return result; + } + + private Type resolveGenericArrayType(GenericArrayType type) { + Type componentType = resolveType(type.getGenericComponentType()); + return Types.newArrayType(componentType); + } + + private Type resolveTypeVariable(final TypeVariable<?> var) { + final TypeResolver unguarded = this; + TypeResolver guarded = new TypeResolver(typeTable) { + @Override Type resolveTypeVariable( + TypeVariable<?> intermediateVar, TypeResolver guardedResolver) { + if (intermediateVar.getGenericDeclaration().equals(var.getGenericDeclaration())) { + return intermediateVar; + } + return unguarded.resolveTypeVariable(intermediateVar, guardedResolver); + } + }; + return resolveTypeVariable(var, guarded); + } + + /** + * Resolves {@code var} using the encapsulated type mapping. If it maps to yet another + * non-reified type, {@code guardedResolver} is used to do further resolution, which doesn't try + * to resolve any type variable on generic declarations that are already being resolved. + */ + Type resolveTypeVariable(TypeVariable<?> var, TypeResolver guardedResolver) { + Type type = typeTable.get(var); + if (type == null) { + Type[] bounds = var.getBounds(); + if (bounds.length == 0) { + return var; + } + return Types.newTypeVariable( + var.getGenericDeclaration(), + var.getName(), + guardedResolver.resolveTypes(bounds)); + } + return guardedResolver.resolveType(type); // in case the type is yet another type variable. + } + + private ParameterizedType resolveParameterizedType(ParameterizedType type) { + Type owner = type.getOwnerType(); + Type resolvedOwner = (owner == null) ? null : resolveType(owner); + Type resolvedRawType = resolveType(type.getRawType()); + + Type[] vars = type.getActualTypeArguments(); + Type[] resolvedArgs = new Type[vars.length]; + for (int i = 0; i < vars.length; i++) { + resolvedArgs[i] = resolveType(vars[i]); + } + return Types.newParameterizedTypeWithOwner( + resolvedOwner, (Class<?>) resolvedRawType, resolvedArgs); + } + + private static <T> T checkNonNullArgument(T arg, String format, Object... messageParams) { + checkArgument(arg != null, format, messageParams); + return arg; + } + + private static <T> T expectArgument(Class<T> type, Object arg) { + try { + return type.cast(arg); + } catch (ClassCastException e) { + throw new IllegalArgumentException(arg + " is not a " + type.getSimpleName()); + } + } + + private static final class TypeMappingIntrospector { + + private static final WildcardCapturer wildcardCapturer = new WildcardCapturer(); + + private final Map<TypeVariable<?>, Type> mappings = Maps.newHashMap(); + private final Set<Type> introspectedTypes = Sets.newHashSet(); + + /** + * Returns type mappings using type parameters and type arguments found in + * the generic superclass and the super interfaces of {@code contextClass}. + */ + static ImmutableMap<TypeVariable<?>, Type> getTypeMappings( + Type contextType) { + TypeMappingIntrospector introspector = new TypeMappingIntrospector(); + introspector.introspect(wildcardCapturer.capture(contextType)); + return ImmutableMap.copyOf(introspector.mappings); + } + + private void introspect(Type type) { + if (!introspectedTypes.add(type)) { + return; + } + if (type instanceof ParameterizedType) { + introspectParameterizedType((ParameterizedType) type); + } else if (type instanceof Class) { + introspectClass((Class<?>) type); + } else if (type instanceof TypeVariable) { + for (Type bound : ((TypeVariable<?>) type).getBounds()) { + introspect(bound); + } + } else if (type instanceof WildcardType) { + for (Type bound : ((WildcardType) type).getUpperBounds()) { + introspect(bound); + } + } + } + + private void introspectClass(Class<?> clazz) { + introspect(clazz.getGenericSuperclass()); + for (Type interfaceType : clazz.getGenericInterfaces()) { + introspect(interfaceType); + } + } + + private void introspectParameterizedType( + ParameterizedType parameterizedType) { + Class<?> rawClass = (Class<?>) parameterizedType.getRawType(); + TypeVariable<?>[] vars = rawClass.getTypeParameters(); + Type[] typeArgs = parameterizedType.getActualTypeArguments(); + checkState(vars.length == typeArgs.length); + for (int i = 0; i < vars.length; i++) { + map(vars[i], typeArgs[i]); + } + introspectClass(rawClass); + introspect(parameterizedType.getOwnerType()); + } + + private void map(final TypeVariable<?> var, final Type arg) { + if (mappings.containsKey(var)) { + // Mapping already established + // This is possible when following both superClass -> enclosingClass + // and enclosingclass -> superClass paths. + // Since we follow the path of superclass first, enclosing second, + // superclass mapping should take precedence. + return; + } + // First, check whether var -> arg forms a cycle + for (Type t = arg; t != null; t = mappings.get(t)) { + if (var.equals(t)) { + // cycle detected, remove the entire cycle from the mapping so that + // each type variable resolves deterministically to itself. + // Otherwise, a F -> T cycle will end up resolving both F and T + // nondeterministically to either F or T. + for (Type x = arg; x != null; x = mappings.remove(x)) {} + return; + } + } + mappings.put(var, arg); + } + } + + // This is needed when resolving types against a context with wildcards + // For example: + // class Holder<T> { + // void set(T data) {...} + // } + // Holder<List<?>> should *not* resolve the set() method to set(List<?> data). + // Instead, it should create a capture of the wildcard so that set() rejects any List<T>. + private static final class WildcardCapturer { + + private final AtomicInteger id = new AtomicInteger(); + + Type capture(Type type) { + checkNotNull(type); + if (type instanceof Class) { + return type; + } + if (type instanceof TypeVariable) { + return type; + } + if (type instanceof GenericArrayType) { + GenericArrayType arrayType = (GenericArrayType) type; + return Types.newArrayType(capture(arrayType.getGenericComponentType())); + } + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + return Types.newParameterizedTypeWithOwner( + captureNullable(parameterizedType.getOwnerType()), + (Class<?>) parameterizedType.getRawType(), + capture(parameterizedType.getActualTypeArguments())); + } + if (type instanceof WildcardType) { + WildcardType wildcardType = (WildcardType) type; + Type[] lowerBounds = wildcardType.getLowerBounds(); + if (lowerBounds.length == 0) { // ? extends something changes to capture-of + Type[] upperBounds = wildcardType.getUpperBounds(); + String name = "capture#" + id.incrementAndGet() + "-of ? extends " + + Joiner.on('&').join(upperBounds); + return Types.newTypeVariable( + WildcardCapturer.class, name, wildcardType.getUpperBounds()); + } else { + // TODO(benyu): handle ? super T somehow. + return type; + } + } + throw new AssertionError("must have been one of the known types"); + } + + private Type captureNullable(@Nullable Type type) { + if (type == null) { + return null; + } + return capture(type); + } + + private Type[] capture(Type[] types) { + Type[] result = new Type[types.length]; + for (int i = 0; i < types.length; i++) { + result[i] = capture(types[i]); + } + return result; + } + } +} diff --git a/guava/src/com/google/common/reflect/TypeToInstanceMap.java b/guava/src/com/google/common/reflect/TypeToInstanceMap.java new file mode 100644 index 0000000..3b00820 --- /dev/null +++ b/guava/src/com/google/common/reflect/TypeToInstanceMap.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2012 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 com.google.common.annotations.Beta; + +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * A map, each entry of which maps a {@link TypeToken} to an instance of that type. + * In addition to implementing {@code Map}, the additional type-safe operations + * {@link #putInstance} and {@link #getInstance} are available. + * + * <p>Generally, implementations don't support {@link #put} and {@link #putAll} + * because there is no way to check an object at runtime to be an instance of a + * {@link TypeToken}. Instead, caller should use the type safe {@link #putInstance}. + * + * <p>Also, if caller suppresses unchecked warnings and passes in an {@code Iterable<String>} + * for type {@code Iterable<Integer>}, the map won't be able to detect and throw type error. + * + * <p>Like any other {@code Map<Class, Object>}, this map may contain entries + * for primitive types, and a primitive type and its corresponding wrapper type + * may map to different values. + * + * @param <B> the common supertype that all entries must share; often this is + * simply {@link Object} + * + * @author Ben Yu + * @since 13.0 + */ +@Beta +public interface TypeToInstanceMap<B> extends Map<TypeToken<? extends B>, B> { + + /** + * Returns the value the specified class is mapped to, or {@code null} if no + * entry for this class is present. This will only return a value that was + * bound to this specific class, not a value that may have been bound to a + * subtype. + * + * <p>{@code getInstance(Foo.class)} is equivalent to + * {@code getInstance(TypeToken.of(Foo.class))}. + */ + @Nullable + <T extends B> T getInstance(Class<T> type); + + /** + * Maps the specified class to the specified value. Does <i>not</i> associate + * this value with any of the class's supertypes. + * + * <p>{@code putInstance(Foo.class, foo)} is equivalent to + * {@code putInstance(TypeToken.of(Foo.class), foo)}. + * + * @return the value previously associated with this class (possibly {@code null}), + * or {@code null} if there was no previous entry. + */ + @Nullable + <T extends B> T putInstance(Class<T> type, @Nullable T value); + + /** + * Returns the value the specified type is mapped to, or {@code null} if no + * entry for this type is present. This will only return a value that was + * bound to this specific type, not a value that may have been bound to a subtype. + */ + @Nullable + <T extends B> T getInstance(TypeToken<T> type); + + /** + * Maps the specified type to the specified value. Does <i>not</i> associate + * this value with any of the type's supertypes. + * + * @return the value previously associated with this type (possibly {@code null}), + * or {@code null} if there was no previous entry. + */ + @Nullable + <T extends B> T putInstance(TypeToken<T> type, @Nullable T value); +} diff --git a/guava/src/com/google/common/reflect/TypeToken.java b/guava/src/com/google/common/reflect/TypeToken.java new file mode 100644 index 0000000..08a2440 --- /dev/null +++ b/guava/src/com/google/common/reflect/TypeToken.java @@ -0,0 +1,1086 @@ +/* + * Copyright (C) 2006 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 com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ForwardingSet; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Ordering; + +import java.io.Serializable; +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.Comparator; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * A {@link Type} with generics. + * + * <p>Operations that are otherwise only available in {@link Class} are implemented to support + * {@code Type}, for example {@link #isAssignableFrom}, {@link #isArray} and {@link + * #getComponentType}. It also provides additional utilities such as {@link #getTypes} and {@link + * #resolveType} etc. + * + * <p>There are three ways to get a {@code TypeToken} instance: <ul> + * <li>Wrap a {@code Type} obtained via reflection. For example: {@code + * TypeToken.of(method.getGenericReturnType())}. + * <li>Capture a generic type with a (usually anonymous) subclass. For example: <pre> {@code + * + * new TypeToken<List<String>>() {} + * }</pre> + * Note that it's critical that the actual type argument is carried by a subclass. + * The following code is wrong because it only captures the {@code <T>} type variable + * of the {@code listType()} method signature; while {@code <String>} is lost in erasure: + * <pre> {@code + * + * class Util { + * static <T> TypeToken<List<T>> listType() { + * return new TypeToken<List<T>>() {}; + * } + * } + * + * TypeToken<List<String>> stringListType = Util.<String>listType(); + * }</pre> + * <li>Capture a generic type with a (usually anonymous) subclass and resolve it against + * a context class that knows what the type parameters are. For example: <pre> {@code + * abstract class IKnowMyType<T> { + * TypeToken<T> type = new TypeToken<T>(getClass()) {}; + * } + * new IKnowMyType<String>() {}.type => String + * }</pre> + * </ul> + * + * <p>{@code TypeToken} is serializable when no type variable is contained in the type. + * + * <p>Note to Guice users: {@code} TypeToken is similar to Guice's {@code TypeLiteral} class, + * but with one important difference: it supports non-reified types such as {@code T}, + * {@code List<T>} or even {@code List<? extends Number>}; while TypeLiteral does not. + * TypeToken is also serializable and offers numerous additional utility methods. + * + * @author Bob Lee + * @author Sven Mawson + * @author Ben Yu + * @since 12.0 + */ +@Beta +@SuppressWarnings("serial") // SimpleTypeToken is the serialized form. +public abstract class TypeToken<T> extends TypeCapture<T> implements Serializable { + + private final Type runtimeType; + + /** Resolver for resolving types with {@link #runtimeType} as context. */ + private transient TypeResolver typeResolver; + + /** + * Constructs a new type token of {@code T}. + * + * <p>Clients create an empty anonymous subclass. Doing so embeds the type + * parameter in the anonymous class's type hierarchy so we can reconstitute + * it at runtime despite erasure. + * + * <p>For example: <pre> {@code + * + * TypeToken<List<String>> t = new TypeToken<List<String>>() {}; + * }</pre> + */ + protected TypeToken() { + this.runtimeType = capture(); + checkState(!(runtimeType instanceof TypeVariable), + "Cannot construct a TypeToken for a type variable.\n" + + "You probably meant to call new TypeToken<%s>(getClass()) " + + "that can resolve the type variable for you.\n" + + "If you do need to create a TypeToken of a type variable, " + + "please use TypeToken.of() instead.", runtimeType); + } + + /** + * Constructs a new type token of {@code T} while resolving free type variables in the context of + * {@code declaringClass}. + * + * <p>Clients create an empty anonymous subclass. Doing so embeds the type + * parameter in the anonymous class's type hierarchy so we can reconstitute + * it at runtime despite erasure. + * + * <p>For example: <pre> {@code + * + * abstract class IKnowMyType<T> { + * TypeToken<T> getMyType() { + * return new TypeToken<T>(getClass()) {}; + * } + * } + * + * new IKnowMyType<String>() {}.getMyType() => String + * }</pre> + */ + protected TypeToken(Class<?> declaringClass) { + Type captured = super.capture(); + if (captured instanceof Class) { + this.runtimeType = captured; + } else { + this.runtimeType = of(declaringClass).resolveType(captured).runtimeType; + } + } + + private TypeToken(Type type) { + this.runtimeType = checkNotNull(type); + } + + /** Returns an instance of type token that wraps {@code type}. */ + public static <T> TypeToken<T> of(Class<T> type) { + return new SimpleTypeToken<T>(type); + } + + /** Returns an instance of type token that wraps {@code type}. */ + public static TypeToken<?> of(Type type) { + return new SimpleTypeToken<Object>(type); + } + + /** + * Returns the raw type of {@code T}. Formally speaking, if {@code T} is returned by + * {@link java.lang.reflect.Method#getGenericReturnType}, the raw type is what's returned by + * {@link java.lang.reflect.Method#getReturnType} of the same method object. Specifically: + * <ul> + * <li>If {@code T} is a {@code Class} itself, {@code T} itself is returned. + * <li>If {@code T} is a {@link ParameterizedType}, the raw type of the parameterized type is + * returned. + * <li>If {@code T} is a {@link GenericArrayType}, the returned type is the corresponding array + * class. For example: {@code List<Integer>[] => List[]}. + * <li>If {@code T} is a type variable or a wildcard type, the raw type of the first upper bound + * is returned. For example: {@code <X extends Foo> => Foo}. + * </ul> + */ + public final Class<? super T> getRawType() { + Class<?> rawType = getRawType(runtimeType); + @SuppressWarnings("unchecked") // raw type is |T| + Class<? super T> result = (Class<? super T>) rawType; + return result; + } + + /** + * Returns the raw type of the class or parameterized type; if {@code T} is type variable or + * wildcard type, the raw types of all its upper bounds are returned. + */ + private ImmutableSet<Class<? super T>> getImmediateRawTypes() { + // Cast from ImmutableSet<Class<?>> to ImmutableSet<Class<? super T>> + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableSet<Class<? super T>> result = (ImmutableSet) getRawTypes(runtimeType); + return result; + } + + /** Returns the represented type. */ + public final Type getType() { + return runtimeType; + } + + /** + * Returns a new {@code TypeToken} where type variables represented by {@code typeParam} + * are substituted by {@code typeArg}. For example, it can be used to construct + * {@code Map<K, V>} for any {@code K} and {@code V} type: <pre> {@code + * + * static <K, V> TypeToken<Map<K, V>> mapOf( + * TypeToken<K> keyType, TypeToken<V> valueType) { + * return new TypeToken<Map<K, V>>() {} + * .where(new TypeParameter<K>() {}, keyType) + * .where(new TypeParameter<V>() {}, valueType); + * } + * }</pre> + * + * @param <X> The parameter type + * @param typeParam the parameter type variable + * @param typeArg the actual type to substitute + */ + public final <X> TypeToken<T> where(TypeParameter<X> typeParam, TypeToken<X> typeArg) { + TypeResolver resolver = new TypeResolver() + .where(ImmutableMap.of(typeParam.typeVariable, typeArg.runtimeType)); + // If there's any type error, we'd report now rather than later. + return new SimpleTypeToken<T>(resolver.resolveType(runtimeType)); + } + + /** + * Returns a new {@code TypeToken} where type variables represented by {@code typeParam} + * are substituted by {@code typeArg}. For example, it can be used to construct + * {@code Map<K, V>} for any {@code K} and {@code V} type: <pre> {@code + * + * static <K, V> TypeToken<Map<K, V>> mapOf( + * Class<K> keyType, Class<V> valueType) { + * return new TypeToken<Map<K, V>>() {} + * .where(new TypeParameter<K>() {}, keyType) + * .where(new TypeParameter<V>() {}, valueType); + * } + * }</pre> + * + * @param <X> The parameter type + * @param typeParam the parameter type variable + * @param typeArg the actual type to substitute + */ + public final <X> TypeToken<T> where(TypeParameter<X> typeParam, Class<X> typeArg) { + return where(typeParam, of(typeArg)); + } + + /** + * Resolves the given {@code type} against the type context represented by this type. + * For example: <pre> {@code + * + * new TypeToken<List<String>>() {}.resolveType( + * List.class.getMethod("get", int.class).getGenericReturnType()) + * => String.class + * }</pre> + */ + public final TypeToken<?> resolveType(Type type) { + checkNotNull(type); + TypeResolver resolver = typeResolver; + if (resolver == null) { + resolver = (typeResolver = TypeResolver.accordingTo(runtimeType)); + } + return of(resolver.resolveType(type)); + } + + private TypeToken<?> resolveSupertype(Type type) { + TypeToken<?> supertype = resolveType(type); + // super types' type mapping is a subset of type mapping of this type. + supertype.typeResolver = typeResolver; + return supertype; + } + + /** + * Returns the generic superclass of this type or {@code null} if the type represents + * {@link Object} or an interface. This method is similar but different from {@link + * Class#getGenericSuperclass}. For example, {@code + * new TypeToken<StringArrayList>() {}.getGenericSuperclass()} will return {@code + * new TypeToken<ArrayList<String>>() {}}; while {@code + * StringArrayList.class.getGenericSuperclass()} will return {@code ArrayList<E>}, where {@code E} + * is the type variable declared by class {@code ArrayList}. + * + * <p>If this type is a type variable or wildcard, its first upper bound is examined and returned + * if the bound is a class or extends from a class. This means that the returned type could be a + * type variable too. + */ + @Nullable + final TypeToken<? super T> getGenericSuperclass() { + if (runtimeType instanceof TypeVariable) { + // First bound is always the super class, if one exists. + return boundAsSuperclass(((TypeVariable<?>) runtimeType).getBounds()[0]); + } + if (runtimeType instanceof WildcardType) { + // wildcard has one and only one upper bound. + return boundAsSuperclass(((WildcardType) runtimeType).getUpperBounds()[0]); + } + Type superclass = getRawType().getGenericSuperclass(); + if (superclass == null) { + return null; + } + @SuppressWarnings("unchecked") // super class of T + TypeToken<? super T> superToken = (TypeToken<? super T>) resolveSupertype(superclass); + return superToken; + } + + @Nullable private TypeToken<? super T> boundAsSuperclass(Type bound) { + TypeToken<?> token = of(bound); + if (token.getRawType().isInterface()) { + return null; + } + @SuppressWarnings("unchecked") // only upper bound of T is passed in. + TypeToken<? super T> superclass = (TypeToken<? super T>) token; + return superclass; + } + + /** + * Returns the generic interfaces that this type directly {@code implements}. This method is + * similar but different from {@link Class#getGenericInterfaces()}. For example, {@code + * new TypeToken<List<String>>() {}.getGenericInterfaces()} will return a list that contains + * {@code new TypeToken<Iterable<String>>() {}}; while {@code List.class.getGenericInterfaces()} + * will return an array that contains {@code Iterable<T>}, where the {@code T} is the type + * variable declared by interface {@code Iterable}. + * + * <p>If this type is a type variable or wildcard, its upper bounds are examined and those that + * are either an interface or upper-bounded only by interfaces are returned. This means that the + * returned types could include type variables too. + */ + final ImmutableList<TypeToken<? super T>> getGenericInterfaces() { + if (runtimeType instanceof TypeVariable) { + return boundsAsInterfaces(((TypeVariable<?>) runtimeType).getBounds()); + } + if (runtimeType instanceof WildcardType) { + return boundsAsInterfaces(((WildcardType) runtimeType).getUpperBounds()); + } + ImmutableList.Builder<TypeToken<? super T>> builder = ImmutableList.builder(); + for (Type interfaceType : getRawType().getGenericInterfaces()) { + @SuppressWarnings("unchecked") // interface of T + TypeToken<? super T> resolvedInterface = (TypeToken<? super T>) + resolveSupertype(interfaceType); + builder.add(resolvedInterface); + } + return builder.build(); + } + + private ImmutableList<TypeToken<? super T>> boundsAsInterfaces(Type[] bounds) { + ImmutableList.Builder<TypeToken<? super T>> builder = ImmutableList.builder(); + for (Type bound : bounds) { + @SuppressWarnings("unchecked") // upper bound of T + TypeToken<? super T> boundType = (TypeToken<? super T>) of(bound); + if (boundType.getRawType().isInterface()) { + builder.add(boundType); + } + } + return builder.build(); + } + + /** + * Returns the set of interfaces and classes that this type is or is a subtype of. The returned + * types are parameterized with proper type arguments. + * + * <p>Subtypes are always listed before supertypes. But the reverse is not true. A type isn't + * necessarily a subtype of all the types following. Order between types without subtype + * relationship is arbitrary and not guaranteed. + * + * <p>If this type is a type variable or wildcard, upper bounds that are themselves type variables + * aren't included (their super interfaces and superclasses are). + */ + public final TypeSet getTypes() { + return new TypeSet(); + } + + /** + * Returns the generic form of {@code superclass}. For example, if this is + * {@code ArrayList<String>}, {@code Iterable<String>} is returned given the + * input {@code Iterable.class}. + */ + public final TypeToken<? super T> getSupertype(Class<? super T> superclass) { + checkArgument(superclass.isAssignableFrom(getRawType()), + "%s is not a super class of %s", superclass, this); + if (runtimeType instanceof TypeVariable) { + return getSupertypeFromUpperBounds(superclass, ((TypeVariable<?>) runtimeType).getBounds()); + } + if (runtimeType instanceof WildcardType) { + return getSupertypeFromUpperBounds(superclass, ((WildcardType) runtimeType).getUpperBounds()); + } + if (superclass.isArray()) { + return getArraySupertype(superclass); + } + @SuppressWarnings("unchecked") // resolved supertype + TypeToken<? super T> supertype = (TypeToken<? super T>) + resolveSupertype(toGenericType(superclass).runtimeType); + return supertype; + } + + /** + * Returns subtype of {@code this} with {@code subclass} as the raw class. + * For example, if this is {@code Iterable<String>} and {@code subclass} is {@code List}, + * {@code List<String>} is returned. + */ + public final TypeToken<? extends T> getSubtype(Class<?> subclass) { + checkArgument(!(runtimeType instanceof TypeVariable), + "Cannot get subtype of type variable <%s>", this); + if (runtimeType instanceof WildcardType) { + return getSubtypeFromLowerBounds(subclass, ((WildcardType) runtimeType).getLowerBounds()); + } + checkArgument(getRawType().isAssignableFrom(subclass), + "%s isn't a subclass of %s", subclass, this); + // unwrap array type if necessary + if (isArray()) { + return getArraySubtype(subclass); + } + @SuppressWarnings("unchecked") // guarded by the isAssignableFrom() statement above + TypeToken<? extends T> subtype = (TypeToken<? extends T>) + of(resolveTypeArgsForSubclass(subclass)); + return subtype; + } + + /** Returns true if this type is assignable from the given {@code type}. */ + public final boolean isAssignableFrom(TypeToken<?> type) { + return isAssignableFrom(type.runtimeType); + } + + /** Check if this type is assignable from the given {@code type}. */ + public final boolean isAssignableFrom(Type type) { + return isAssignable(checkNotNull(type), runtimeType); + } + + /** + * Returns true if this type is known to be an array type, such as {@code int[]}, {@code T[]}, + * {@code <? extends Map<String, Integer>[]>} etc. + */ + public final boolean isArray() { + return getComponentType() != null; + } + + /** + * Returns the array component type if this type represents an array ({@code int[]}, {@code T[]}, + * {@code <? extends Map<String, Integer>[]>} etc.), or else {@code null} is returned. + */ + @Nullable public final TypeToken<?> getComponentType() { + Type componentType = Types.getComponentType(runtimeType); + if (componentType == null) { + return null; + } + return of(componentType); + } + + /** + * The set of interfaces and classes that {@code T} is or is a subtype of. {@link Object} is not + * included in the set if this type is an interface. + */ + public class TypeSet extends ForwardingSet<TypeToken<? super T>> implements Serializable { + + private transient ImmutableSet<TypeToken<? super T>> types; + + TypeSet() {} + + /** Returns the types that are interfaces implemented by this type. */ + public TypeSet interfaces() { + return new InterfaceSet(this); + } + + /** Returns the types that are classes. */ + public TypeSet classes() { + return new ClassSet(); + } + + @Override protected Set<TypeToken<? super T>> delegate() { + ImmutableSet<TypeToken<? super T>> filteredTypes = types; + if (filteredTypes == null) { + // Java has no way to express ? super T when we parameterize TypeToken vs. Class. + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableList<TypeToken<? super T>> collectedTypes = (ImmutableList) + TypeCollector.FOR_GENERIC_TYPE.collectTypes(TypeToken.this); + return (types = FluentIterable.from(collectedTypes) + .filter(TypeFilter.IGNORE_TYPE_VARIABLE_OR_WILDCARD) + .toImmutableSet()); + } else { + return filteredTypes; + } + } + + /** Returns the raw types of the types in this set, in the same order. */ + public Set<Class<? super T>> rawTypes() { + // Java has no way to express ? super T when we parameterize TypeToken vs. Class. + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableList<Class<? super T>> collectedTypes = (ImmutableList) + TypeCollector.FOR_RAW_TYPE.collectTypes(getImmediateRawTypes()); + return ImmutableSet.copyOf(collectedTypes); + } + + private static final long serialVersionUID = 0; + } + + private final class InterfaceSet extends TypeSet { + + private transient final TypeSet allTypes; + private transient ImmutableSet<TypeToken<? super T>> interfaces; + + InterfaceSet(TypeSet allTypes) { + this.allTypes = allTypes; + } + + @Override protected Set<TypeToken<? super T>> delegate() { + ImmutableSet<TypeToken<? super T>> result = interfaces; + if (result == null) { + return (interfaces = FluentIterable.from(allTypes) + .filter(TypeFilter.INTERFACE_ONLY) + .toImmutableSet()); + } else { + return result; + } + } + + @Override public TypeSet interfaces() { + return this; + } + + @Override public Set<Class<? super T>> rawTypes() { + // Java has no way to express ? super T when we parameterize TypeToken vs. Class. + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableList<Class<? super T>> collectedTypes = (ImmutableList) + TypeCollector.FOR_RAW_TYPE.collectTypes(getImmediateRawTypes()); + return FluentIterable.from(collectedTypes) + .filter(new Predicate<Class<?>>() { + @Override public boolean apply(Class<?> type) { + return type.isInterface(); + } + }) + .toImmutableSet(); + } + + @Override public TypeSet classes() { + throw new UnsupportedOperationException("interfaces().classes() not supported."); + } + + private Object readResolve() { + return getTypes().interfaces(); + } + + private static final long serialVersionUID = 0; + } + + private final class ClassSet extends TypeSet { + + private transient ImmutableSet<TypeToken<? super T>> classes; + + @Override protected Set<TypeToken<? super T>> delegate() { + ImmutableSet<TypeToken<? super T>> result = classes; + if (result == null) { + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableList<TypeToken<? super T>> collectedTypes = (ImmutableList) + TypeCollector.FOR_GENERIC_TYPE.classesOnly().collectTypes(TypeToken.this); + return (classes = FluentIterable.from(collectedTypes) + .filter(TypeFilter.IGNORE_TYPE_VARIABLE_OR_WILDCARD) + .toImmutableSet()); + } else { + return result; + } + } + + @Override public TypeSet classes() { + return this; + } + + @Override public Set<Class<? super T>> rawTypes() { + // Java has no way to express ? super T when we parameterize TypeToken vs. Class. + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableList<Class<? super T>> collectedTypes = (ImmutableList) + TypeCollector.FOR_RAW_TYPE.classesOnly().collectTypes(getImmediateRawTypes()); + return ImmutableSet.copyOf(collectedTypes); + } + + @Override public TypeSet interfaces() { + throw new UnsupportedOperationException("classes().interfaces() not supported."); + } + + private Object readResolve() { + return getTypes().classes(); + } + + private static final long serialVersionUID = 0; + } + + private enum TypeFilter implements Predicate<TypeToken<?>> { + + IGNORE_TYPE_VARIABLE_OR_WILDCARD { + @Override public boolean apply(TypeToken<?> type) { + return !(type.runtimeType instanceof TypeVariable + || type.runtimeType instanceof WildcardType); + } + }, + INTERFACE_ONLY { + @Override public boolean apply(TypeToken<?> type) { + return type.getRawType().isInterface(); + } + } + } + + /** + * Returns true if {@code o} is another {@code TypeToken} that represents the same {@link Type}. + */ + @Override public boolean equals(@Nullable Object o) { + if (o instanceof TypeToken) { + TypeToken<?> that = (TypeToken<?>) o; + return runtimeType.equals(that.runtimeType); + } + return false; + } + + @Override public int hashCode() { + return runtimeType.hashCode(); + } + + @Override public String toString() { + return Types.toString(runtimeType); + } + + /** Implemented to support serialization of subclasses. */ + protected Object writeReplace() { + // TypeResolver just transforms the type to our own impls that are Serializable + // except TypeVariable. + return of(new TypeResolver().resolveType(runtimeType)); + } + + /** + * Ensures that this type token doesn't contain type variables, which can cause unchecked type + * errors for callers like {@link TypeToInstanceMap}. + */ + final TypeToken<T> rejectTypeVariables() { + checkArgument(!Types.containsTypeVariable(runtimeType), + "%s contains a type variable and is not safe for the operation"); + return this; + } + + private static boolean isAssignable(Type from, Type to) { + if (to.equals(from)) { + return true; + } + if (to instanceof WildcardType) { + return isAssignableToWildcardType(from, (WildcardType) to); + } + // if "from" is type variable, it's assignable if any of its "extends" + // bounds is assignable to "to". + if (from instanceof TypeVariable) { + return isAssignableFromAny(((TypeVariable<?>) from).getBounds(), to); + } + // if "from" is wildcard, it'a assignable to "to" if any of its "extends" + // bounds is assignable to "to". + if (from instanceof WildcardType) { + return isAssignableFromAny(((WildcardType) from).getUpperBounds(), to); + } + if (from instanceof GenericArrayType) { + return isAssignableFromGenericArrayType((GenericArrayType) from, to); + } + // Proceed to regular Type assignability check + if (to instanceof Class) { + return isAssignableToClass(from, (Class<?>) to); + } else if (to instanceof ParameterizedType) { + return isAssignableToParameterizedType(from, (ParameterizedType) to); + } else if (to instanceof GenericArrayType) { + return isAssignableToGenericArrayType(from, (GenericArrayType) to); + } else { // to instanceof TypeVariable + return false; + } + } + + private static boolean isAssignableFromAny(Type[] fromTypes, Type to) { + for (Type from : fromTypes) { + if (isAssignable(from, to)) { + return true; + } + } + return false; + } + + private static boolean isAssignableToClass(Type from, Class<?> to) { + return to.isAssignableFrom(getRawType(from)); + } + + private static boolean isAssignableToWildcardType( + Type from, WildcardType to) { + // if "to" is <? extends Foo>, "from" can be: + // Foo, SubFoo, <? extends Foo>, <? extends SubFoo>, <T extends Foo> or + // <T extends SubFoo>. + // if "to" is <? super Foo>, "from" can be: + // Foo, SuperFoo, <? super Foo> or <? super SuperFoo>. + return isAssignable(from, supertypeBound(to)) && isAssignableBySubtypeBound(from, to); + } + + private static boolean isAssignableBySubtypeBound(Type from, WildcardType to) { + Type toSubtypeBound = subtypeBound(to); + if (toSubtypeBound == null) { + return true; + } + Type fromSubtypeBound = subtypeBound(from); + if (fromSubtypeBound == null) { + return false; + } + return isAssignable(toSubtypeBound, fromSubtypeBound); + } + + private static boolean isAssignableToParameterizedType(Type from, ParameterizedType to) { + Class<?> matchedClass = getRawType(to); + if (!matchedClass.isAssignableFrom(getRawType(from))) { + return false; + } + Type[] typeParams = matchedClass.getTypeParameters(); + Type[] toTypeArgs = to.getActualTypeArguments(); + TypeToken<?> fromTypeToken = of(from); + for (int i = 0; i < typeParams.length; i++) { + // If "to" is "List<? extends CharSequence>" + // and "from" is StringArrayList, + // First step is to figure out StringArrayList "is-a" List<E> and <E> is + // String. + // typeParams[0] is E and fromTypeToken.get(typeParams[0]) will resolve to + // String. + // String is then matched against <? extends CharSequence>. + Type fromTypeArg = fromTypeToken.resolveType(typeParams[i]).runtimeType; + if (!matchTypeArgument(fromTypeArg, toTypeArgs[i])) { + return false; + } + } + return true; + } + + private static boolean isAssignableToGenericArrayType(Type from, GenericArrayType to) { + if (from instanceof Class) { + Class<?> fromClass = (Class<?>) from; + if (!fromClass.isArray()) { + return false; + } + return isAssignable(fromClass.getComponentType(), to.getGenericComponentType()); + } else if (from instanceof GenericArrayType) { + GenericArrayType fromArrayType = (GenericArrayType) from; + return isAssignable(fromArrayType.getGenericComponentType(), to.getGenericComponentType()); + } else { + return false; + } + } + + private static boolean isAssignableFromGenericArrayType(GenericArrayType from, Type to) { + if (to instanceof Class) { + Class<?> toClass = (Class<?>) to; + if (!toClass.isArray()) { + return toClass == Object.class; // any T[] is assignable to Object + } + return isAssignable(from.getGenericComponentType(), toClass.getComponentType()); + } else if (to instanceof GenericArrayType) { + GenericArrayType toArrayType = (GenericArrayType) to; + return isAssignable(from.getGenericComponentType(), toArrayType.getGenericComponentType()); + } else { + return false; + } + } + + private static boolean matchTypeArgument(Type from, Type to) { + if (from.equals(to)) { + return true; + } + if (to instanceof WildcardType) { + return isAssignableToWildcardType(from, (WildcardType) to); + } + return false; + } + + private static Type supertypeBound(Type type) { + if (type instanceof WildcardType) { + return supertypeBound((WildcardType) type); + } + return type; + } + + private static Type supertypeBound(WildcardType type) { + Type[] upperBounds = type.getUpperBounds(); + if (upperBounds.length == 1) { + return supertypeBound(upperBounds[0]); + } else if (upperBounds.length == 0) { + return Object.class; + } else { + throw new AssertionError( + "There should be at most one upper bound for wildcard type: " + type); + } + } + + @Nullable private static Type subtypeBound(Type type) { + if (type instanceof WildcardType) { + return subtypeBound((WildcardType) type); + } else { + return type; + } + } + + @Nullable private static Type subtypeBound(WildcardType type) { + Type[] lowerBounds = type.getLowerBounds(); + if (lowerBounds.length == 1) { + return subtypeBound(lowerBounds[0]); + } else if (lowerBounds.length == 0) { + return null; + } else { + throw new AssertionError( + "Wildcard should have at most one lower bound: " + type); + } + } + + @VisibleForTesting static Class<?> getRawType(Type type) { + // For wildcard or type variable, the first bound determines the runtime type. + return getRawTypes(type).iterator().next(); + } + + @VisibleForTesting static ImmutableSet<Class<?>> getRawTypes(Type type) { + if (type instanceof Class) { + return ImmutableSet.<Class<?>>of((Class<?>) type); + } else if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + // JDK implementation declares getRawType() to return Class<?> + return ImmutableSet.<Class<?>>of((Class<?>) parameterizedType.getRawType()); + } else if (type instanceof GenericArrayType) { + GenericArrayType genericArrayType = (GenericArrayType) type; + return ImmutableSet.<Class<?>>of(Types.getArrayClass( + getRawType(genericArrayType.getGenericComponentType()))); + } else if (type instanceof TypeVariable) { + return getRawTypes(((TypeVariable<?>) type).getBounds()); + } else if (type instanceof WildcardType) { + return getRawTypes(((WildcardType) type).getUpperBounds()); + } else { + throw new AssertionError(type + " unsupported"); + } + } + + private static ImmutableSet<Class<?>> getRawTypes(Type[] types) { + ImmutableSet.Builder<Class<?>> builder = ImmutableSet.builder(); + for (Type type : types) { + builder.addAll(getRawTypes(type)); + } + return builder.build(); + } + + /** + * Returns the type token representing the generic type declaration of {@code cls}. For example: + * {@code TypeToken.getGenericType(Iterable.class)} returns {@code Iterable<T>}. + * + * <p>If {@code cls} isn't parameterized and isn't a generic array, the type token of the class is + * returned. + */ + @VisibleForTesting static <T> TypeToken<? extends T> toGenericType(Class<T> cls) { + if (cls.isArray()) { + Type arrayOfGenericType = Types.newArrayType( + // If we are passed with int[].class, don't turn it to GenericArrayType + toGenericType(cls.getComponentType()).runtimeType); + @SuppressWarnings("unchecked") // array is covariant + TypeToken<? extends T> result = (TypeToken<? extends T>) of(arrayOfGenericType); + return result; + } + TypeVariable<Class<T>>[] typeParams = cls.getTypeParameters(); + if (typeParams.length > 0) { + @SuppressWarnings("unchecked") // Like, it's Iterable<T> for Iterable.class + TypeToken<? extends T> type = (TypeToken<? extends T>) + of(Types.newParameterizedType(cls, typeParams)); + return type; + } else { + return of(cls); + } + } + + private TypeToken<? super T> getSupertypeFromUpperBounds( + Class<? super T> supertype, Type[] upperBounds) { + for (Type upperBound : upperBounds) { + @SuppressWarnings("unchecked") // T's upperbound is <? super T>. + TypeToken<? super T> bound = (TypeToken<? super T>) of(upperBound); + if (of(supertype).isAssignableFrom(bound)) { + @SuppressWarnings({"rawtypes", "unchecked"}) // guarded by the isAssignableFrom check. + TypeToken<? super T> result = bound.getSupertype((Class) supertype); + return result; + } + } + throw new IllegalArgumentException(supertype + " isn't a super type of " + this); + } + + private TypeToken<? extends T> getSubtypeFromLowerBounds(Class<?> subclass, Type[] lowerBounds) { + for (Type lowerBound : lowerBounds) { + @SuppressWarnings("unchecked") // T's lower bound is <? extends T> + TypeToken<? extends T> bound = (TypeToken<? extends T>) of(lowerBound); + // Java supports only one lowerbound anyway. + return bound.getSubtype(subclass); + } + throw new IllegalArgumentException(subclass + " isn't a subclass of " + this); + } + + private TypeToken<? super T> getArraySupertype(Class<? super T> supertype) { + // with component type, we have lost generic type information + // Use raw type so that compiler allows us to call getSupertype() + @SuppressWarnings("rawtypes") + TypeToken componentType = checkNotNull(getComponentType(), + "%s isn't a super type of %s", supertype, this); + // array is covariant. component type is super type, so is the array type. + @SuppressWarnings("unchecked") // going from raw type back to generics + TypeToken<?> componentSupertype = componentType.getSupertype(supertype.getComponentType()); + @SuppressWarnings("unchecked") // component type is super type, so is array type. + TypeToken<? super T> result = (TypeToken<? super T>) + // If we are passed with int[].class, don't turn it to GenericArrayType + of(newArrayClassOrGenericArrayType(componentSupertype.runtimeType)); + return result; + } + + private TypeToken<? extends T> getArraySubtype(Class<?> subclass) { + // array is covariant. component type is subtype, so is the array type. + TypeToken<?> componentSubtype = getComponentType() + .getSubtype(subclass.getComponentType()); + @SuppressWarnings("unchecked") // component type is subtype, so is array type. + TypeToken<? extends T> result = (TypeToken<? extends T>) + // If we are passed with int[].class, don't turn it to GenericArrayType + of(newArrayClassOrGenericArrayType(componentSubtype.runtimeType)); + return result; + } + + private Type resolveTypeArgsForSubclass(Class<?> subclass) { + if (runtimeType instanceof Class) { + // no resolution needed + return subclass; + } + // class Base<A, B> {} + // class Sub<X, Y> extends Base<X, Y> {} + // Base<String, Integer>.subtype(Sub.class): + + // Sub<X, Y>.getSupertype(Base.class) => Base<X, Y> + // => X=String, Y=Integer + // => Sub<X, Y>=Sub<String, Integer> + TypeToken<?> genericSubtype = toGenericType(subclass); + @SuppressWarnings({"rawtypes", "unchecked"}) // subclass isn't <? extends T> + Type supertypeWithArgsFromSubtype = genericSubtype + .getSupertype((Class) getRawType()) + .runtimeType; + return new TypeResolver().where(supertypeWithArgsFromSubtype, runtimeType) + .resolveType(genericSubtype.runtimeType); + } + + /** + * Creates an array class if {@code componentType} is a class, or else, a + * {@link GenericArrayType}. This is what Java7 does for generic array type + * parameters. + */ + private static Type newArrayClassOrGenericArrayType(Type componentType) { + return Types.JavaVersion.JAVA7.newArrayType(componentType); + } + + private static final class SimpleTypeToken<T> extends TypeToken<T> { + + SimpleTypeToken(Type type) { + super(type); + } + + private static final long serialVersionUID = 0; + } + + /** + * Collects parent types from a sub type. + * + * @param <K> The type "kind". Either a TypeToken, or Class. + */ + private abstract static class TypeCollector<K> { + + static final TypeCollector<TypeToken<?>> FOR_GENERIC_TYPE = + new TypeCollector<TypeToken<?>>() { + @Override Class<?> getRawType(TypeToken<?> type) { + return type.getRawType(); + } + + @Override Iterable<? extends TypeToken<?>> getInterfaces(TypeToken<?> type) { + return type.getGenericInterfaces(); + } + + @Nullable + @Override TypeToken<?> getSuperclass(TypeToken<?> type) { + return type.getGenericSuperclass(); + } + }; + + static final TypeCollector<Class<?>> FOR_RAW_TYPE = + new TypeCollector<Class<?>>() { + @Override Class<?> getRawType(Class<?> type) { + return type; + } + + @Override Iterable<? extends Class<?>> getInterfaces(Class<?> type) { + return Arrays.asList(type.getInterfaces()); + } + + @Nullable + @Override Class<?> getSuperclass(Class<?> type) { + return type.getSuperclass(); + } + }; + + /** For just classes, we don't have to traverse interfaces. */ + final TypeCollector<K> classesOnly() { + return new ForwardingTypeCollector<K>(this) { + @Override Iterable<? extends K> getInterfaces(K type) { + return ImmutableSet.of(); + } + @Override ImmutableList<K> collectTypes(Iterable<? extends K> types) { + ImmutableList.Builder<K> builder = ImmutableList.builder(); + for (K type : types) { + if (!getRawType(type).isInterface()) { + builder.add(type); + } + } + return super.collectTypes(builder.build()); + } + }; + } + + final ImmutableList<K> collectTypes(K type) { + return collectTypes(ImmutableList.of(type)); + } + + ImmutableList<K> collectTypes(Iterable<? extends K> types) { + // type -> order number. 1 for Object, 2 for anything directly below, so on so forth. + Map<K, Integer> map = Maps.newHashMap(); + for (K type : types) { + collectTypes(type, map); + } + return sortKeysByValue(map, Ordering.natural().reverse()); + } + + /** Collects all types to map, and returns the total depth from T up to Object. */ + private int collectTypes(K type, Map<? super K, Integer> map) { + Integer existing = map.get(this); + if (existing != null) { + // short circuit: if set contains type it already contains its supertypes + return existing; + } + int aboveMe = getRawType(type).isInterface() + ? 1 // interfaces should be listed before Object + : 0; + for (K interfaceType : getInterfaces(type)) { + aboveMe = Math.max(aboveMe, collectTypes(interfaceType, map)); + } + K superclass = getSuperclass(type); + if (superclass != null) { + aboveMe = Math.max(aboveMe, collectTypes(superclass, map)); + } + // TODO(benyu): should we include Object for interface? + // Also, CharSequence[] and Object[] for String[]? + map.put(type, aboveMe + 1); + return aboveMe + 1; + } + + private static <K, V> ImmutableList<K> sortKeysByValue( + final Map<K, V> map, final Comparator<? super V> valueComparator) { + Ordering<K> keyOrdering = new Ordering<K>() { + @Override public int compare(K left, K right) { + return valueComparator.compare(map.get(left), map.get(right)); + } + }; + return keyOrdering.immutableSortedCopy(map.keySet()); + } + + abstract Class<?> getRawType(K type); + abstract Iterable<? extends K> getInterfaces(K type); + @Nullable abstract K getSuperclass(K type); + + private static class ForwardingTypeCollector<K> extends TypeCollector<K> { + + private final TypeCollector<K> delegate; + + ForwardingTypeCollector(TypeCollector<K> delegate) { + this.delegate = delegate; + } + + @Override Class<?> getRawType(K type) { + return delegate.getRawType(type); + } + + @Override Iterable<? extends K> getInterfaces(K type) { + return delegate.getInterfaces(type); + } + + @Override K getSuperclass(K type) { + return delegate.getSuperclass(type); + } + } + } +} diff --git a/guava/src/com/google/common/reflect/Types.java b/guava/src/com/google/common/reflect/Types.java new file mode 100644 index 0000000..19264d3 --- /dev/null +++ b/guava/src/com/google/common/reflect/Types.java @@ -0,0 +1,518 @@ +/* + * 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 com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.transform; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Objects; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; + +import java.io.Serializable; +import java.lang.reflect.Array; +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.Collection; + +import javax.annotation.Nullable; + +/** + * Utilities for working with {@link Type}. + * + * @author Ben Yu + */ +final class Types { + + /** Class#toString without the "class " and "interface " prefixes */ + private static final Function<Type, String> TYPE_TO_STRING = + new Function<Type, String>() { + @Override public String apply(Type from) { + return Types.toString(from); + } + }; + + private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null"); + + /** Returns the array type of {@code componentType}. */ + static Type newArrayType(Type componentType) { + if (componentType instanceof WildcardType) { + WildcardType wildcard = (WildcardType) componentType; + Type[] lowerBounds = wildcard.getLowerBounds(); + checkArgument(lowerBounds.length <= 1, "Wildcard cannot have more than one lower bounds."); + if (lowerBounds.length == 1) { + return supertypeOf(newArrayType(lowerBounds[0])); + } else { + Type[] upperBounds = wildcard.getUpperBounds(); + checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound."); + return subtypeOf(newArrayType(upperBounds[0])); + } + } + return JavaVersion.CURRENT.newArrayType(componentType); + } + + /** + * Returns a type where {@code rawType} is parameterized by + * {@code arguments} and is owned by {@code ownerType}. + */ + static ParameterizedType newParameterizedTypeWithOwner( + @Nullable Type ownerType, Class<?> rawType, Type... arguments) { + if (ownerType == null) { + return newParameterizedType(rawType, arguments); + } + // ParameterizedTypeImpl constructor already checks, but we want to throw NPE before IAE + checkNotNull(arguments); + checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType); + return new ParameterizedTypeImpl(ownerType, rawType, arguments); + } + + /** + * Returns a type where {@code rawType} is parameterized by + * {@code arguments}. + */ + static ParameterizedType newParameterizedType(Class<?> rawType, Type... arguments) { + return new ParameterizedTypeImpl( + ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments); + } + + /** Decides what owner type to use for constructing {@link ParameterizedType} from a raw class. */ + private enum ClassOwnership { + + OWNED_BY_ENCLOSING_CLASS { + @Nullable + @Override + Class<?> getOwnerType(Class<?> rawType) { + return rawType.getEnclosingClass(); + } + }, + LOCAL_CLASS_HAS_NO_OWNER { + @Nullable + @Override + Class<?> getOwnerType(Class<?> rawType) { + if (rawType.isLocalClass()) { + return null; + } else { + return rawType.getEnclosingClass(); + } + } + }; + + @Nullable abstract Class<?> getOwnerType(Class<?> rawType); + + static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior(); + + private static ClassOwnership detectJvmBehavior() { + class LocalClass<T> {} + Class<?> subclass = new LocalClass<String>() {}.getClass(); + ParameterizedType parameterizedType = (ParameterizedType) + subclass.getGenericSuperclass(); + for (ClassOwnership behavior : ClassOwnership.values()) { + if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) { + return behavior; + } + } + throw new AssertionError(); + } + } + + /** + * Returns a new {@link TypeVariable} that belongs to {@code declaration} with + * {@code name} and {@code bounds}. + */ + static <D extends GenericDeclaration> TypeVariable<D> newTypeVariable( + D declaration, String name, Type... bounds) { + return new TypeVariableImpl<D>( + declaration, + name, + (bounds.length == 0) + ? new Type[] { Object.class } + : bounds); + } + + /** Returns a new {@link WildcardType} with {@code upperBound}. */ + @VisibleForTesting static WildcardType subtypeOf(Type upperBound) { + return new WildcardTypeImpl(new Type[0], new Type[] { upperBound }); + } + + /** Returns a new {@link WildcardType} with {@code lowerBound}. */ + @VisibleForTesting static WildcardType supertypeOf(Type lowerBound) { + return new WildcardTypeImpl(new Type[] { lowerBound }, new Type[] { Object.class }); + } + + /** + * Returns human readable string representation of {@code type}. + * <ul> + * <li> For array type {@code Foo[]}, {@code "com.mypackage.Foo[]"} are + * returned. + * <li> For any class, {@code theClass.getName()} are returned. + * <li> For all other types, {@code type.toString()} are returned. + * </ul> + */ + static String toString(Type type) { + return (type instanceof Class) + ? ((Class<?>) type).getName() + : type.toString(); + } + + @Nullable static Type getComponentType(Type type) { + checkNotNull(type); + if (type instanceof Class) { + return ((Class<?>) type).getComponentType(); + } else if (type instanceof GenericArrayType) { + return ((GenericArrayType) type).getGenericComponentType(); + } else if (type instanceof WildcardType) { + return subtypeOfComponentType(((WildcardType) type).getUpperBounds()); + } else if (type instanceof TypeVariable) { + return subtypeOfComponentType(((TypeVariable<?>) type).getBounds()); + } else { + return null; + } + } + + /** + * Returns {@code ? extends X} if any of {@code bounds} is a subtype of {@code X[]}; or null + * otherwise. + */ + @Nullable private static Type subtypeOfComponentType(Type[] bounds) { + for (Type bound : bounds) { + Type componentType = getComponentType(bound); + if (componentType != null) { + // Only the first bound can be a class or array. + // Bounds after the first can only be interfaces. + if (componentType instanceof Class) { + Class<?> componentClass = (Class<?>) componentType; + if (componentClass.isPrimitive()) { + return componentClass; + } + } + return subtypeOf(componentType); + } + } + return null; + } + + static boolean containsTypeVariable(@Nullable Type type) { + if (type instanceof TypeVariable) { + return true; + } + if (type instanceof GenericArrayType) { + return containsTypeVariable(((GenericArrayType) type).getGenericComponentType()); + } + if (type instanceof ParameterizedType) { + return containsTypeVariable(((ParameterizedType) type).getActualTypeArguments()); + } + if (type instanceof WildcardType) { + WildcardType wildcard = (WildcardType) type; + return containsTypeVariable(wildcard.getUpperBounds()) + || containsTypeVariable(wildcard.getLowerBounds()); + } + return false; + } + + private static boolean containsTypeVariable(Type[] types) { + for (Type paramType : types) { + if (containsTypeVariable(paramType)) { + return true; + } + } + return false; + } + + private static final class GenericArrayTypeImpl + implements GenericArrayType, Serializable { + + private final Type componentType; + + GenericArrayTypeImpl(Type componentType) { + this.componentType = JavaVersion.CURRENT.usedInGenericType(componentType); + } + + @Override public Type getGenericComponentType() { + return componentType; + } + + @Override public String toString() { + return Types.toString(componentType) + "[]"; + } + + @Override public int hashCode() { + return componentType.hashCode(); + } + + @Override public boolean equals(Object obj) { + if (obj instanceof GenericArrayType) { + GenericArrayType that = (GenericArrayType) obj; + return Objects.equal( + getGenericComponentType(), that.getGenericComponentType()); + } + return false; + } + + private static final long serialVersionUID = 0; + } + + private static final class ParameterizedTypeImpl + implements ParameterizedType, Serializable { + + private final Type ownerType; + private final ImmutableList<Type> argumentsList; + private final Class<?> rawType; + + ParameterizedTypeImpl( + @Nullable Type ownerType, Class<?> rawType, Type[] typeArguments) { + checkNotNull(rawType); + checkArgument(typeArguments.length == rawType.getTypeParameters().length); + disallowPrimitiveType(typeArguments, "type parameter"); + this.ownerType = ownerType; + this.rawType = rawType; + this.argumentsList = JavaVersion.CURRENT.usedInGenericType(typeArguments); + } + + @Override public Type[] getActualTypeArguments() { + return toArray(argumentsList); + } + + @Override public Type getRawType() { + return rawType; + } + + @Override public Type getOwnerType() { + return ownerType; + } + + @Override public String toString() { + StringBuilder builder = new StringBuilder(); + if (ownerType != null) { + builder.append(Types.toString(ownerType)).append('.'); + } + builder.append(rawType.getName()) + .append('<') + .append(COMMA_JOINER.join(transform(argumentsList, TYPE_TO_STRING))) + .append('>'); + return builder.toString(); + } + + @Override public int hashCode() { + return (ownerType == null ? 0 : ownerType.hashCode()) + ^ argumentsList.hashCode() ^ rawType.hashCode(); + } + + @Override public boolean equals(Object other) { + if (!(other instanceof ParameterizedType)) { + return false; + } + ParameterizedType that = (ParameterizedType) other; + return getRawType().equals(that.getRawType()) + && Objects.equal(getOwnerType(), that.getOwnerType()) + && Arrays.equals( + getActualTypeArguments(), that.getActualTypeArguments()); + } + + private static final long serialVersionUID = 0; + } + + private static final class TypeVariableImpl<D extends GenericDeclaration> + implements TypeVariable<D> { + + private final D genericDeclaration; + private final String name; + private final ImmutableList<Type> bounds; + + TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) { + disallowPrimitiveType(bounds, "bound for type variable"); + this.genericDeclaration = checkNotNull(genericDeclaration); + this.name = checkNotNull(name); + this.bounds = ImmutableList.copyOf(bounds); + } + + @Override public Type[] getBounds() { + return toArray(bounds); + } + + @Override public D getGenericDeclaration() { + return genericDeclaration; + } + + @Override public String getName() { + return name; + } + + @Override public String toString() { + return name; + } + + @Override public int hashCode() { + return genericDeclaration.hashCode() ^ name.hashCode(); + } + + @Override public boolean equals(Object obj) { + if (obj instanceof TypeVariable) { + TypeVariable<?> that = (TypeVariable<?>) obj; + return name.equals(that.getName()) + && genericDeclaration.equals(that.getGenericDeclaration()); + } + return false; + } + } + + static final class WildcardTypeImpl implements WildcardType, Serializable { + + private final ImmutableList<Type> lowerBounds; + private final ImmutableList<Type> upperBounds; + + WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) { + disallowPrimitiveType(lowerBounds, "lower bound for wildcard"); + disallowPrimitiveType(upperBounds, "upper bound for wildcard"); + this.lowerBounds = JavaVersion.CURRENT.usedInGenericType(lowerBounds); + this.upperBounds = JavaVersion.CURRENT.usedInGenericType(upperBounds); + } + + @Override public Type[] getLowerBounds() { + return toArray(lowerBounds); + } + + @Override public Type[] getUpperBounds() { + return toArray(upperBounds); + } + + @Override public boolean equals(Object obj) { + if (obj instanceof WildcardType) { + WildcardType that = (WildcardType) obj; + return lowerBounds.equals(Arrays.asList(that.getLowerBounds())) + && upperBounds.equals(Arrays.asList(that.getUpperBounds())); + } + return false; + } + + @Override public int hashCode() { + return lowerBounds.hashCode() ^ upperBounds.hashCode(); + } + + @Override public String toString() { + StringBuilder builder = new StringBuilder("?"); + for (Type lowerBound : lowerBounds) { + builder.append(" super ").append(Types.toString(lowerBound)); + } + for (Type upperBound : filterUpperBounds(upperBounds)) { + builder.append(" extends ").append(Types.toString(upperBound)); + } + return builder.toString(); + } + + private static final long serialVersionUID = 0; + } + + private static Type[] toArray(Collection<Type> types) { + return types.toArray(new Type[types.size()]); + } + + private static Iterable<Type> filterUpperBounds(Iterable<Type> bounds) { + return Iterables.filter( + bounds, Predicates.not(Predicates.<Type>equalTo(Object.class))); + } + + private static void disallowPrimitiveType(Type[] types, String usedAs) { + for (Type type : types) { + if (type instanceof Class) { + Class<?> cls = (Class<?>) type; + checkArgument(!cls.isPrimitive(), + "Primitive type '%s' used as %s", cls, usedAs); + } + } + } + + static IllegalArgumentException buildUnexpectedTypeException( + Type type, Class<?>... expected) { + // Build exception message + StringBuilder exceptionMessage = + new StringBuilder("Unexpected type. Expected one of: "); + for (Class<?> clazz : expected) { + exceptionMessage.append(clazz.getName()).append(", "); + } + exceptionMessage.append("but got: ").append(type.getClass().getName()) + .append(", for type: ").append(toString(type)).append('.'); + + return new IllegalArgumentException(exceptionMessage.toString()); + } + + /** Returns the {@code Class} object of arrays with {@code componentType}. */ + static Class<?> getArrayClass(Class<?> componentType) { + // TODO(user): This is not the most efficient way to handle generic + // arrays, but is there another way to extract the array class in a + // non-hacky way (i.e. using String value class names- "[L...")? + return Array.newInstance(componentType, 0).getClass(); + } + + // TODO(benyu): Once we are on Java 7, delete this abstraction + enum JavaVersion { + + JAVA6 { + @Override GenericArrayType newArrayType(Type componentType) { + return new GenericArrayTypeImpl(componentType); + } + @Override Type usedInGenericType(Type type) { + checkNotNull(type); + if (type instanceof Class) { + Class<?> cls = (Class<?>) type; + if (cls.isArray()) { + return new GenericArrayTypeImpl(cls.getComponentType()); + } + } + return type; + } + }, + JAVA7 { + @Override Type newArrayType(Type componentType) { + if (componentType instanceof Class) { + return getArrayClass((Class<?>) componentType); + } else { + return new GenericArrayTypeImpl(componentType); + } + } + @Override Type usedInGenericType(Type type) { + return checkNotNull(type); + } + } + ; + + static final JavaVersion CURRENT = + (new TypeCapture<int[]>() {}.capture() instanceof Class) + ? JAVA7 : JAVA6; + abstract Type newArrayType(Type componentType); + abstract Type usedInGenericType(Type type); + + final ImmutableList<Type> usedInGenericType(Type[] types) { + ImmutableList.Builder<Type> builder = ImmutableList.builder(); + for (Type type : types) { + builder.add(usedInGenericType(type)); + } + return builder.build(); + } + } + + private Types() {} +} diff --git a/guava/src/com/google/common/reflect/package-info.java b/guava/src/com/google/common/reflect/package-info.java new file mode 100644 index 0000000..e8ac02a --- /dev/null +++ b/guava/src/com/google/common/reflect/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2012 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. + */ + +/** + * This package contains utilities to work with Java reflection. + * It is a part of the open-source + * <a href="http://guava-libraries.googlecode.com">Guava libraries</a>. + */ +@javax.annotation.ParametersAreNonnullByDefault +package com.google.common.reflect; |