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}