    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005//     http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5.commons.services;
    014
    015import java.lang.reflect.Field;
    016import java.lang.reflect.Method;
    017import java.lang.reflect.Type;
    018import java.util.Iterator;
    019import java.util.ServiceLoader;
    020
    021import org.apache.tapestry5.commons.internal.services.GenericsResolverImpl;
    022
    023/**
    024 * <p>Methods related to the use of Java 5+ generics.
    025 * Instances should be obtained through {@link GenericsResolver.Provider#getInstance()}.</p>
    026 *
    027 * <p>
    028 * If you have exceptions or bad results with classes using Generics, such as exceptions
    029 * or missing BeanModel properties,
    030 * you should try adding the <code>genericsresolver-guava</code> Tapestry subproject to our classpath.
    031 * </p>
    032 *
    033 * @since 5.5.0
    034 */
    035@SuppressWarnings("unchecked")
    036public interface GenericsResolver
    037{
    038    /**
    039     * Analyzes the method in the context of containingClass and returns the Class that is represented by
    040     * the method's generic return type. Any parameter information in the generic return type is lost. If you want
    041     * to preserve the type parameters of the return type consider using
    042     * {@link #extractActualType(java.lang.reflect.Type, java.lang.reflect.Method)}.
    043     *
    044     * @param containingClass class which either contains or inherited the method
    045     * @param method          method from which to extract the return type
    046     * @return the class represented by the methods generic return type, resolved based on the context .
    047     * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
    048     * @see #asClass(java.lang.reflect.Type)
    049     */
    050    Class<?> extractGenericReturnType(Class<?> containingClass, Method method);
    051
    052    /**
    053     * Analyzes the field in the context of containingClass and returns the Class that is represented by
    054     * the field's generic type. Any parameter information in the generic type is lost, if you want
    055     * to preserve the type parameters of the return type consider using
    056     * #getTypeVariableIndex(java.lang.reflect.TypeVariable).
    057     *
    058     * @param containingClass class which either contains or inherited the field
    059     * @param field           field from which to extract the type
    060     * @return the class represented by the field's generic type, resolved based on the containingClass.
    061     * @see #extractActualType(java.lang.reflect.Type, java.lang.reflect.Field)
    062     * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
    063     * @see #asClass(java.lang.reflect.Type)
    064     */
    065    Class extractGenericFieldType(Class containingClass, Field field);
    066
    067    /**
    068     * Analyzes the method in the context of containingClass and returns the Class that is represented by
    069     * the method's generic return type. Any parameter information in the generic return type is lost.
    070     *
    071     * @param containingType Type which is/represents the class that either contains or inherited the method
    072     * @param method         method from which to extract the generic return type
    073     * @return the generic type represented by the methods generic return type, resolved based on the containingType.
    074     * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
    075     */
    076    Type extractActualType(Type containingType, Method method);
    077
    078    /**
    079     * Analyzes the method in the context of containingClass and returns the Class that is represented by
    080     * the method's generic return type. Any parameter information in the generic return type is lost.
    081     *
    082     * @param containingType Type which is/represents the class that either contains or inherited the field
    083     * @param field          field from which to extract the generic return type
    084     * @return the generic type represented by the methods generic return type, resolved based on the containingType.
    085     * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
    086     */
    087    Type extractActualType(Type containingType, Field field);
    088
    089    /**
    090     * Resolves the type parameter based on the context of the containingType.
    091     *
    092     * {@link java.lang.reflect.TypeVariable} will be unwrapped to the type argument resolved form the class
    093     * hierarchy. This may be something other than a simple Class if the type argument is a ParameterizedType for
    094     * instance (e.g. {@code List<E>; List<Map<Long, String>>}, E would be returned as a ParameterizedType with the raw
    095     * type Map and type arguments Long and String.
    096     *
    097     *
    098     * @param type
    099     *          the generic type (ParameterizedType, GenericArrayType, WildcardType, TypeVariable) to be resolved
    100     * @param containingType
    101     *          the type which his
    102     * @return
    103     *          the type resolved to the best of our ability.
    104     */
    105    Type resolve(final Type type, final Type containingType);
    106
    107    /**
    108     * Convenience class for getting a {@link GenericsResolver} instance.
    109     */
    110    final static public class Provider
    111    {
    112
    113        final private static GenericsResolver instance;
    114
    115        static
    116        {
    117
    118            ServiceLoader<GenericsResolver> serviceLoader = ServiceLoader.load(GenericsResolver.class);
    119            Iterator<GenericsResolver> iterator = serviceLoader.iterator();
    120            if (iterator.hasNext())
    121            {
    122                instance = iterator.next();
    123            }
    124            else
    125            {
    126                instance = new GenericsResolverImpl();
    127            }
    128        }
    129
    130        /**
    131         * Returns a cached {@linkplain GenericsResolver} instance.
    132         * If {@link ServiceLoader} finds one instance, it returns the first one found. If not,
    133         * it returns {@link GenericsResolverImpl}.
    134         * @return a {@link GenericsResolver} instance.
    135         */
    136        public static GenericsResolver getInstance()
    137        {
    138            return instance;
    139        }
    140
    141    }
    142
    143    /**
    144     * Get the class represented by the reflected type.
    145     * This method is lossy; You cannot recover the type information from the class that is returned.
    146     *
    147     * {@code TypeVariable} the first bound is returned. If your type variable extends multiple interfaces that information
    148     * is lost.
    149     *
    150     * {@code WildcardType} the first lower bound is returned. If the wildcard is defined with upper bounds
    151     * then {@code Object} is returned.
    152     *
    153     * @param actualType
    154     *           a Class, ParameterizedType, GenericArrayType
    155     * @return the un-parameterized class associated with the type.
    156     */
    157    Class asClass(Type actualType);
    158
    159}