TAP5-2560: Error in GenericsUtils affecting property access Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/9a4dd324 Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/9a4dd324 Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/9a4dd324
Branch: refs/heads/master Commit: 9a4dd324549ff637d3cb021f16d28239393d7941 Parents: efd7eae Author: Thiago H. de Paula Figueiredo <[email protected]> Authored: Thu Jan 17 00:13:47 2019 -0200 Committer: Thiago H. de Paula Figueiredo <[email protected]> Committed: Thu Jan 17 00:13:47 2019 -0200 ---------------------------------------------------------------------- 55_RELEASE_NOTES.md | 11 +- .../internal/services/GenericsResolverImpl.java | 627 +++++++++++++ .../ioc/internal/util/GenericsUtils.java | 513 +---------- .../tapestry5/services/GenericsResolver.java | 160 ++++ genericsresolver-guava/LICENSE.txt | 202 +++++ genericsresolver-guava/NOTICE.txt | 2 + genericsresolver-guava/build.gradle | 8 + .../GuavaGenericsResolver.java | 63 ++ ...g.apache.tapestry5.services.GenericsResolver | 1 + genericsresolver-guava/src/test/conf/.gitignore | 1 + .../AbstractBeanModelSourceImplTest.java | 872 +++++++++++++++++++ .../GuavaBeanModelSourceImplTest.java | 24 + .../src/test/resources/log4j.properties | 10 + settings.gradle | 2 +- .../AbstractBeanModelSourceImplTest.java | 3 +- 15 files changed, 2000 insertions(+), 499 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/9a4dd324/55_RELEASE_NOTES.md ---------------------------------------------------------------------- diff --git a/55_RELEASE_NOTES.md b/55_RELEASE_NOTES.md index 3dd11bd..b754fec 100644 --- a/55_RELEASE_NOTES.md +++ b/55_RELEASE_NOTES.md @@ -4,6 +4,7 @@ Scratch pad for changes destined for the 5.5 release notes page. The minimum Java release required to run apps created with Tapestry 5.5 is Java 8. # Java 8, 9, 10 and 11 supported +With the ASM upgrade, now code compiled with Java 8 to 11 is supported. # Updates to embedded Tomcat and Jetty versions (TAP5-2548) With Java 8, we made the switch to servlet-api 3.0. We updated the embedded Tomcat and Jetty containers to the respective versions. Unfortunately, we had to rename Jetty7Runner to JettyRunner and Tomcat6Runner to TomcatRunner in the tapestry-runner package. @@ -15,4 +16,12 @@ security needs. Three rules are added out-of-the-box and may be overriden: * `ClassFile`: blocks access to assets with `.class` endings (case insensitive). * `PropertiesFile`: blocks access to assets with `.properties` endings (case insensitive). -* `XMLFile`: blocks access to assets with `.xml` endings (case insensitive). \ No newline at end of file +* `XMLFile`: blocks access to assets with `.xml` endings (case insensitive). + +# New subproject/JAR: genericsresolver-guava +Tapestry's own code to resolve the bound types of generic types and methods, based around GenericsUtils, +couldn't handle some cases, as discovered in TAP5-2560. Fixing the code to handle these cases +turned out to not be feasible, so we introduced a new JAR, genericsresolver-java, +which replaces GenericsUtils with Google Guava's TypeResolver and associated classes. +To use it, just add genericsresolver-java, which is versioned in the same way as the other Tapestry JARs, +to the classpath of your projects and make sure a not too-old version of Google Guava is also in the classpath. \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/9a4dd324/commons/src/main/java/org/apache/tapestry5/internal/services/GenericsResolverImpl.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/tapestry5/internal/services/GenericsResolverImpl.java b/commons/src/main/java/org/apache/tapestry5/internal/services/GenericsResolverImpl.java new file mode 100644 index 0000000..c0abfaf --- /dev/null +++ b/commons/src/main/java/org/apache/tapestry5/internal/services/GenericsResolverImpl.java @@ -0,0 +1,627 @@ +// Copyright 2007 The Apache Software Foundation +// +// 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 org.apache.tapestry5.internal.services; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.LinkedList; + +import org.apache.tapestry5.services.GenericsResolver; + +/** + * Implementation copied from Tapestry 5.4's GenericUtils (commons package). + */ +@SuppressWarnings("rawtypes") +public class GenericsResolverImpl implements GenericsResolver +{ + /** + * Analyzes the method in the context of containingClass and returns the Class that is represented by + * the method's generic return type. Any parameter information in the generic return type is lost. If you want + * to preserve the type parameters of the return type consider using + * {@link #extractActualType(java.lang.reflect.Type, java.lang.reflect.Method)}. + * + * @param containingClass class which either contains or inherited the method + * @param method method from which to extract the return type + * @return the class represented by the methods generic return type, resolved based on the context . + * @see #extractActualType(java.lang.reflect.Type, java.lang.reflect.Method) + * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type) + * @see #asClass(java.lang.reflect.Type) + */ + public Class<?> extractGenericReturnType(Class<?> containingClass, Method method) + { + return asClass(resolve(method.getGenericReturnType(), containingClass)); + } + + + /** + * Analyzes the field in the context of containingClass and returns the Class that is represented by + * the field's generic type. Any parameter information in the generic type is lost, if you want + * to preserve the type parameters of the return type consider using + * {@link #getTypeVariableIndex(java.lang.reflect.TypeVariable)}. + * + * @param containingClass class which either contains or inherited the field + * @param field field from which to extract the type + * @return the class represented by the field's generic type, resolved based on the containingClass. + * @see #extractActualType(java.lang.reflect.Type, java.lang.reflect.Field) + * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type) + * @see #asClass(java.lang.reflect.Type) + */ + public Class extractGenericFieldType(Class containingClass, Field field) + { + return asClass(resolve(field.getGenericType(), containingClass)); + } + + /** + * Analyzes the method in the context of containingClass and returns the Class that is represented by + * the method's generic return type. Any parameter information in the generic return type is lost. + * + * @param containingType Type which is/represents the class that either contains or inherited the method + * @param method method from which to extract the generic return type + * @return the generic type represented by the methods generic return type, resolved based on the containingType. + * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type) + */ + public Type extractActualType(Type containingType, Method method) + { + return resolve(method.getGenericReturnType(), containingType); + } + + /** + * Analyzes the method in the context of containingClass and returns the Class that is represented by + * the method's generic return type. Any parameter information in the generic return type is lost. + * + * @param containingType Type which is/represents the class that either contains or inherited the field + * @param field field from which to extract the generic return type + * @return the generic type represented by the methods generic return type, resolved based on the containingType. + * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type) + */ + public Type extractActualType(Type containingType, Field field) + { + return resolve(field.getGenericType(), containingType); + } + + /** + * Resolves the type parameter based on the context of the containingType. + * + * {@link java.lang.reflect.TypeVariable} will be unwrapped to the type argument resolved form the class + * hierarchy. This may be something other than a simple Class if the type argument is a ParameterizedType for + * instance (e.g. {@code List<E>; List<Map<Long, String>>}, E would be returned as a ParameterizedType with the raw + * type Map and type arguments Long and String. + * + * + * @param type + * the generic type (ParameterizedType, GenericArrayType, WildcardType, TypeVariable) to be resolved + * @param containingType + * the type which his + * @return + * the type resolved to the best of our ability. + * @since 5.2.? + */ + public Type resolve(final Type type, final Type containingType) + { + // The type isn't generic. (String, Long, etc) + if (type instanceof Class) + return type; + + // List<T>, List<String>, List<T extends Number> + if (type instanceof ParameterizedType) + return resolve((ParameterizedType) type, containingType); + + // T[], List<String>[], List<T>[] + if (type instanceof GenericArrayType) + return resolve((GenericArrayType) type, containingType); + + // List<? extends T>, List<? extends Object & Comparable & Serializable> + if (type instanceof WildcardType) + return resolve((WildcardType) type, containingType); + + // T + if (type instanceof TypeVariable) + return resolve((TypeVariable) type, containingType); + + // I'm leaning towards an exception here. + return type; + } + + + /** + * Determines if the suspected super type is assignable from the suspected sub type. + * + * @param suspectedSuperType + * e.g. {@code GenericDAO<Pet, String>} + * @param suspectedSubType + * e.g. {@code PetDAO extends GenericDAO<Pet,String>} + * @return + * true if (sourceType)targetClass is a valid cast + */ + @SuppressWarnings({ "unused", "unchecked" }) + private boolean isAssignableFrom(Type suspectedSuperType, Type suspectedSubType) + { + final Class suspectedSuperClass = asClass(suspectedSuperType); + final Class suspectedSubClass = asClass(suspectedSubType); + + // The raw types need to be compatible. + if (!suspectedSuperClass.isAssignableFrom(suspectedSubClass)) + { + return false; + } + + // From this point we know that the raw types are assignable. + // We need to figure out what the generic parameters in the targetClass are + // as they pertain to the sourceType. + + if (suspectedSuperType instanceof WildcardType) + { + // ? extends Number + // needs to match all the bounds (there will only be upper bounds or lower bounds + for (Type t : ((WildcardType) suspectedSuperType).getUpperBounds()) + { + if (!isAssignableFrom(t, suspectedSubType)) return false; + } + for (Type t : ((WildcardType) suspectedSuperType).getLowerBounds()) + { + if (!isAssignableFrom(suspectedSubType, t)) return false; + } + return true; + } + + Type curType = suspectedSubType; + Class curClass; + + while (curType != null && !curType.equals(Object.class)) + { + curClass = asClass(curType); + + if (curClass.equals(suspectedSuperClass)) + { + final Type resolved = resolve(curType, suspectedSubType); + + if (suspectedSuperType instanceof Class) + { + if ( resolved instanceof Class ) + return suspectedSuperType.equals(resolved); + + // They may represent the same class, but the suspectedSuperType is not parameterized. The parameter + // types default to Object so they must be a match. + // e.g. Pair p = new StringLongPair(); + // Pair p = new Pair<? extends Number, String> + + return true; + } + + if (suspectedSuperType instanceof ParameterizedType) + { + if (resolved instanceof ParameterizedType) + { + final Type[] type1Arguments = ((ParameterizedType) suspectedSuperType).getActualTypeArguments(); + final Type[] type2Arguments = ((ParameterizedType) resolved).getActualTypeArguments(); + if (type1Arguments.length != type2Arguments.length) return false; + + for (int i = 0; i < type1Arguments.length; ++i) + { + if (!isAssignableFrom(type1Arguments[i], type2Arguments[i])) return false; + } + return true; + } + } + else if (suspectedSuperType instanceof GenericArrayType) + { + if (resolved instanceof GenericArrayType) + { + return isAssignableFrom( + ((GenericArrayType) suspectedSuperType).getGenericComponentType(), + ((GenericArrayType) resolved).getGenericComponentType() + ); + } + } + + return false; + } + + final Type[] types = curClass.getGenericInterfaces(); + for (Type t : types) + { + final Type resolved = resolve(t, suspectedSubType); + if (isAssignableFrom(suspectedSuperType, resolved)) + return true; + } + + curType = curClass.getGenericSuperclass(); + } + return false; + } + + /** + * Get the class represented by the reflected type. + * This method is lossy; You cannot recover the type information from the class that is returned. + * + * {@code TypeVariable} the first bound is returned. If your type variable extends multiple interfaces that information + * is lost. + * + * {@code WildcardType} the first lower bound is returned. If the wildcard is defined with upper bounds + * then {@code Object} is returned. + * + * @param actualType + * a Class, ParameterizedType, GenericArrayType + * @return the un-parameterized class associated with the type. + */ + public Class asClass(Type actualType) + { + if (actualType instanceof Class) return (Class) actualType; + + if (actualType instanceof ParameterizedType) + { + final Type rawType = ((ParameterizedType) actualType).getRawType(); + // The sun implementation returns getRawType as Class<?>, but there is room in the interface for it to be + // some other Type. We'll assume it's a Class. + // TODO: consider logging or throwing our own exception for that day when "something else" causes some confusion + return (Class) rawType; + } + + if (actualType instanceof GenericArrayType) + { + final Type type = ((GenericArrayType) actualType).getGenericComponentType(); + return Array.newInstance(asClass(type), 0).getClass(); + } + + if (actualType instanceof TypeVariable) + { + // Support for List<T extends Number> + // There is always at least one bound. If no bound is specified in the source then it will be Object.class + return asClass(((TypeVariable) actualType).getBounds()[0]); + } + + if (actualType instanceof WildcardType) + { + final WildcardType wildcardType = (WildcardType) actualType; + final Type[] bounds = wildcardType.getLowerBounds(); + if (bounds != null && bounds.length > 0) + { + return asClass(bounds[0]); + } + // If there is no lower bounds then the only thing that makes sense is Object. + return Object.class; + } + + throw new RuntimeException(String.format("Unable to convert %s to Class.", actualType)); + } + + /** + * Convert the type into a string. The string representation approximates the code that would be used to define the + * type. + * + * @param type - the type. + * @return a string representation of the type, similar to how it was declared. + */ + public static String toString(Type type) + { + if ( type instanceof ParameterizedType ) return toString((ParameterizedType)type); + if ( type instanceof WildcardType ) return toString((WildcardType)type); + if ( type instanceof GenericArrayType) return toString((GenericArrayType)type); + if ( type instanceof Class ) + { + final Class theClass = (Class) type; + return (theClass.isArray() ? theClass.getName() + "[]" : theClass.getName()); + } + return type.toString(); + } + + /** + * Method to resolve a TypeVariable to its most + * <a href="http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#112582">reifiable</a> form. + * + * + * How to resolve a TypeVariable:<br/> + * All of the TypeVariables defined by a generic class will be given a Type by any class that extends it. The Type + * given may or may not be reifiable; it may be another TypeVariable for instance. + * + * Consider <br/> + * <i>class Pair>A,B> { A getA(){...}; ...}</i><br/> + * <i>class StringLongPair extends Pair>String, Long> { }</i><br/> + * + * To resolve the actual return type of Pair.getA() you must first resolve the TypeVariable "A". + * We can do that by first finding the index of "A" in the Pair.class.getTypeParameters() array of TypeVariables. + * + * To get to the Type provided by StringLongPair you access the generics information by calling + * StringLongPair.class.getGenericSuperclass; this will be a ParameterizedType. ParameterizedType gives you access + * to the actual type arguments provided to Pair by StringLongPair. The array is in the same order as the array in + * Pair.class.getTypeParameters so you can use the index we discovered earlier to extract the Type; String.class. + * + * When extracting Types we only have to consider the superclass hierarchy and not the interfaces implemented by + * the class. When a class implements a generic interface it must provide types for the interface and any generic + * methods implemented from the interface will be re-defined by the class with its generic type variables. + * + * @param typeVariable - the type variable to resolve. + * @param containingType - the shallowest class in the class hierarchy (furthest from Object) where typeVariable is defined. + * @return a Type that has had all possible TypeVariables resolved that have been defined between the type variable + * declaration and the containingType. + */ + private Type resolve(TypeVariable typeVariable, Type containingType) + { + // The generic declaration is either a Class, Method or Constructor + final GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); + + if (!(genericDeclaration instanceof Class)) + { + // It's a method or constructor. The best we can do here is try to resolve the bounds + // e.g. <T extends E> T getT(T param){} where E is defined by the class. + final Type bounds0 = typeVariable.getBounds()[0]; + return resolve(bounds0, containingType); + } + + final Class typeVariableOwner = (Class) genericDeclaration; + + // find the typeOwner in the containingType's hierarchy + final LinkedList<Type> stack = new LinkedList<Type>(); + + // If you pass a List<Long> as the containingType then the TypeVariable is going to be resolved by the + // containingType and not the super class. + if (containingType instanceof ParameterizedType) + { + stack.add(containingType); + } + + Class theClass = asClass(containingType); + Type genericSuperclass = theClass.getGenericSuperclass(); + while (genericSuperclass != null && // true for interfaces with no superclass + !theClass.equals(Object.class) && + !theClass.equals(typeVariableOwner)) + { + stack.addFirst(genericSuperclass); + theClass = asClass(genericSuperclass); + genericSuperclass = theClass.getGenericSuperclass(); + } + + int i = getTypeVariableIndex(typeVariable); + Type resolved = typeVariable; + for (Type t : stack) + { + if (t instanceof ParameterizedType) + { + resolved = ((ParameterizedType) t).getActualTypeArguments()[i]; + if (resolved instanceof Class) return resolved; + if (resolved instanceof TypeVariable) + { + // Need to look at the next class in the hierarchy + i = getTypeVariableIndex((TypeVariable) resolved); + continue; + } + return resolve(resolved, containingType); + } + } + + // the only way we get here is if resolved is still a TypeVariable, otherwise an + // exception is thrown or a value is returned. + return ((TypeVariable) resolved).getBounds()[0]; + } + + /** + * @param type - something like List<T>[] or List<? extends T>[] or T[] + * @param containingType - the shallowest type in the hierarchy where type is defined. + * @return either the passed type if no changes required or a copy with a best effort resolve of the component type. + */ + private GenericArrayType resolve(GenericArrayType type, Type containingType) + { + final Type componentType = type.getGenericComponentType(); + + if (!(componentType instanceof Class)) + { + final Type resolved = resolve(componentType, containingType); + return create(resolved); + } + + return type; + } + + /** + * @param type - something like List<T>, List<T extends Number> + * @param containingType - the shallowest type in the hierarchy where type is defined. + * @return the passed type if nothing to resolve or a copy of the type with the type arguments resolved. + */ + private ParameterizedType resolve(ParameterizedType type, Type containingType) + { + // Use a copy because we're going to modify it. + final Type[] types = type.getActualTypeArguments().clone(); + + boolean modified = resolve(types, containingType); + return modified ? create(type.getRawType(), type.getOwnerType(), types) : type; + } + + /** + * @param type - something like List<? super T>, List<<? extends T>, List<? extends T & Comparable<? super T>> + * @param containingType - the shallowest type in the hierarchy where type is defined. + * @return the passed type if nothing to resolve or a copy of the type with the upper and lower bounds resolved. + */ + private WildcardType resolve(WildcardType type, Type containingType) + { + // Use a copy because we're going to modify them. + final Type[] upper = type.getUpperBounds().clone(); + final Type[] lower = type.getLowerBounds().clone(); + + boolean modified = resolve(upper, containingType); + modified = modified || resolve(lower, containingType); + + return modified ? create(upper, lower) : type; + } + + /** + * @param types - Array of types to resolve. The unresolved type is replaced in the array with the resolved type. + * @param containingType - the shallowest type in the hierarchy where type is defined. + * @return true if any of the types were resolved. + */ + private boolean resolve(Type[] types, Type containingType) + { + boolean modified = false; + for (int i = 0; i < types.length; ++i) + { + Type t = types[i]; + if (!(t instanceof Class)) + { + modified = true; + final Type resolved = resolve(t, containingType); + if (!resolved.equals(t)) + { + types[i] = resolved; + modified = true; + } + } + } + return modified; + } + + /** + * @param rawType - the un-parameterized type. + * @param ownerType - the outer class or null if the class is not defined within another class. + * @param typeArguments - type arguments. + * @return a copy of the type with the typeArguments replaced. + */ + static ParameterizedType create(final Type rawType, final Type ownerType, final Type[] typeArguments) + { + return new ParameterizedType() + { + @Override + public Type[] getActualTypeArguments() + { + return typeArguments; + } + + @Override + public Type getRawType() + { + return rawType; + } + + @Override + public Type getOwnerType() + { + return ownerType; + } + + @Override + public String toString() + { + return GenericsResolverImpl.toString(this); + } + }; + } + + static GenericArrayType create(final Type componentType) + { + return new GenericArrayType() + { + @Override + public Type getGenericComponentType() + { + return componentType; + } + + @Override + public String toString() + { + return GenericsResolverImpl.toString(this); + } + }; + } + + /** + * @param upperBounds - e.g. ? extends Number + * @param lowerBounds - e.g. ? super Long + * @return An new copy of the type with the upper and lower bounds replaced. + */ + static WildcardType create(final Type[] upperBounds, final Type[] lowerBounds) + { + + return new WildcardType() + { + @Override + public Type[] getUpperBounds() + { + return upperBounds; + } + + @Override + public Type[] getLowerBounds() + { + return lowerBounds; + } + + @Override + public String toString() + { + return GenericsResolverImpl.toString(this); + } + }; + } + + static String toString(ParameterizedType pt) + { + String s = toString(pt.getActualTypeArguments()); + return String.format("%s<%s>", toString(pt.getRawType()), s); + } + + static String toString(GenericArrayType gat) + { + return String.format("%s[]", toString(gat.getGenericComponentType())); + } + + static String toString(WildcardType wt) + { + final boolean isSuper = wt.getLowerBounds().length > 0; + return String.format("? %s %s", + isSuper ? "super" : "extends", + toString(wt.getLowerBounds())); + } + + static String toString(Type[] types) + { + StringBuilder sb = new StringBuilder(); + for ( Type t : types ) + { + sb.append(toString(t)).append(", "); + } + return sb.substring(0, sb.length() - 2);// drop last , + } + + /** + * Find the index of the TypeVariable in the classes parameters. The offset can be used on a subclass to find + * the actual type. + * + * @param typeVariable - the type variable in question. + * @return the index of the type variable in its declaring class/method/constructor's type parameters. + */ + private static int getTypeVariableIndex(final TypeVariable typeVariable) + { + // the label from the class (the T in List<T>, the K or V in Map<K,V>, etc) + final String typeVarName = typeVariable.getName(); + final TypeVariable[] typeParameters = typeVariable.getGenericDeclaration().getTypeParameters(); + for (int typeArgumentIndex = 0; typeArgumentIndex < typeParameters.length; typeArgumentIndex++) + { + // The .equals for TypeVariable may not be compatible, a name check should be sufficient. + if (typeParameters[typeArgumentIndex].getName().equals(typeVarName)) + return typeArgumentIndex; + } + + // The only way this could happen is if the TypeVariable is hand built incorrectly, or it's corrupted. + throw new RuntimeException( + String.format("%s does not have a TypeVariable matching %s", typeVariable.getGenericDeclaration(), typeVariable)); + } + +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/9a4dd324/commons/src/main/java/org/apache/tapestry5/ioc/internal/util/GenericsUtils.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/tapestry5/ioc/internal/util/GenericsUtils.java b/commons/src/main/java/org/apache/tapestry5/ioc/internal/util/GenericsUtils.java index 9bf4d00..f01064b 100644 --- a/commons/src/main/java/org/apache/tapestry5/ioc/internal/util/GenericsUtils.java +++ b/commons/src/main/java/org/apache/tapestry5/ioc/internal/util/GenericsUtils.java @@ -12,15 +12,20 @@ package org.apache.tapestry5.ioc.internal.util; -import java.lang.reflect.*; -import java.util.LinkedList; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import org.apache.tapestry5.services.GenericsResolver; /** - * Static methods related to the use of JDK 1.5 generics. + * Static methods related to the use of JDK 1.5 generics. From Tapestry 5.5.0, + * this class just delegates to {@link GenericsResolver}. */ -@SuppressWarnings("unchecked") public class GenericsUtils { + final private static GenericsResolver GENERICS_RESOLVER = GenericsResolver.Provider.getInstance(); + /** * Analyzes the method in the context of containingClass and returns the Class that is represented by * the method's generic return type. Any parameter information in the generic return type is lost. If you want @@ -36,10 +41,9 @@ public class GenericsUtils */ public static Class<?> extractGenericReturnType(Class<?> containingClass, Method method) { - return asClass(resolve(method.getGenericReturnType(), containingClass)); + return GENERICS_RESOLVER.extractGenericReturnType(containingClass, method); } - /** * Analyzes the field in the context of containingClass and returns the Class that is represented by * the field's generic type. Any parameter information in the generic type is lost, if you want @@ -55,7 +59,7 @@ public class GenericsUtils */ public static Class extractGenericFieldType(Class containingClass, Field field) { - return asClass(resolve(field.getGenericType(), containingClass)); + return GENERICS_RESOLVER.extractGenericFieldType(containingClass, field); } /** @@ -69,7 +73,7 @@ public class GenericsUtils */ public static Type extractActualType(Type containingType, Method method) { - return resolve(method.getGenericReturnType(), containingType); + return GENERICS_RESOLVER.extractActualType(containingType, method); } /** @@ -83,7 +87,7 @@ public class GenericsUtils */ public static Type extractActualType(Type containingType, Field field) { - return resolve(field.getGenericType(), containingType); + return GENERICS_RESOLVER.extractActualType(containingType, field); } /** @@ -105,137 +109,9 @@ public class GenericsUtils */ public static Type resolve(final Type type, final Type containingType) { - // The type isn't generic. (String, Long, etc) - if (type instanceof Class) - return type; - - // List<T>, List<String>, List<T extends Number> - if (type instanceof ParameterizedType) - return resolve((ParameterizedType) type, containingType); - - // T[], List<String>[], List<T>[] - if (type instanceof GenericArrayType) - return resolve((GenericArrayType) type, containingType); - - // List<? extends T>, List<? extends Object & Comparable & Serializable> - if (type instanceof WildcardType) - return resolve((WildcardType) type, containingType); - - // T - if (type instanceof TypeVariable) - return resolve((TypeVariable) type, containingType); - - // I'm leaning towards an exception here. - return type; + return GENERICS_RESOLVER.resolve(type, containingType); } - - - /** - * Determines if the suspected super type is assignable from the suspected sub type. - * - * @param suspectedSuperType - * e.g. {@code GenericDAO<Pet, String>} - * @param suspectedSubType - * e.g. {@code PetDAO extends GenericDAO<Pet,String>} - * @return - * true if (sourceType)targetClass is a valid cast - */ - public static boolean isAssignableFrom(Type suspectedSuperType, Type suspectedSubType) - { - final Class suspectedSuperClass = asClass(suspectedSuperType); - final Class suspectedSubClass = asClass(suspectedSubType); - - // The raw types need to be compatible. - if (!suspectedSuperClass.isAssignableFrom(suspectedSubClass)) - { - return false; - } - - // From this point we know that the raw types are assignable. - // We need to figure out what the generic parameters in the targetClass are - // as they pertain to the sourceType. - - if (suspectedSuperType instanceof WildcardType) - { - // ? extends Number - // needs to match all the bounds (there will only be upper bounds or lower bounds - for (Type t : ((WildcardType) suspectedSuperType).getUpperBounds()) - { - if (!isAssignableFrom(t, suspectedSubType)) return false; - } - for (Type t : ((WildcardType) suspectedSuperType).getLowerBounds()) - { - if (!isAssignableFrom(suspectedSubType, t)) return false; - } - return true; - } - - Type curType = suspectedSubType; - Class curClass; - - while (curType != null && !curType.equals(Object.class)) - { - curClass = asClass(curType); - - if (curClass.equals(suspectedSuperClass)) - { - final Type resolved = resolve(curType, suspectedSubType); - - if (suspectedSuperType instanceof Class) - { - if ( resolved instanceof Class ) - return suspectedSuperType.equals(resolved); - - // They may represent the same class, but the suspectedSuperType is not parameterized. The parameter - // types default to Object so they must be a match. - // e.g. Pair p = new StringLongPair(); - // Pair p = new Pair<? extends Number, String> - - return true; - } - - if (suspectedSuperType instanceof ParameterizedType) - { - if (resolved instanceof ParameterizedType) - { - final Type[] type1Arguments = ((ParameterizedType) suspectedSuperType).getActualTypeArguments(); - final Type[] type2Arguments = ((ParameterizedType) resolved).getActualTypeArguments(); - if (type1Arguments.length != type2Arguments.length) return false; - - for (int i = 0; i < type1Arguments.length; ++i) - { - if (!isAssignableFrom(type1Arguments[i], type2Arguments[i])) return false; - } - return true; - } - } - else if (suspectedSuperType instanceof GenericArrayType) - { - if (resolved instanceof GenericArrayType) - { - return isAssignableFrom( - ((GenericArrayType) suspectedSuperType).getGenericComponentType(), - ((GenericArrayType) resolved).getGenericComponentType() - ); - } - } - - return false; - } - - final Type[] types = curClass.getGenericInterfaces(); - for (Type t : types) - { - final Type resolved = resolve(t, suspectedSubType); - if (isAssignableFrom(suspectedSuperType, resolved)) - return true; - } - - curType = curClass.getGenericSuperclass(); - } - return false; - } - + /** * Get the class represented by the reflected type. * This method is lossy; You cannot recover the type information from the class that is returned. @@ -252,362 +128,7 @@ public class GenericsUtils */ public static Class asClass(Type actualType) { - if (actualType instanceof Class) return (Class) actualType; - - if (actualType instanceof ParameterizedType) - { - final Type rawType = ((ParameterizedType) actualType).getRawType(); - // The sun implementation returns getRawType as Class<?>, but there is room in the interface for it to be - // some other Type. We'll assume it's a Class. - // TODO: consider logging or throwing our own exception for that day when "something else" causes some confusion - return (Class) rawType; - } - - if (actualType instanceof GenericArrayType) - { - final Type type = ((GenericArrayType) actualType).getGenericComponentType(); - return Array.newInstance(asClass(type), 0).getClass(); - } - - if (actualType instanceof TypeVariable) - { - // Support for List<T extends Number> - // There is always at least one bound. If no bound is specified in the source then it will be Object.class - return asClass(((TypeVariable) actualType).getBounds()[0]); - } - - if (actualType instanceof WildcardType) - { - final WildcardType wildcardType = (WildcardType) actualType; - final Type[] bounds = wildcardType.getLowerBounds(); - if (bounds != null && bounds.length > 0) - { - return asClass(bounds[0]); - } - // If there is no lower bounds then the only thing that makes sense is Object. - return Object.class; - } - - throw new RuntimeException(String.format("Unable to convert %s to Class.", actualType)); - } - - /** - * Convert the type into a string. The string representation approximates the code that would be used to define the - * type. - * - * @param type - the type. - * @return a string representation of the type, similar to how it was declared. - */ - public static String toString(Type type) - { - if ( type instanceof ParameterizedType ) return toString((ParameterizedType)type); - if ( type instanceof WildcardType ) return toString((WildcardType)type); - if ( type instanceof GenericArrayType) return toString((GenericArrayType)type); - if ( type instanceof Class ) - { - final Class theClass = (Class) type; - return (theClass.isArray() ? theClass.getName() + "[]" : theClass.getName()); - } - return type.toString(); - } - - /** - * Method to resolve a TypeVariable to its most - * <a href="http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#112582">reifiable</a> form. - * - * - * How to resolve a TypeVariable:<br/> - * All of the TypeVariables defined by a generic class will be given a Type by any class that extends it. The Type - * given may or may not be reifiable; it may be another TypeVariable for instance. - * - * Consider <br/> - * <i>class Pair>A,B> { A getA(){...}; ...}</i><br/> - * <i>class StringLongPair extends Pair>String, Long> { }</i><br/> - * - * To resolve the actual return type of Pair.getA() you must first resolve the TypeVariable "A". - * We can do that by first finding the index of "A" in the Pair.class.getTypeParameters() array of TypeVariables. - * - * To get to the Type provided by StringLongPair you access the generics information by calling - * StringLongPair.class.getGenericSuperclass; this will be a ParameterizedType. ParameterizedType gives you access - * to the actual type arguments provided to Pair by StringLongPair. The array is in the same order as the array in - * Pair.class.getTypeParameters so you can use the index we discovered earlier to extract the Type; String.class. - * - * When extracting Types we only have to consider the superclass hierarchy and not the interfaces implemented by - * the class. When a class implements a generic interface it must provide types for the interface and any generic - * methods implemented from the interface will be re-defined by the class with its generic type variables. - * - * @param typeVariable - the type variable to resolve. - * @param containingType - the shallowest class in the class hierarchy (furthest from Object) where typeVariable is defined. - * @return a Type that has had all possible TypeVariables resolved that have been defined between the type variable - * declaration and the containingType. - */ - private static Type resolve(TypeVariable typeVariable, Type containingType) - { - // The generic declaration is either a Class, Method or Constructor - final GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); - - if (!(genericDeclaration instanceof Class)) - { - // It's a method or constructor. The best we can do here is try to resolve the bounds - // e.g. <T extends E> T getT(T param){} where E is defined by the class. - final Type bounds0 = typeVariable.getBounds()[0]; - return resolve(bounds0, containingType); - } - - final Class typeVariableOwner = (Class) genericDeclaration; - - // find the typeOwner in the containingType's hierarchy - final LinkedList<Type> stack = new LinkedList<Type>(); - - // If you pass a List<Long> as the containingType then the TypeVariable is going to be resolved by the - // containingType and not the super class. - if (containingType instanceof ParameterizedType) - { - stack.add(containingType); - } - - Class theClass = asClass(containingType); - Type genericSuperclass = theClass.getGenericSuperclass(); - while (genericSuperclass != null && // true for interfaces with no superclass - !theClass.equals(Object.class) && - !theClass.equals(typeVariableOwner)) - { - stack.addFirst(genericSuperclass); - theClass = asClass(genericSuperclass); - genericSuperclass = theClass.getGenericSuperclass(); - } - - int i = getTypeVariableIndex(typeVariable); - Type resolved = typeVariable; - for (Type t : stack) - { - if (t instanceof ParameterizedType) - { - resolved = ((ParameterizedType) t).getActualTypeArguments()[i]; - if (resolved instanceof Class) return resolved; - if (resolved instanceof TypeVariable) - { - // Need to look at the next class in the hierarchy - i = getTypeVariableIndex((TypeVariable) resolved); - continue; - } - return resolve(resolved, containingType); - } - } - - // the only way we get here is if resolved is still a TypeVariable, otherwise an - // exception is thrown or a value is returned. - return ((TypeVariable) resolved).getBounds()[0]; - } - - /** - * @param type - something like List<T>[] or List<? extends T>[] or T[] - * @param containingType - the shallowest type in the hierarchy where type is defined. - * @return either the passed type if no changes required or a copy with a best effort resolve of the component type. - */ - private static GenericArrayType resolve(GenericArrayType type, Type containingType) - { - final Type componentType = type.getGenericComponentType(); - - if (!(componentType instanceof Class)) - { - final Type resolved = resolve(componentType, containingType); - return create(resolved); - } - - return type; - } - - /** - * @param type - something like List<T>, List<T extends Number> - * @param containingType - the shallowest type in the hierarchy where type is defined. - * @return the passed type if nothing to resolve or a copy of the type with the type arguments resolved. - */ - private static ParameterizedType resolve(ParameterizedType type, Type containingType) - { - // Use a copy because we're going to modify it. - final Type[] types = type.getActualTypeArguments().clone(); - - boolean modified = resolve(types, containingType); - return modified ? create(type.getRawType(), type.getOwnerType(), types) : type; - } - - /** - * @param type - something like List<? super T>, List<<? extends T>, List<? extends T & Comparable<? super T>> - * @param containingType - the shallowest type in the hierarchy where type is defined. - * @return the passed type if nothing to resolve or a copy of the type with the upper and lower bounds resolved. - */ - private static WildcardType resolve(WildcardType type, Type containingType) - { - // Use a copy because we're going to modify them. - final Type[] upper = type.getUpperBounds().clone(); - final Type[] lower = type.getLowerBounds().clone(); - - boolean modified = resolve(upper, containingType); - modified = modified || resolve(lower, containingType); - - return modified ? create(upper, lower) : type; - } - - /** - * @param types - Array of types to resolve. The unresolved type is replaced in the array with the resolved type. - * @param containingType - the shallowest type in the hierarchy where type is defined. - * @return true if any of the types were resolved. - */ - private static boolean resolve(Type[] types, Type containingType) - { - boolean modified = false; - for (int i = 0; i < types.length; ++i) - { - Type t = types[i]; - if (!(t instanceof Class)) - { - modified = true; - final Type resolved = resolve(t, containingType); - if (!resolved.equals(t)) - { - types[i] = resolved; - modified = true; - } - } - } - return modified; - } - - /** - * @param rawType - the un-parameterized type. - * @param ownerType - the outer class or null if the class is not defined within another class. - * @param typeArguments - type arguments. - * @return a copy of the type with the typeArguments replaced. - */ - static ParameterizedType create(final Type rawType, final Type ownerType, final Type[] typeArguments) - { - return new ParameterizedType() - { - @Override - public Type[] getActualTypeArguments() - { - return typeArguments; - } - - @Override - public Type getRawType() - { - return rawType; - } - - @Override - public Type getOwnerType() - { - return ownerType; - } - - @Override - public String toString() - { - return GenericsUtils.toString(this); - } - }; - } - - static GenericArrayType create(final Type componentType) - { - return new GenericArrayType() - { - @Override - public Type getGenericComponentType() - { - return componentType; - } - - @Override - public String toString() - { - return GenericsUtils.toString(this); - } - }; - } - - /** - * @param upperBounds - e.g. ? extends Number - * @param lowerBounds - e.g. ? super Long - * @return An new copy of the type with the upper and lower bounds replaced. - */ - static WildcardType create(final Type[] upperBounds, final Type[] lowerBounds) - { - - return new WildcardType() - { - @Override - public Type[] getUpperBounds() - { - return upperBounds; - } - - @Override - public Type[] getLowerBounds() - { - return lowerBounds; - } - - @Override - public String toString() - { - return GenericsUtils.toString(this); - } - }; - } - - static String toString(ParameterizedType pt) - { - String s = toString(pt.getActualTypeArguments()); - return String.format("%s<%s>", toString(pt.getRawType()), s); - } - - static String toString(GenericArrayType gat) - { - return String.format("%s[]", toString(gat.getGenericComponentType())); - } - - static String toString(WildcardType wt) - { - final boolean isSuper = wt.getLowerBounds().length > 0; - return String.format("? %s %s", - isSuper ? "super" : "extends", - toString(wt.getLowerBounds())); - } - - static String toString(Type[] types) - { - StringBuilder sb = new StringBuilder(); - for ( Type t : types ) - { - sb.append(toString(t)).append(", "); - } - return sb.substring(0, sb.length() - 2);// drop last , - } - - /** - * Find the index of the TypeVariable in the classes parameters. The offset can be used on a subclass to find - * the actual type. - * - * @param typeVariable - the type variable in question. - * @return the index of the type variable in its declaring class/method/constructor's type parameters. - */ - private static int getTypeVariableIndex(final TypeVariable typeVariable) - { - // the label from the class (the T in List<T>, the K or V in Map<K,V>, etc) - final String typeVarName = typeVariable.getName(); - final TypeVariable[] typeParameters = typeVariable.getGenericDeclaration().getTypeParameters(); - for (int typeArgumentIndex = 0; typeArgumentIndex < typeParameters.length; typeArgumentIndex++) - { - // The .equals for TypeVariable may not be compatible, a name check should be sufficient. - if (typeParameters[typeArgumentIndex].getName().equals(typeVarName)) - return typeArgumentIndex; - } - - // The only way this could happen is if the TypeVariable is hand built incorrectly, or it's corrupted. - throw new RuntimeException( - String.format("%s does not have a TypeVariable matching %s", typeVariable.getGenericDeclaration(), typeVariable)); + return GENERICS_RESOLVER.asClass(actualType); } + } http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/9a4dd324/commons/src/main/java/org/apache/tapestry5/services/GenericsResolver.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/tapestry5/services/GenericsResolver.java b/commons/src/main/java/org/apache/tapestry5/services/GenericsResolver.java new file mode 100644 index 0000000..aa1e8de --- /dev/null +++ b/commons/src/main/java/org/apache/tapestry5/services/GenericsResolver.java @@ -0,0 +1,160 @@ +// 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 org.apache.tapestry5.services; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Iterator; +import java.util.ServiceLoader; + +import org.apache.tapestry5.internal.services.GenericsResolverImpl; + +/** + * <p>Methods related to the use of Java 5+ generics. + * Instances should be obtained through {@link GenericsResolver.Provider#getInstance()}.</p> + * + * <p> + * If you have exceptions or bad results with classes using Generics, such as exceptions + * or missing BeanModel properties, + * you should try adding the <code>genericsresolver-guava<code> Tapestry subproject to our classpath. + * </p> + * + * @since 5.5.0 + */ +@SuppressWarnings("unchecked") +public interface GenericsResolver +{ + /** + * Analyzes the method in the context of containingClass and returns the Class that is represented by + * the method's generic return type. Any parameter information in the generic return type is lost. If you want + * to preserve the type parameters of the return type consider using + * {@link #extractActualType(java.lang.reflect.Type, java.lang.reflect.Method)}. + * + * @param containingClass class which either contains or inherited the method + * @param method method from which to extract the return type + * @return the class represented by the methods generic return type, resolved based on the context . + * @see #extractActualType(java.lang.reflect.Type, java.lang.reflect.Method) + * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type) + * @see #asClass(java.lang.reflect.Type) + */ + Class<?> extractGenericReturnType(Class<?> containingClass, Method method); + + /** + * Analyzes the field in the context of containingClass and returns the Class that is represented by + * the field's generic type. Any parameter information in the generic type is lost, if you want + * to preserve the type parameters of the return type consider using + * {@link #getTypeVariableIndex(java.lang.reflect.TypeVariable)}. + * + * @param containingClass class which either contains or inherited the field + * @param field field from which to extract the type + * @return the class represented by the field's generic type, resolved based on the containingClass. + * @see #extractActualType(java.lang.reflect.Type, java.lang.reflect.Field) + * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type) + * @see #asClass(java.lang.reflect.Type) + */ + Class extractGenericFieldType(Class containingClass, Field field); + + /** + * Analyzes the method in the context of containingClass and returns the Class that is represented by + * the method's generic return type. Any parameter information in the generic return type is lost. + * + * @param containingType Type which is/represents the class that either contains or inherited the method + * @param method method from which to extract the generic return type + * @return the generic type represented by the methods generic return type, resolved based on the containingType. + * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type) + */ + Type extractActualType(Type containingType, Method method); + + /** + * Analyzes the method in the context of containingClass and returns the Class that is represented by + * the method's generic return type. Any parameter information in the generic return type is lost. + * + * @param containingType Type which is/represents the class that either contains or inherited the field + * @param field field from which to extract the generic return type + * @return the generic type represented by the methods generic return type, resolved based on the containingType. + * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type) + */ + Type extractActualType(Type containingType, Field field); + + /** + * Resolves the type parameter based on the context of the containingType. + * + * {@link java.lang.reflect.TypeVariable} will be unwrapped to the type argument resolved form the class + * hierarchy. This may be something other than a simple Class if the type argument is a ParameterizedType for + * instance (e.g. {@code List<E>; List<Map<Long, String>>}, E would be returned as a ParameterizedType with the raw + * type Map and type arguments Long and String. + * + * + * @param type + * the generic type (ParameterizedType, GenericArrayType, WildcardType, TypeVariable) to be resolved + * @param containingType + * the type which his + * @return + * the type resolved to the best of our ability. + */ + Type resolve(final Type type, final Type containingType); + + /** + * Convenience class for getting a {@link GenericsResolver} instance. + */ + final static public class Provider + { + + final private static GenericsResolver instance; + + static + { + + ServiceLoader<GenericsResolver> serviceLoader = ServiceLoader.load(GenericsResolver.class); + Iterator<GenericsResolver> iterator = serviceLoader.iterator(); + if (iterator.hasNext()) + { + instance = iterator.next(); + } + else + { + instance = new GenericsResolverImpl(); + } + } + + /** + * Returns a cached {@linkplain GenericsResolver} instance. + * If {@link ServiceLoader} finds one instance, it returns the first one found. If not, + * it returns {@link GenericsResolverImpl}. + * @return a {@link GenericsResolver} instance. + */ + public static GenericsResolver getInstance() + { + return instance; + } + + } + + /** + * Get the class represented by the reflected type. + * This method is lossy; You cannot recover the type information from the class that is returned. + * + * {@code TypeVariable} the first bound is returned. If your type variable extends multiple interfaces that information + * is lost. + * + * {@code WildcardType} the first lower bound is returned. If the wildcard is defined with upper bounds + * then {@code Object} is returned. + * + * @param actualType + * a Class, ParameterizedType, GenericArrayType + * @return the un-parameterized class associated with the type. + */ + Class asClass(Type actualType); + +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/9a4dd324/genericsresolver-guava/LICENSE.txt ---------------------------------------------------------------------- diff --git a/genericsresolver-guava/LICENSE.txt b/genericsresolver-guava/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/genericsresolver-guava/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/9a4dd324/genericsresolver-guava/NOTICE.txt ---------------------------------------------------------------------- diff --git a/genericsresolver-guava/NOTICE.txt b/genericsresolver-guava/NOTICE.txt new file mode 100644 index 0000000..3f59805 --- /dev/null +++ b/genericsresolver-guava/NOTICE.txt @@ -0,0 +1,2 @@ +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/9a4dd324/genericsresolver-guava/build.gradle ---------------------------------------------------------------------- diff --git a/genericsresolver-guava/build.gradle b/genericsresolver-guava/build.gradle new file mode 100644 index 0000000..f7b037c --- /dev/null +++ b/genericsresolver-guava/build.gradle @@ -0,0 +1,8 @@ +description = "Replaces the Tapestry Commons's own Java Generics resolution code with the one from Google Guava's one" + +dependencies { + compile project(':commons') + testCompile project(':tapestry-core') + testCompile project(':tapestry-test') + provided compile ('com.google.guava:guava:27.0.1-jre') +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/9a4dd324/genericsresolver-guava/src/main/java/org/apache/tapestry5/internal/genericsresolverguava/GuavaGenericsResolver.java ---------------------------------------------------------------------- diff --git a/genericsresolver-guava/src/main/java/org/apache/tapestry5/internal/genericsresolverguava/GuavaGenericsResolver.java b/genericsresolver-guava/src/main/java/org/apache/tapestry5/internal/genericsresolverguava/GuavaGenericsResolver.java new file mode 100644 index 0000000..a7f97c5 --- /dev/null +++ b/genericsresolver-guava/src/main/java/org/apache/tapestry5/internal/genericsresolverguava/GuavaGenericsResolver.java @@ -0,0 +1,63 @@ +// 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 org.apache.tapestry5.internal.genericsresolverguava; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import org.apache.tapestry5.services.GenericsResolver; + +import com.google.common.reflect.TypeToken; + +/** + * {@link GuavaGenericsResolver} implementation using Guava. + */ +public class GuavaGenericsResolver implements GenericsResolver { + + @Override + public Class<?> extractGenericReturnType(Class<?> containingClass, Method method) + { + return TypeToken.of(containingClass).resolveType(method.getGenericReturnType()).getRawType(); + } + + @Override + public Class extractGenericFieldType(Class containingClass, Field field) + { + return TypeToken.of(containingClass).resolveType(field.getGenericType()).getRawType(); + } + + @Override + public Type extractActualType(Type containingType, Method method) + { + return TypeToken.of(containingType).resolveType(method.getGenericReturnType()).getType(); + } + + @Override + public Type extractActualType(Type containingType, Field field) + { + return TypeToken.of(containingType).resolveType(field.getGenericType()).getType(); + } + + @Override + public Type resolve(Type type, Type containingType) + { + return TypeToken.of(containingType).resolveType(type).getType(); + } + + @Override + public Class asClass(Type actualType) { + return TypeToken.of(actualType).getRawType(); + } + +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/9a4dd324/genericsresolver-guava/src/main/resources/META-INF/services/org.apache.tapestry5.services.GenericsResolver ---------------------------------------------------------------------- diff --git a/genericsresolver-guava/src/main/resources/META-INF/services/org.apache.tapestry5.services.GenericsResolver b/genericsresolver-guava/src/main/resources/META-INF/services/org.apache.tapestry5.services.GenericsResolver new file mode 100644 index 0000000..5085652 --- /dev/null +++ b/genericsresolver-guava/src/main/resources/META-INF/services/org.apache.tapestry5.services.GenericsResolver @@ -0,0 +1 @@ +org.apache.tapestry5.internal.genericsresolverguava.GuavaGenericsResolver \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/9a4dd324/genericsresolver-guava/src/test/conf/.gitignore ---------------------------------------------------------------------- diff --git a/genericsresolver-guava/src/test/conf/.gitignore b/genericsresolver-guava/src/test/conf/.gitignore new file mode 100644 index 0000000..a3f142a --- /dev/null +++ b/genericsresolver-guava/src/test/conf/.gitignore @@ -0,0 +1 @@ +/testng.xml
