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.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.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}