13c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin/*
23c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * Copyright (C) 2014 The Android Open Source Project
33c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *
43c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * Licensed under the Apache License, Version 2.0 (the "License");
53c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * you may not use this file except in compliance with the License.
63c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * You may obtain a copy of the License at
73c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *
83c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *      http://www.apache.org/licenses/LICENSE-2.0
93c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *
103c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * Unless required by applicable law or agreed to in writing, software
113c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * distributed under the License is distributed on an "AS IS" BASIS,
123c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * See the License for the specific language governing permissions and
143c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * limitations under the License.
153c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin */
163c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
173c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinpackage android.hardware.camera2.utils;
183c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
193c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinimport java.lang.reflect.Array;
203c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinimport java.lang.reflect.GenericArrayType;
213c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinimport java.lang.reflect.ParameterizedType;
223c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinimport java.lang.reflect.Type;
233c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinimport java.lang.reflect.TypeVariable;
243c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinimport java.lang.reflect.WildcardType;
253c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
263c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinimport static com.android.internal.util.Preconditions.*;
273c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
283c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin/**
293c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * Super type token; allows capturing generic types at runtime by forcing them to be reified.
303c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *
313c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * <p>Usage example: <pre>{@code
323c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *      // using anonymous classes (preferred)
333c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *      TypeReference&lt;Integer> intToken = new TypeReference&lt;Integer>() {{ }};
343c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *
353c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *      // using named classes
363c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *      class IntTypeReference extends TypeReference&lt;Integer> {...}
373c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *      TypeReference&lt;Integer> intToken = new IntTypeReference();
383c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * }</p></pre>
393c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *
403c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * <p>Unlike the reference implementation, this bans nested TypeVariables; that is all
413c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * dynamic types must equal to the static types.</p>
423c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin *
433c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * <p>See <a href="http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html">
443c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html</a>
453c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin * for more details.</p>
463c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin */
473c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinpublic abstract class TypeReference<T> {
483c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private final Type mType;
492cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk    private final int mHash;
503c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
513c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
523c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * Create a new type reference for {@code T}.
533c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
543c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @throws IllegalArgumentException if {@code T}'s actual type contains a type variable
553c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
563c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @see TypeReference
573c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
583c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    protected TypeReference() {
593c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        ParameterizedType thisType = (ParameterizedType)getClass().getGenericSuperclass();
603c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
613c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        // extract the "T" from TypeReference<T>
623c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        mType = thisType.getActualTypeArguments()[0];
633c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
643c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        /*
653c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin         * Prohibit type references with type variables such as
663c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin         *
673c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin         *    class GenericListToken<T> extends TypeReference<List<T>>
683c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin         *
693c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin         * Since the "T" there is not known without an instance of T, type equality would
703c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin         * consider *all* Lists equal regardless of T. Allowing this would defeat
713c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin         * some of the type safety of a type reference.
723c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin         */
733c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        if (containsTypeVariable(mType)) {
743c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            throw new IllegalArgumentException(
753c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                    "Including a type variable in a type reference is not allowed");
763c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
772cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk        mHash = mType.hashCode();
783c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
793c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
803c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
813c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * Return the dynamic {@link Type} corresponding to the captured type {@code T}.
823c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
833c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    public Type getType() {
843c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return mType;
853c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
863c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
873c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private TypeReference(Type type) {
883c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        mType = type;
893c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        if (containsTypeVariable(mType)) {
903c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            throw new IllegalArgumentException(
913c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                    "Including a type variable in a type reference is not allowed");
923c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
932cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk        mHash = mType.hashCode();
943c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
953c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
963c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private static class SpecializedTypeReference<T> extends TypeReference<T> {
973c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        public SpecializedTypeReference(Class<T> klass) {
983c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            super(klass);
993c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
1003c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
1013c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
1023c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    @SuppressWarnings("rawtypes")
1033c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private static class SpecializedBaseTypeReference extends TypeReference {
1043c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        public SpecializedBaseTypeReference(Type type) {
1053c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            super(type);
1063c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
1073c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
1083c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
1093c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
1103c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * Create a specialized type reference from a dynamic class instance,
1113c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * bypassing the standard compile-time checks.
1123c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
1133c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <p>As with a regular type reference, the {@code klass} must not contain
1143c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * any type variables.</p>
1153c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
1163c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @param klass a non-{@code null} {@link Class} instance
1173c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
1183c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @return a type reference which captures {@code T} at runtime
1193c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
1203c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @throws IllegalArgumentException if {@code T} had any type variables
1213c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
1223c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    public static <T> TypeReference<T> createSpecializedTypeReference(Class<T> klass) {
1233c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return new SpecializedTypeReference<T>(klass);
1243c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
1253c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
1263c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
1273c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * Create a specialized type reference from a dynamic {@link Type} instance,
1283c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * bypassing the standard compile-time checks.
1293c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
1303c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <p>As with a regular type reference, the {@code type} must not contain
1313c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * any type variables.</p>
1323c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
1333c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @param type a non-{@code null} {@link Type} instance
1343c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
1353c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @return a type reference which captures {@code T} at runtime
1363c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
1373c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @throws IllegalArgumentException if {@code type} had any type variables
1383c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
1393c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    public static TypeReference<?> createSpecializedTypeReference(Type type) {
1403c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return new SpecializedBaseTypeReference(type);
1413c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
1423c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
1433c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
1443c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * Returns the raw type of T.
1453c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
1463c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <p><ul>
1473c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <li>If T is a Class itself, T itself is returned.
1483c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <li>If T is a ParameterizedType, the raw type of the parameterized type is returned.
1493c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <li>If T is a GenericArrayType, the returned type is the corresponding array class.
1503c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * For example: {@code List<Integer>[]} => {@code List[]}.
1513c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <li>If T is a type variable or a wildcard type, the raw type of the first upper bound is
1523c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * returned. For example: {@code <X extends Foo>} => {@code Foo}.
1533c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * </ul>
1543c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
1553c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @return the raw type of {@code T}
1563c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
1573c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    @SuppressWarnings("unchecked")
1583c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    public final Class<? super T> getRawType() {
1593c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return (Class<? super T>)getRawType(mType);
1603c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
1613c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
1623c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private static final Class<?> getRawType(Type type) {
1633c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        if (type == null) {
1643c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            throw new NullPointerException("type must not be null");
1653c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
1663c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
1673c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        if (type instanceof Class<?>) {
1683c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return (Class<?>)type;
1693c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof ParameterizedType) {
1703c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return (Class<?>)(((ParameterizedType)type).getRawType());
1713c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof GenericArrayType) {
1723c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return getArrayClass(getRawType(((GenericArrayType)type).getGenericComponentType()));
1733c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof WildcardType) {
1743c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            // Should be at most 1 upper bound, but treat it like an array for simplicity
1753c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return getRawType(((WildcardType) type).getUpperBounds());
1763c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof TypeVariable) {
1773c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            throw new AssertionError("Type variables are not allowed in type references");
1783c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else {
1793c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            // Impossible
1803c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            throw new AssertionError("Unhandled branch to get raw type for type " + type);
1813c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
1823c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
1833c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
1843c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private static final Class<?> getRawType(Type[] types) {
1853c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        if (types == null) {
1863c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return null;
1873c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
1883c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
1893c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        for (Type type : types) {
1903c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            Class<?> klass = getRawType(type);
1913c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            if (klass !=  null) {
1923c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                return klass;
1933c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            }
1943c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
1953c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
1963c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return null;
1973c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
1983c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
1993c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private static final Class<?> getArrayClass(Class<?> componentType) {
2003c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return Array.newInstance(componentType, 0).getClass();
2013c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
2023c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
2033c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
2043c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * Get the component type, e.g. {@code T} from {@code T[]}.
2053c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
2063c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @return component type, or {@code null} if {@code T} is not an array
2073c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
2083c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    public TypeReference<?> getComponentType() {
2093c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        Type componentType = getComponentType(mType);
2103c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
2113c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return (componentType != null) ?
2123c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                createSpecializedTypeReference(componentType) :
2133c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                null;
2143c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
2153c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
2163c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private static Type getComponentType(Type type) {
2173c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        checkNotNull(type, "type must not be null");
2183c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
2193c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        if (type instanceof Class<?>) {
2203c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return ((Class<?>) type).getComponentType();
2213c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof ParameterizedType) {
2223c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return null;
2233c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof GenericArrayType) {
2243c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return ((GenericArrayType)type).getGenericComponentType();
2253c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof WildcardType) {
2263c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            // Should be at most 1 upper bound, but treat it like an array for simplicity
2273c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            throw new UnsupportedOperationException("TODO: support wild card components");
2283c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof TypeVariable) {
2293c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            throw new AssertionError("Type variables are not allowed in type references");
2303c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else {
2313c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            // Impossible
2323c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            throw new AssertionError("Unhandled branch to get component type for type " + type);
2333c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
2343c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
2353c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
2363c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
2373c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * Compare two objects for equality.
2383c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
2393c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <p>A TypeReference is only equal to another TypeReference if their captured type {@code T}
2403c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * is also equal.</p>
2413c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
2423c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    @Override
2433c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    public boolean equals(Object o) {
2443c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        // Note that this comparison could inaccurately return true when comparing types
2453c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        // with nested type variables; therefore we ban type variables in the constructor.
2463c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return o instanceof TypeReference<?> && mType.equals(((TypeReference<?>)o).mType);
2473c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
2483c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
2493c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
2503c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * {@inheritDoc}
2513c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
2523c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    @Override
2533c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    public int hashCode() {
2542cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk        return mHash;
2553c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
2563c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
2573c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
2583c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * Check if the {@code type} contains a {@link TypeVariable} recursively.
2593c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
2603c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <p>Intuitively, a type variable is a type in a type expression that refers to a generic
2613c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * type which is not known at the definition of the expression (commonly seen when
2623c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * type parameters are used, e.g. {@code class Foo<T>}).</p>
2633c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
2643c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <p>See <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4">
2653c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4</a>
2663c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * for a more formal definition of a type variable</p>.
2673c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
2683c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @param type a type object ({@code null} is allowed)
2693c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @return {@code true} if there were nested type variables; {@code false} otherwise
2703c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
2713c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    public static boolean containsTypeVariable(Type type) {
2723c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        if (type == null) {
2733c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            // Trivially false
2743c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return false;
2753c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof TypeVariable<?>) {
2763c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            /*
2773c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             * T -> trivially true
2783c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             */
2793c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return true;
2803c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof Class<?>) {
2813c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            /*
2823c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             * class Foo -> no type variable
2833c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             * class Foo<T> - has a type variable
2843c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *
2853c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             * This also covers the case of class Foo<T> extends ... / implements ...
2863c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             * since everything on the right hand side would either include a type variable T
2873c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             * or have no type variables.
2883c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             */
2893c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            Class<?> klass = (Class<?>)type;
2903c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
2913c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            // Empty array => class is not generic
2923c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            if (klass.getTypeParameters().length != 0) {
2933c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                return true;
2943c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            } else {
2953c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                // Does the outer class(es) contain any type variables?
2963c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
2973c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                /*
2983c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                 * class Outer<T> {
2993c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                 *   class Inner {
3003c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                 *      T field;
3013c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                 *   }
3023c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                 * }
3033c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                 *
3043c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                 * In this case 'Inner' has no type parameters itself, but it still has a type
3053c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                 * variable as part of the type definition.
3063c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                 */
3073c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                return containsTypeVariable(klass.getDeclaringClass());
3083c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            }
3093c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof ParameterizedType) {
3103c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            /*
3113c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             * This is the "Foo<T1, T2, T3, ... Tn>" in the scope of a
3123c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *
3133c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *      // no type variables here, T1-Tn are known at this definition
3143c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *      class X extends Foo<T1, T2, T3, ... Tn>
3153c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *
3163c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *      // T1 is a type variable, T2-Tn are known at this definition
3173c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *      class X<T1> extends Foo<T1, T2, T3, ... Tn>
3183c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             */
3193c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            ParameterizedType p = (ParameterizedType) type;
3203c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3213c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            // This needs to be recursively checked
3223c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            for (Type arg : p.getActualTypeArguments()) {
3233c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                if (containsTypeVariable(arg)) {
3243c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                    return true;
3253c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                }
3263c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            }
3273c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3283c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return false;
3293c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof WildcardType) {
3303c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            WildcardType wild = (WildcardType) type;
3313c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3323c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            /*
3333c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             * This is is the "?" inside of a
3343c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *
3353c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *       Foo<?> --> unbounded; trivially no type variables
3363c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *       Foo<? super T> --> lower bound; does T have a type variable?
3373c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *       Foo<? extends T> --> upper bound; does T have a type variable?
3383c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             */
3393c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3403c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            /*
3413c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *  According to JLS 4.5.1
3423c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *  (http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.5.1):
3433c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *
3443c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *  - More than 1 lower/upper bound is illegal
3453c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *  - Both a lower and upper bound is illegal
3463c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *
3473c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             *  However, we use this 'array OR array' approach for readability
3483c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             */
3493c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return containsTypeVariable(wild.getLowerBounds()) ||
3503c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                    containsTypeVariable(wild.getUpperBounds());
3513c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
3523c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3533c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return false;
3543c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
3553c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3563c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
3573c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * {@inheritDoc}
3583c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
3593c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    @Override
3603c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    public String toString() {
3613c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        StringBuilder builder = new StringBuilder();
3623c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        builder.append("TypeReference<");
3633c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        toString(getType(), builder);
3643c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        builder.append(">");
3653c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3663c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return builder.toString();
3673c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
3683c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3693c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private static void toString(Type type, StringBuilder out) {
3703c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        if (type == null) {
3713c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return;
3723c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof TypeVariable<?>) {
3733c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            // T
3743c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            out.append(((TypeVariable<?>)type).getName());
3753c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof Class<?>) {
3763c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            Class<?> klass = (Class<?>)type;
3773c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3783c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            out.append(klass.getName());
3793c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            toString(klass.getTypeParameters(), out);
3803c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof ParameterizedType) {
3813c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin             // "Foo<T1, T2, T3, ... Tn>"
3823c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            ParameterizedType p = (ParameterizedType) type;
3833c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3843c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            out.append(((Class<?>)p.getRawType()).getName());
3853c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            toString(p.getActualTypeArguments(), out);
3863c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (type instanceof GenericArrayType) {
3873c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            GenericArrayType gat = (GenericArrayType)type;
3883c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3893c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            toString(gat.getGenericComponentType(), out);
3903c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            out.append("[]");
3913c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else { // WildcardType, BoundedType
3923c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            // TODO:
3933c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            out.append(type.toString());
3943c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
3953c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
3963c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
3973c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private static void toString(Type[] types, StringBuilder out) {
3983c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        if (types == null) {
3993c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return;
4003c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        } else if (types.length == 0) {
4013c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return;
4023c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
4033c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
4043c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        out.append("<");
4053c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
4063c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        for (int i = 0; i < types.length; ++i) {
4073c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            toString(types[i], out);
4083c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            if (i != types.length - 1) {
4093c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                out.append(", ");
4103c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            }
4113c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
4123c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
4133c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        out.append(">");
4143c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
4153c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
4163c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    /**
4173c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * Check if any of the elements in this array contained a type variable.
4183c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
4193c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * <p>Empty and null arrays trivially have no type variables.</p>
4203c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     *
4213c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @param typeArray an array ({@code null} is ok) of types
4223c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     * @return true if any elements contained a type variable; false otherwise
4233c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin     */
4243c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    private static boolean containsTypeVariable(Type[] typeArray) {
4253c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        if (typeArray == null) {
4263c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            return false;
4273c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
4283c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
4293c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        for (Type type : typeArray) {
4303c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            if (containsTypeVariable(type)) {
4313c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin                return true;
4323c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin            }
4333c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        }
4343c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin
4353c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin        return false;
4363c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin    }
4373c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkin}
438