blob: f0c37506bf82cd171b699db8f38feb6d5da42c0b [file] [log] [blame]
// Copyright 2008 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.ioc.internal.util;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
/**
* Static methods related to the use of JDK 1.5 generics.
*/
public class GenericsUtils
{
/**
* Analyzes the method (often defined in a base class) in the context of a particular concrete implementation of the
* class to establish the generic type of a property. This works when the property type is defined as a class
* generic parameter.
*
* @param type base type for evaluation
* @param method method (possibly from a base class of type) to extract
* @return the generic type if it may be determined, or the raw type (that is, with type erasure, most often
* Object)
*/
public static Class extractGenericReturnType(Class type, Method method)
{
Class defaultType = method.getReturnType();
Type genericType = method.getGenericReturnType();
// We can only handle the case where you "lock down" a generic type to a specific type.
if (genericType instanceof TypeVariable)
{
// An odd name for the method that gives you access to the type parameters
// used when implementing this class. When you say Bean<String>, the first
// type variable of the generic superclass is class String.
Type superType = type.getGenericSuperclass();
if (superType instanceof ParameterizedType)
{
ParameterizedType superPType = (ParameterizedType) superType;
TypeVariable tv = (TypeVariable) genericType;
String name = tv.getName();
TypeVariable[] typeVariables = tv.getGenericDeclaration().getTypeParameters();
for (int i = 0; i < typeVariables.length; i++)
{
TypeVariable stv = typeVariables[i];
// We're trying to match the name of the type variable that is used as the return type
// of the method. With that name, we find the corresponding index in the
// type declarations. With the index, we check superPType for the Class instance
// that defines it. Generics has lots of other options that we simply can't handle.
if (stv.getName().equals(name))
{
Type actualType = superPType.getActualTypeArguments()[i];
if (actualType instanceof Class) return (Class) actualType;
break;
}
}
}
}
return defaultType;
// P.S. I wrote this and I barely understand it. Fortunately, I have tests ...
}
}