You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by gn...@apache.org on 2018/01/30 22:05:18 UTC
svn commit: r1822706 [1/2] - in /aries/trunk/blueprint/blueprint-core/src:
main/java/org/apache/aries/blueprint/container/
main/java/org/apache/aries/blueprint/utils/generics/
test/java/org/apache/aries/blueprint/
test/java/org/apache/aries/blueprint/c...
Author: gnodet
Date: Tue Jan 30 22:05:17 2018
New Revision: 1822706
URL: http://svn.apache.org/viewvc?rev=1822706&view=rev
Log:
[ARIES-960] Improve generics support to support type inference
Added:
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/ClassUtil.java
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/GenericsUtil.java
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbGenericArrayTypeImpl.java
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbParametrizedTypeImpl.java
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbTypeVariableImpl.java
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbWildcardTypeImpl.java
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/TypeInference.java
aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/container/TypeInferenceTest.java
aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/DummyServiceTrackerCustomizer.java
aries/trunk/blueprint/blueprint-core/src/test/resources/test-generics-mix.xml
Modified:
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java
aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java
aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/container/BeanRecipeTest.java
Modified: aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java?rev=1822706&r1=1822705&r2=1822706&view=diff
==============================================================================
--- aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java (original)
+++ aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java Tue Jan 30 22:05:17 2018
@@ -21,7 +21,6 @@ package org.apache.aries.blueprint.conta
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
@@ -45,6 +44,8 @@ import org.apache.aries.blueprint.proxy.
import org.apache.aries.blueprint.services.ExtendedBlueprintContainer;
import org.apache.aries.blueprint.utils.ReflectionUtils;
import org.apache.aries.blueprint.utils.ReflectionUtils.PropertyDescriptor;
+import org.apache.aries.blueprint.utils.generics.OwbParametrizedTypeImpl;
+import org.apache.aries.blueprint.utils.generics.TypeInference;
import org.apache.aries.proxy.UnableToProxyException;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@@ -55,7 +56,6 @@ import org.osgi.service.blueprint.reflec
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static org.apache.aries.blueprint.utils.ReflectionUtils.getPublicMethods;
import static org.apache.aries.blueprint.utils.ReflectionUtils.getRealCause;
/**
@@ -342,10 +342,10 @@ public class BeanRecipe extends Abstract
throw new ComponentDefinitionException("No factoryMethod nor class is defined for this bean");
}
// Map of matching constructors
- Map<Constructor, List<Object>> matches = findMatchingConstructors(getType(), args, argTypes);
+ Map<Constructor<?>, List<Object>> matches = findMatchingConstructors(getType(), args, argTypes);
if (matches.size() == 1) {
try {
- Map.Entry<Constructor, List<Object>> match = matches.entrySet().iterator().next();
+ Map.Entry<Constructor<?>, List<Object>> match = matches.entrySet().iterator().next();
return newInstance(match.getKey(), match.getValue().toArray());
} catch (Throwable e) {
throw wrapAsCompDefEx(e);
@@ -366,271 +366,102 @@ public class BeanRecipe extends Abstract
return type == null ? null : type.getName();
}
- private Map<Method, List<Object>> findMatchingMethods(Class type, String name, boolean instance, List<Object> args, List<ReifiedType> types) {
- Map<Method, List<Object>> matches = new HashMap<Method, List<Object>>();
- // Get constructors
- List<Method> methods = new ArrayList<Method>(Arrays.asList(getPublicMethods(type)));
- // Discard any signature with wrong cardinality
- for (Iterator<Method> it = methods.iterator(); it.hasNext();) {
- Method mth = it.next();
- if (!mth.getName().equals(name)) {
- it.remove();
- } else if (mth.getParameterTypes().length != args.size()) {
- it.remove();
- } else if (instance ^ !Modifier.isStatic(mth.getModifiers())) {
- it.remove();
- } else if (mth.isBridge()) {
- it.remove();
- }
- }
-
- // on some JVMs (J9) hidden static methods are returned by Class.getMethods so we need to weed them out
- // to reduce ambiguity
- if (!instance) {
- methods = applyStaticHidingRules(methods);
- }
-
- // Find a direct match with assignment
- if (matches.size() != 1) {
- Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
- for (Method mth : methods) {
- boolean found = true;
- List<Object> match = new ArrayList<Object>();
- for (int i = 0; i < args.size(); i++) {
- ReifiedType argType = new GenericType(mth.getGenericParameterTypes()[i]);
- if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
- found = false;
- break;
- }
- // If the arg is an Unwrappered bean then we need to do the assignment
- // check against the unwrappered bean itself.
- Object arg = args.get(i);
- Object argToTest = arg;
- if (arg instanceof UnwrapperedBeanHolder)
- argToTest = ((UnwrapperedBeanHolder)arg).unwrapperedBean;
- if (!AggregateConverter.isAssignable(argToTest, argType)) {
- found = false;
- break;
- }
- try {
- match.add(convert(arg, mth.getGenericParameterTypes()[i]));
- } catch (Throwable t) {
- found = false;
- break;
- }
- }
- if (found) {
- nmatches.put(mth, match);
+ private Map<Constructor<?>, List<Object>> findMatchingConstructors(Class type, List<Object> args, List<ReifiedType> types) {
+ List<TypeInference.TypedObject> targs = getTypedObjects(args, types);
+ TypeInference.Converter cnv = new TIConverter();
+ List<TypeInference.Match<Constructor<?>>> m = TypeInference.findMatchingConstructors(type, targs, cnv, reorderArguments);
+ if (!m.isEmpty()) {
+ int score = m.iterator().next().getScore();
+ final Iterator<TypeInference.Match<Constructor<?>>> each = m.iterator();
+ while (each.hasNext()) {
+ if (each.next().getScore() > score) {
+ each.remove();
}
}
- if (nmatches.size() > 0) {
- matches = nmatches;
- }
}
- // Find a direct match with conversion
- if (matches.size() != 1) {
- Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
- for (Method mth : methods) {
- boolean found = true;
- List<Object> match = new ArrayList<Object>();
- for (int i = 0; i < args.size(); i++) {
- ReifiedType argType = new GenericType(mth.getGenericParameterTypes()[i]);
- if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
- found = false;
- break;
- }
- try {
- Object val = convert(args.get(i), argType);
- match.add(val);
- } catch (Throwable t) {
- found = false;
- break;
- }
- }
- if (found) {
- nmatches.put(mth, match);
- }
- }
- if (nmatches.size() > 0) {
- matches = nmatches;
+ Map<Constructor<?>, List<Object>> map = new HashMap<Constructor<?>, List<Object>>();
+ for (TypeInference.Match<Constructor<?>> match : m) {
+ List<Object> nargs = new ArrayList<Object>();
+ for (TypeInference.TypedObject to : match.getArgs()) {
+ nargs.add(to.getValue());
}
+ map.put(match.getMember(), nargs);
}
- // Start reordering with assignment
- if (matches.size() != 1 && reorderArguments && args.size() > 1) {
- Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
- for (Method mth : methods) {
- ArgumentMatcher matcher = new ArgumentMatcher(mth.getGenericParameterTypes(), false);
- List<Object> match = matcher.match(args, types);
- if (match != null) {
- nmatches.put(mth, match);
- }
- }
- if (nmatches.size() > 0) {
- matches = nmatches;
- }
- }
- // Start reordering with conversion
- if (matches.size() != 1 && reorderArguments && args.size() > 1) {
- Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
- for (Method mth : methods) {
- ArgumentMatcher matcher = new ArgumentMatcher(mth.getGenericParameterTypes(), true);
- List<Object> match = matcher.match(args, types);
- if (match != null) {
- nmatches.put(mth, match);
- }
- }
- if (nmatches.size() > 0) {
- matches = nmatches;
- }
- }
-
- return matches;
- }
-
- private static List<Method> applyStaticHidingRules(Collection<Method> methods) {
- List<Method> result = new ArrayList<Method>(methods.size());
- for (Method m : methods) {
- boolean toBeAdded = true;
-
- Iterator<Method> it = result.iterator();
- inner: while (it.hasNext()) {
- Method other = it.next();
- if (hasIdenticalParameters(m, other)) {
- Class<?> mClass = m.getDeclaringClass();
- Class<?> otherClass = other.getDeclaringClass();
-
- if (mClass.isAssignableFrom(otherClass)) {
- toBeAdded = false;
- break inner;
- } else if (otherClass.isAssignableFrom(mClass)) {
- it.remove();
- }
- }
- }
-
- if (toBeAdded) result.add(m);
- }
-
- return result;
- }
-
- private static boolean hasIdenticalParameters(Method one, Method two) {
- Class<?>[] oneTypes = one.getParameterTypes();
- Class<?>[] twoTypes = two.getParameterTypes();
-
- if (oneTypes.length != twoTypes.length) return false;
-
- for (int i=0; i<oneTypes.length; i++) {
- if (!oneTypes[i].equals(twoTypes[i])) return false;
- }
-
- return true;
- }
-
- private Map<Constructor, List<Object>> findMatchingConstructors(Class type, List<Object> args, List<ReifiedType> types) {
- Map<Constructor, List<Object>> matches = new HashMap<Constructor, List<Object>>();
- // Get constructors
- List<Constructor> constructors = new ArrayList<Constructor>(Arrays.asList(type.getConstructors()));
- // Discard any signature with wrong cardinality
- for (Iterator<Constructor> it = constructors.iterator(); it.hasNext();) {
- if (it.next().getParameterTypes().length != args.size()) {
- it.remove();
- }
- }
- // Find a direct match with assignment
- if (matches.size() != 1) {
- Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
- for (Constructor cns : constructors) {
- boolean found = true;
- List<Object> match = new ArrayList<Object>();
- for (int i = 0; i < args.size(); i++) {
- ReifiedType argType = new GenericType(cns.getGenericParameterTypes()[i]);
- if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
- found = false;
- break;
- }
- // If the arg is an Unwrappered bean then we need to do the assignment
- // check against the unwrappered bean itself.
- Object arg = args.get(i);
- Object argToTest = arg;
- if (arg instanceof UnwrapperedBeanHolder)
- argToTest = ((UnwrapperedBeanHolder)arg).unwrapperedBean;
- if (!AggregateConverter.isAssignable(argToTest, argType)) {
- found = false;
- break;
- }
- try {
- match.add(convert(arg, cns.getGenericParameterTypes()[i]));
- } catch (Throwable t) {
- found = false;
- break;
- }
- }
- if (found) {
- nmatches.put(cns, match);
- }
- }
- if (nmatches.size() > 0) {
- matches = nmatches;
- }
+ return map;
+ }
+
+ private Map<Method, List<Object>> findMatchingMethods(Class type, String name, boolean instance, List<Object> args, List<ReifiedType> types) {
+ List<TypeInference.TypedObject> targs = getTypedObjects(args, types);
+ TypeInference.Converter cnv = new TIConverter();
+ List<TypeInference.Match<Method>> m;
+ if (instance) {
+ m = TypeInference.findMatchingMethods(type, name, targs, cnv, reorderArguments);
+ } else {
+ m = TypeInference.findMatchingStatics(type, name, targs, cnv, reorderArguments);
}
- // Find a direct match with conversion
- if (matches.size() != 1) {
- Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
- for (Constructor cns : constructors) {
- boolean found = true;
- List<Object> match = new ArrayList<Object>();
- for (int i = 0; i < args.size(); i++) {
- ReifiedType argType = new GenericType(cns.getGenericParameterTypes()[i]);
- if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
- found = false;
- break;
- }
- try {
- Object val = convert(args.get(i), argType);
- match.add(val);
- } catch (Throwable t) {
- found = false;
- break;
- }
+ if (!m.isEmpty()) {
+ int score = m.iterator().next().getScore();
+ final Iterator<TypeInference.Match<Method>> each = m.iterator();
+ while (each.hasNext()) {
+ if (each.next().getScore() > score) {
+ each.remove();
}
- if (found) {
- nmatches.put(cns, match);
- }
- }
- if (nmatches.size() > 0) {
- matches = nmatches;
}
}
- // Start reordering with assignment
- if (matches.size() != 1 && reorderArguments && arguments.size() > 1) {
- Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
- for (Constructor cns : constructors) {
- ArgumentMatcher matcher = new ArgumentMatcher(cns.getGenericParameterTypes(), false);
- List<Object> match = matcher.match(args, types);
- if (match != null) {
- nmatches.put(cns, match);
- }
+ Map<Method, List<Object>> map = new HashMap<Method, List<Object>>();
+ for (TypeInference.Match<Method> match : m) {
+ List<Object> nargs = new ArrayList<Object>();
+ for (TypeInference.TypedObject to : match.getArgs()) {
+ nargs.add(to.getValue());
}
- if (nmatches.size() > 0) {
- matches = nmatches;
+ map.put(match.getMember(), nargs);
+ }
+ return map;
+ }
+
+ private class TIConverter implements TypeInference.Converter {
+ public TypeInference.TypedObject convert(TypeInference.TypedObject from, Type to) throws Exception {
+ Object arg = BeanRecipe.this.convert(from.getValue(), new GenericType(to));
+ return new TypeInference.TypedObject(to, arg);
+ }
+ }
+
+ private Type toType(ReifiedType rt) {
+ if (rt.size() > 0) {
+ Type[] at = new Type[rt.size()];
+ for (int j = 0; j < at.length; j++) {
+ at[j] = toType(rt.getActualTypeArgument(j));
}
+ return new OwbParametrizedTypeImpl(null, rt.getRawClass(), at);
+ } else {
+ return rt.getRawClass();
}
- // Start reordering with conversion
- if (matches.size() != 1 && reorderArguments && arguments.size() > 1) {
- Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
- for (Constructor cns : constructors) {
- ArgumentMatcher matcher = new ArgumentMatcher(cns.getGenericParameterTypes(), true);
- List<Object> match = matcher.match(args, types);
- if (match != null) {
- nmatches.put(cns, match);
+ }
+
+ private List<TypeInference.TypedObject> getTypedObjects(List<Object> args, List<ReifiedType> types) {
+ List<TypeInference.TypedObject> targs = new ArrayList<TypeInference.TypedObject>();
+ for (int i = 0; i < args.size(); i++) {
+ Type t;
+ Object o = args.get(i);
+ ReifiedType rt = types.get(i);
+ if (rt == null && o != null) {
+ Object ot = o instanceof UnwrapperedBeanHolder ? ((UnwrapperedBeanHolder) o).unwrapperedBean : o;
+ rt = new GenericType(ot.getClass());
+ }
+ if (rt != null) {
+ if (rt.size() == 0 && rt.getRawClass().getTypeParameters().length > 0 && rt.getRawClass() != Class.class) {
+ GenericType[] tt = new GenericType[rt.getRawClass().getTypeParameters().length];
+ Arrays.fill(tt, 0, tt.length, new GenericType(Object.class));
+ rt = new GenericType(rt.getRawClass(), tt);
}
+ t = toType(rt);
+ } else {
+ t = null;
}
- if (nmatches.size() > 0) {
- matches = nmatches;
- }
+
+ targs.add(new TypeInference.TypedObject(t, o));
}
- return matches;
+ return targs;
}
/**
@@ -1028,7 +859,7 @@ public class BeanRecipe extends Abstract
List<Object> list = new ArrayList<Object>();
for (TypeEntry entry : entries) {
if (entry.argument == UNMATCHED) {
- throw new RuntimeException("There are unmatched types");
+ throw new RuntimeException("There are unmatched generics");
} else {
list.add(entry.argument);
}
Added: aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/ClassUtil.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/ClassUtil.java?rev=1822706&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/ClassUtil.java (added)
+++ aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/ClassUtil.java Tue Jan 30 22:05:17 2018
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.aries.blueprint.utils.generics;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+
+/**
+ * Utility classes with respect to the class operations.
+ *
+ * @author <a href="mailto:gurkanerdogdu@yahoo.com">Gurkan Erdogdu</a>
+ * @since 1.0
+ */
+public final class ClassUtil
+{
+ public static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPERS_MAP;
+
+ static
+ {
+ Map<Class<?>, Class<?>> primitiveToWrappersMap = new HashMap<Class<?>, Class<?>>();
+ primitiveToWrappersMap.put(Integer.TYPE,Integer.class);
+ primitiveToWrappersMap.put(Float.TYPE,Float.class);
+ primitiveToWrappersMap.put(Double.TYPE,Double.class);
+ primitiveToWrappersMap.put(Character.TYPE,Character.class);
+ primitiveToWrappersMap.put(Long.TYPE,Long.class);
+ primitiveToWrappersMap.put(Byte.TYPE,Byte.class);
+ primitiveToWrappersMap.put(Short.TYPE,Short.class);
+ primitiveToWrappersMap.put(Boolean.TYPE,Boolean.class);
+ primitiveToWrappersMap.put(Void.TYPE,Void.class);
+ PRIMITIVE_TO_WRAPPERS_MAP = Collections.unmodifiableMap(primitiveToWrappersMap);
+ }
+
+ public static final Type[] NO_TYPES = new Type[0];
+
+ /*
+ * Private constructor
+ */
+ private ClassUtil()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public static boolean isSame(Type type1, Type type2)
+ {
+ if ((type1 instanceof Class) && ((Class<?>)type1).isPrimitive())
+ {
+ type1 = PRIMITIVE_TO_WRAPPERS_MAP.get(type1);
+ }
+ if ((type2 instanceof Class) && ((Class<?>)type2).isPrimitive())
+ {
+ type2 = PRIMITIVE_TO_WRAPPERS_MAP.get(type2);
+ }
+ return type1 == type2;
+ }
+
+ public static Class<?> getPrimitiveWrapper(Class<?> clazz)
+ {
+ return PRIMITIVE_TO_WRAPPERS_MAP.get(clazz);
+
+ }
+
+ /**
+ * Gets the class of the given type arguments.
+ * <p>
+ * If the given type {@link Type} parameters is an instance of the
+ * {@link ParameterizedType}, it returns the raw type otherwise it return
+ * the casted {@link Class} of the type argument.
+ * </p>
+ *
+ * @param type class or parametrized type
+ * @return
+ */
+ public static Class<?> getClass(Type type)
+ {
+ return getClazz(type);
+ }
+
+
+ /**
+ * Returns true if type is an instance of <code>ParameterizedType</code>
+ * else otherwise.
+ *
+ * @param type type of the artifact
+ * @return true if type is an instance of <code>ParameterizedType</code>
+ */
+ public static boolean isParametrizedType(Type type)
+ {
+ return type instanceof ParameterizedType;
+ }
+
+ /**
+ * Returns true if type is an instance of <code>WildcardType</code>
+ * else otherwise.
+ *
+ * @param type type of the artifact
+ * @return true if type is an instance of <code>WildcardType</code>
+ */
+ public static boolean isWildCardType(Type type)
+ {
+ return type instanceof WildcardType;
+ }
+
+
+ /**
+ * Returns true if rhs is assignable type
+ * to the lhs, false otherwise.
+ *
+ * @param lhs left hand side class
+ * @param rhs right hand side class
+ * @return true if rhs is assignable to lhs
+ */
+ public static boolean isClassAssignableFrom(Class<?> lhs, Class<?> rhs)
+ {
+ if(lhs.isPrimitive())
+ {
+ lhs = getPrimitiveWrapper(lhs);
+ }
+
+ if(rhs.isPrimitive())
+ {
+ rhs = getPrimitiveWrapper(rhs);
+ }
+
+ if (lhs.isAssignableFrom(rhs))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return raw class type for given type.
+ * @param type base type instance
+ * @return class type for given type
+ */
+ public static Class<?> getClazz(Type type)
+ {
+ if(type instanceof ParameterizedType)
+ {
+ ParameterizedType pt = (ParameterizedType)type;
+ return (Class<?>)pt.getRawType();
+ }
+ else if(type instanceof Class)
+ {
+ return (Class<?>)type;
+ }
+ else if(type instanceof GenericArrayType)
+ {
+ GenericArrayType arrayType = (GenericArrayType)type;
+ return Array.newInstance(getClazz(arrayType.getGenericComponentType()), 0).getClass();
+ }
+ else if (type instanceof WildcardType)
+ {
+ WildcardType wildcardType = (WildcardType)type;
+ Type[] bounds = wildcardType.getUpperBounds();
+ if (bounds.length > 1)
+ {
+ throw new IllegalArgumentException("Illegal use of wild card type with more than one upper bound: " + wildcardType);
+ }
+ else if (bounds.length == 0)
+ {
+ return Object.class;
+ }
+ else
+ {
+ return getClass(bounds[0]);
+ }
+ }
+ else if (type instanceof TypeVariable)
+ {
+ TypeVariable<?> typeVariable = (TypeVariable<?>)type;
+ if (typeVariable.getBounds().length > 1)
+ {
+ throw new IllegalArgumentException("Illegal use of type variable with more than one bound: " + typeVariable);
+ }
+ else
+ {
+ Type[] bounds = typeVariable.getBounds();
+ if (bounds.length == 0)
+ {
+ return Object.class;
+ }
+ else
+ {
+ return getClass(bounds[0]);
+ }
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported type " + type);
+ }
+ }
+
+
+ public static boolean isRawClassEquals(Type ipType, Type apiType)
+ {
+ Class ipClass = getRawPrimitiveType(ipType);
+ Class apiClass = getRawPrimitiveType(apiType);
+
+ if (ipClass == null || apiClass == null)
+ {
+ // we found some illegal generics
+ return false;
+ }
+
+ return ipClass.equals(apiClass);
+ }
+
+ private static Class getRawPrimitiveType(Type type)
+ {
+ if (type instanceof Class)
+ {
+ if (((Class) type).isPrimitive())
+ {
+ return getPrimitiveWrapper((Class) type);
+ }
+ return (Class) type;
+ }
+
+ if (type instanceof ParameterizedType)
+ {
+ return getRawPrimitiveType(((ParameterizedType) type).getRawType());
+ }
+
+ return null;
+ }
+}
Added: aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/GenericsUtil.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/GenericsUtil.java?rev=1822706&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/GenericsUtil.java (added)
+++ aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/GenericsUtil.java Tue Jan 30 22:05:17 2018
@@ -0,0 +1,1154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.aries.blueprint.utils.generics;
+
+
+import java.lang.reflect.*;
+import java.util.*;
+
+/**
+ * Utility classes for generic type operations.
+ */
+public final class GenericsUtil
+{
+ public static boolean satisfiesDependency(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType)
+ {
+ if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType)
+ {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+ }
+ else
+ {
+ Type injectionPointRawType = injectionPointType instanceof ParameterizedType? ((ParameterizedType)injectionPointType).getRawType(): injectionPointType;
+ Type beanRawType = beanType instanceof ParameterizedType? ((ParameterizedType)beanType).getRawType(): beanType;
+
+ if (ClassUtil.isSame(injectionPointRawType, beanRawType))
+ {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean satisfiesDependencyRaw(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType)
+ {
+ if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType)
+ {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+ }
+ else
+ {
+ Type injectionPointRawType = injectionPointType instanceof ParameterizedType? ((ParameterizedType)injectionPointType).getRawType(): injectionPointType;
+ Type beanRawType = beanType instanceof ParameterizedType? ((ParameterizedType)beanType).getRawType(): beanType;
+
+ if (ClassUtil.isSame(injectionPointRawType, beanRawType))
+ {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointRawType, beanRawType);
+ }
+ else
+ {
+ Class bean = (Class) beanType;
+ if (bean.getSuperclass() != null && ClassUtil.isRawClassEquals(injectionPointType, bean.getSuperclass()))
+ {
+ return true;
+ }
+
+ Class<?>[] interfaces = bean.getInterfaces();
+ if (interfaces == null || interfaces.length == 0)
+ {
+ return false;
+ }
+
+ for (Class<?> clazz : interfaces)
+ {
+ if (ClassUtil.isRawClassEquals(injectionPointType, clazz))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 5.2.3 and 5.2.4
+ */
+ public static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, Type requiredType, Type beanType)
+ {
+ if (requiredType instanceof Class)
+ {
+ return isAssignableFrom(isDelegateOrEvent, (Class<?>)requiredType, beanType);
+ }
+ else if (requiredType instanceof ParameterizedType)
+ {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, (ParameterizedType)requiredType, beanType);
+ }
+ else if (requiredType instanceof TypeVariable)
+ {
+ return isAssignableFrom(isDelegateOrEvent, (TypeVariable<?>)requiredType, beanType);
+ }
+ else if (requiredType instanceof GenericArrayType)
+ {
+ return Class.class.isInstance(beanType) && Class.class.cast(beanType).isArray()
+ && isAssignableFrom(isDelegateOrEvent, (GenericArrayType)requiredType, beanType);
+ }
+ else if (requiredType instanceof WildcardType)
+ {
+ return isAssignableFrom(isDelegateOrEvent, (WildcardType)requiredType, beanType);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported type " + requiredType.getClass());
+ }
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, Type beanType)
+ {
+ if (beanType instanceof Class)
+ {
+ return isAssignableFrom(injectionPointType, (Class<?>)beanType);
+ }
+ else if (beanType instanceof TypeVariable)
+ {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable<?>)beanType);
+ }
+ else if (beanType instanceof ParameterizedType)
+ {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType)beanType);
+ }
+ else if (beanType instanceof GenericArrayType)
+ {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (GenericArrayType)beanType);
+ }
+ else if (beanType instanceof WildcardType)
+ {
+ return isAssignableFrom(isDelegateOrEvent, (Type)injectionPointType, (WildcardType)beanType);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported type " + injectionPointType.getClass());
+ }
+ }
+
+ private static boolean isAssignableFrom(Class<?> injectionPointType, Class<?> beanType)
+ {
+ return ClassUtil.isClassAssignableFrom(injectionPointType, beanType);
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, TypeVariable<?> beanType)
+ {
+ for (Type bounds: beanType.getBounds())
+ {
+ if (isAssignableFrom(isDelegateOrEvent, injectionPointType, bounds))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * CDI Spec. 5.2.4: "A parameterized bean type is considered assignable to a raw required type
+ * if the raw generics are identical and all type parameters of the bean type are either unbounded type variables or java.lang.Object."
+ */
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, ParameterizedType beanType)
+ {
+ if (beanType.getRawType() != injectionPointType)
+ {
+ return false; //raw generics don't match
+ }
+
+ if (isDelegateOrEvent)
+ {
+ // for delegate and events we match 'in reverse' kind off
+ // @Observes ProcessInjectionPoint<?, Instance> does also match Instance<SomeBean>
+ return isAssignableFrom(true, injectionPointType, beanType.getRawType());
+ }
+
+ for (Type typeArgument: beanType.getActualTypeArguments())
+ {
+ if (typeArgument == Object.class)
+ {
+ continue;
+ }
+ if (!(typeArgument instanceof TypeVariable))
+ {
+ return false; //neither object nor type variable
+ }
+ TypeVariable<?> typeVariable = (TypeVariable<?>)typeArgument;
+ for (Type bounds: typeVariable.getBounds())
+ {
+ if (bounds != Object.class)
+ {
+ return false; //bound type variable
+ }
+ }
+ }
+ return true;
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, GenericArrayType beanType)
+ {
+ return injectionPointType.isArray() && isAssignableFrom(isDelegateOrEvent, injectionPointType.getComponentType(), beanType.getGenericComponentType());
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, Type injectionPointType, WildcardType beanType)
+ {
+ for (Type bounds: beanType.getLowerBounds())
+ {
+ if (!isAssignableFrom(isDelegateOrEvent, false, bounds, injectionPointType))
+ {
+ return false;
+ }
+ }
+ for (Type bounds: beanType.getUpperBounds())
+ {
+ if (isAssignableFrom(isDelegateOrEvent, false, injectionPointType, bounds))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Type beanType)
+ {
+ if (beanType instanceof Class)
+ {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (Class<?>)beanType);
+ }
+ else if (beanType instanceof TypeVariable)
+ {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (TypeVariable<?>)beanType);
+ }
+ else if (beanType instanceof ParameterizedType)
+ {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType)beanType);
+ }
+ else if (beanType instanceof WildcardType)
+ {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (WildcardType)beanType);
+ }
+ else if (beanType instanceof GenericArrayType)
+ {
+ return false;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported type " + beanType.getClass());
+ }
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Class<?> beanType)
+ {
+ Class<?> rawInjectionPointType = getRawType(injectionPointType);
+ if (rawInjectionPointType.equals(beanType))
+ {
+ if (isProducer)
+ {
+ for (final Type t : injectionPointType.getActualTypeArguments())
+ {
+ if (!TypeVariable.class.isInstance(t) || !isNotBound(TypeVariable.class.cast(t).getBounds()))
+ {
+ if (!Class.class.isInstance(t) || Object.class != t)
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+ if (!rawInjectionPointType.isAssignableFrom(beanType))
+ {
+ return false;
+ }
+ if (beanType.getSuperclass() != null && isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType.getGenericSuperclass()))
+ {
+ return true;
+ }
+ for (Type genericInterface: beanType.getGenericInterfaces())
+ {
+ if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, genericInterface))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, TypeVariable<?> beanType)
+ {
+ final Type[] types = beanType.getBounds();
+ if (isNotBound(types))
+ {
+ return true;
+ }
+ for (final Type bounds: types)
+ {
+ if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, bounds))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * CDI Spec. 5.2.4
+ */
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, ParameterizedType injectionPointType, ParameterizedType beanType)
+ {
+ if (injectionPointType.getRawType() != beanType.getRawType())
+ {
+ return false;
+ }
+ boolean swapParams = !isDelegateOrEvent;
+ Type[] injectionPointTypeArguments = injectionPointType.getActualTypeArguments();
+ Type[] beanTypeArguments = beanType.getActualTypeArguments();
+ for (int i = 0; i < injectionPointTypeArguments.length; i++)
+ {
+ Type injectionPointTypeArgument = injectionPointTypeArguments[i];
+ Type beanTypeArgument = beanTypeArguments[i];
+
+ // for this special case it's actually an 'assignable to', thus we swap the params, see CDI-389
+ // but this special rule does not apply to Delegate injection points...
+ if (swapParams &&
+ (injectionPointTypeArgument instanceof Class || injectionPointTypeArgument instanceof TypeVariable) &&
+ beanTypeArgument instanceof TypeVariable)
+ {
+ final Type[] bounds = ((TypeVariable<?>) beanTypeArgument).getBounds();
+ final boolean isNotBound = isNotBound(bounds);
+ if (!isNotBound)
+ {
+ for (final Type upperBound : bounds)
+ {
+ if (!isAssignableFrom(true, false, upperBound, injectionPointTypeArgument))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ else if (swapParams && injectionPointTypeArgument instanceof TypeVariable)
+ {
+ return false;
+ }
+ else if (isDelegateOrEvent && injectionPointTypeArgument instanceof Class && beanTypeArgument instanceof Class)
+ {
+ // if no wildcard type was given then we require a real exact match.
+ return injectionPointTypeArgument.equals(beanTypeArgument);
+
+ }
+ else if (!isAssignableFrom(isDelegateOrEvent, false, injectionPointTypeArgument, beanTypeArgument))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean isNotBound(final Type... bounds)
+ {
+ return bounds == null || bounds.length == 0 || (bounds.length == 1 && Object.class == bounds[0]);
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, TypeVariable<?> injectionPointType, Type beanType)
+ {
+ for (Type bounds: injectionPointType.getBounds())
+ {
+ if (!isAssignableFrom(isDelegateOrEvent, false, bounds, beanType))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // rules are a bit different when in an array so we handle ParameterizedType manually (not reusing isAssignableFrom)
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, GenericArrayType injectionPointType, Type beanType)
+ {
+ final Type genericComponentType = injectionPointType.getGenericComponentType();
+ final Class componentType = Class.class.cast(beanType).getComponentType();
+ if (Class.class.isInstance(genericComponentType))
+ {
+ return Class.class.cast(genericComponentType).isAssignableFrom(componentType);
+ }
+ if (ParameterizedType.class.isInstance(genericComponentType))
+ {
+ return isAssignableFrom(isDelegateOrEvent, false, ParameterizedType.class.cast(genericComponentType).getRawType(), componentType);
+ }
+ return isAssignableFrom(isDelegateOrEvent, false, genericComponentType, componentType);
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, Type beanType)
+ {
+ if (beanType instanceof TypeVariable)
+ {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable<?>)beanType);
+ }
+ for (Type bounds: injectionPointType.getLowerBounds())
+ {
+ if (!isAssignableFrom(isDelegateOrEvent, false, beanType, bounds))
+ {
+ return false;
+ }
+ }
+ for (Type bounds: injectionPointType.getUpperBounds())
+ {
+ Set<Type> beanTypeClosure = getTypeClosure(beanType);
+ boolean isAssignable = false;
+ for (Type beanSupertype: beanTypeClosure)
+ {
+ if (isAssignableFrom(isDelegateOrEvent, false, bounds, beanSupertype)
+ || (Class.class.isInstance(bounds)
+ && ParameterizedType.class.isInstance(beanSupertype)
+ && bounds == ParameterizedType.class.cast(beanSupertype).getRawType()))
+ {
+ isAssignable = true;
+ break;
+ }
+ }
+ if (!isAssignable)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * CDI 1.1 Spec. 5.2.4, third bullet point
+ */
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, TypeVariable<?> beanType)
+ {
+ for (Type upperBound: injectionPointType.getUpperBounds())
+ {
+ for (Type bound: beanType.getBounds())
+ {
+ if (!isAssignableFrom(isDelegateOrEvent, false, upperBound, bound) && !isAssignableFrom(isDelegateOrEvent, false, bound, upperBound))
+ {
+ return false;
+ }
+ }
+ }
+ for (Type lowerBound: injectionPointType.getLowerBounds())
+ {
+ for (Type bound: beanType.getBounds())
+ {
+ if (!isAssignableFrom(isDelegateOrEvent, false, bound, lowerBound))
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return <tt>true</tt>, if the specified type declaration contains an unresolved type variable.
+ */
+ public static boolean containsTypeVariable(Type type)
+ {
+ if (type instanceof Class)
+ {
+ return false;
+ }
+ else if (type instanceof TypeVariable)
+ {
+ return true;
+ }
+ else if (type instanceof ParameterizedType)
+ {
+ ParameterizedType parameterizedType = (ParameterizedType)type;
+ return containTypeVariable(parameterizedType.getActualTypeArguments());
+ }
+ else if (type instanceof WildcardType)
+ {
+ WildcardType wildcardType = (WildcardType)type;
+ return containTypeVariable(wildcardType.getUpperBounds()) || containTypeVariable(wildcardType.getLowerBounds());
+ }
+ else if (type instanceof GenericArrayType)
+ {
+ GenericArrayType arrayType = (GenericArrayType)type;
+ return containsTypeVariable(arrayType.getGenericComponentType());
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
+ }
+
+ }
+
+ public static boolean containTypeVariable(Collection<? extends Type> types)
+ {
+ return containTypeVariable(types.toArray(new Type[types.size()]));
+ }
+
+ public static boolean containTypeVariable(Type[] types)
+ {
+ for (Type type: types)
+ {
+ if (containsTypeVariable(type))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ *
+ * @param type to check
+ *
+ * @return {@code true} if the given type contains a {@link WildcardType}
+ * {@code false} otherwise
+ */
+ public static boolean containsWildcardType(Type type)
+ {
+ if (!(type instanceof ParameterizedType))
+ {
+ return false;
+ }
+
+ for (Type typeArgument : getParameterizedType(type).getActualTypeArguments())
+ {
+ if (ClassUtil.isParametrizedType(typeArgument))
+ {
+ if (containsWildcardType(typeArgument))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (ClassUtil.isWildCardType(typeArgument))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Resolves the actual type of the specified field for the type hierarchy specified by the given subclass
+ */
+ public static Type resolveType(Class<?> subclass, Field field)
+ {
+ return resolveType(field.getGenericType(), subclass, newSeenList());
+ }
+
+ /**
+ * Resolves the actual return type of the specified method for the type hierarchy specified by the given subclass
+ */
+ public static Type resolveReturnType(Class<?> subclass, Method method)
+ {
+ return resolveType(method.getGenericReturnType(), subclass, newSeenList());
+ }
+
+ /**
+ * Resolves the actual parameter generics of the specified constructor for the type hierarchy specified by the given subclass
+ */
+ public static Type[] resolveParameterTypes(Class<?> subclass, Constructor<?> constructor)
+ {
+ return resolveTypes(constructor.getGenericParameterTypes(), subclass);
+ }
+
+ /**
+ * Resolves the actual parameter generics of the specified method for the type hierarchy specified by the given subclass
+ */
+ public static Type[] resolveParameterTypes(Class<?> subclass, Method method)
+ {
+ return resolveTypes(method.getGenericParameterTypes(), subclass);
+ }
+
+ /**
+ * Resolves the actual type of the specified type for the type hierarchy specified by the given subclass
+ */
+ public static Type resolveType(Type type, Class<?> subclass, Member member)
+ {
+ return resolveType(type, subclass, newSeenList());
+ }
+
+ public static Type resolveType(Type type, Class<?> subclass, Member member, Collection<TypeVariable<?>> seen)
+ {
+ return resolveType(type, subclass, seen);
+ }
+
+ public static Type resolveType(Type type, Type actualType, Collection<TypeVariable<?>> seen)
+ {
+ if (type instanceof Class)
+ {
+ return type;
+ }
+ else if (type instanceof ParameterizedType)
+ {
+ ParameterizedType parameterizedType = (ParameterizedType)type;
+
+ Type[] resolvedTypeArguments;
+ if (Enum.class.equals(parameterizedType.getRawType()))
+ {
+ // Enums derive from themselves, which would create an infinite loop
+ // we directly escape the loop if we detect this.
+ resolvedTypeArguments = new Type[]{new OwbWildcardTypeImpl(new Type[]{Enum.class}, ClassUtil.NO_TYPES)};
+ }
+ else
+ {
+ resolvedTypeArguments = resolveTypes(parameterizedType.getActualTypeArguments(), actualType, seen);
+
+ }
+
+ return new OwbParametrizedTypeImpl(parameterizedType.getOwnerType(), parameterizedType.getRawType(), resolvedTypeArguments);
+ }
+ else if (type instanceof TypeVariable)
+ {
+ TypeVariable<?> variable = (TypeVariable<?>)type;
+ return resolveTypeVariable(variable, actualType, seen);
+ }
+ else if (type instanceof WildcardType)
+ {
+ WildcardType wildcardType = (WildcardType)type;
+ Type[] upperBounds = resolveTypes(wildcardType.getUpperBounds(), actualType, seen);
+ Type[] lowerBounds = resolveTypes(wildcardType.getLowerBounds(), actualType, seen);
+ return new OwbWildcardTypeImpl(upperBounds, lowerBounds);
+ }
+ else if (type instanceof GenericArrayType)
+ {
+ GenericArrayType arrayType = (GenericArrayType)type;
+ return createArrayType(resolveType(arrayType.getGenericComponentType(), actualType, seen));
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
+ }
+ }
+
+ public static Type[] resolveTypes(Type[] types, Type actualType, Collection<TypeVariable<?>> seen)
+ {
+ Type[] resolvedTypeArguments = new Type[types.length];
+ for (int i = 0; i < types.length; i++)
+ {
+ final Type type = resolveType(types[i], actualType, seen);
+ if (type != null) // means a stackoverflow was avoided, just keep what we have
+ {
+ resolvedTypeArguments[i] = type;
+ }
+ }
+ return resolvedTypeArguments;
+ }
+
+ public static Type[] resolveTypes(Type[] types, Type actualType)
+ {
+ Type[] resolvedTypeArguments = new Type[types.length];
+ for (int i = 0; i < types.length; i++)
+ {
+ resolvedTypeArguments[i] = resolveType(types[i], actualType, newSeenList());
+ }
+ return resolvedTypeArguments;
+ }
+
+ public static Set<Type> getTypeClosure(Class<?> type)
+ {
+ return getTypeClosure(type, type);
+ }
+
+ public static Set<Type> getTypeClosure(Type actualType)
+ {
+ return getTypeClosure(actualType, actualType);
+ }
+
+ /**
+ * Returns the type closure for the specified parameters.
+ * <h3>Example 1:</h3>
+ * <p>
+ * Take the following classes:
+ * </p>
+ * <code>
+ * public class Foo<T> {
+ * private T t;
+ * }
+ * public class Bar extends Foo<Number> {
+ * }
+ * </code>
+ * <p>
+ * To get the type closure of T in the context of Bar (which is {Number.class, Object.class}), you have to call this method like
+ * </p>
+ * <code>
+ * GenericUtil.getTypeClosure(Foo.class.getDeclaredField("t").getType(), Bar.class, Foo.class);
+ * </code>
+ * <h3>Example 2:</h3>
+ * <p>
+ * Take the following classes:
+ * </p>
+ * <code>
+ * public class Foo<T> {
+ * private T t;
+ * }
+ * public class Bar<T> extends Foo<T> {
+ * }
+ * </code>
+ * <p>
+ * To get the type closure of Bar<T> in the context of Foo<Number> (which are besides Object.class the <tt>ParameterizedType</tt>s Bar<Number> and Foo<Number>),
+ * you have to call this method like
+ * </p>
+ * <code>
+ * GenericUtil.getTypeClosure(Foo.class, new TypeLiteral<Foo<Number>>() {}.getType(), Bar.class);
+ * </code>
+ *
+ * @param type the type to get the closure for
+ * @param actualType the context to bind type variables
+ * @return the type closure
+ */
+ public static Set<Type> getTypeClosure(Type type, Type actualType)
+ {
+ Class<?> rawType = getRawType(type);
+ Class<?> actualRawType = getRawType(actualType);
+ if (rawType.isAssignableFrom(actualRawType) && rawType != actualRawType)
+ {
+ return getTypeClosure(actualType, type);
+ }
+ if (hasTypeParameters(type))
+ {
+ type = getParameterizedType(type);
+ }
+ return getDirectTypeClosure(type, actualType);
+ }
+
+ public static Set<Type> getDirectTypeClosure(final Type type, final Type actualType)
+ {
+ Set<Type> typeClosure = new HashSet<Type>();
+ typeClosure.add(Object.class);
+ fillTypeHierarchy(typeClosure, type, actualType);
+ return typeClosure;
+ }
+
+ private static void fillTypeHierarchy(Set<Type> set, Type type, Type actualType)
+ {
+ if (type == null)
+ {
+ return;
+ }
+ Type resolvedType = GenericsUtil.resolveType(type, actualType, newSeenList());
+ set.add(resolvedType);
+ Class<?> resolvedClass = GenericsUtil.getRawType(resolvedType, actualType);
+ if (resolvedClass.getSuperclass() != null)
+ {
+ fillTypeHierarchy(set, resolvedClass.getGenericSuperclass(), resolvedType);
+ }
+ for (Type interfaceType: resolvedClass.getGenericInterfaces())
+ {
+ fillTypeHierarchy(set, interfaceType, resolvedType);
+ }
+ }
+
+ private static Collection<TypeVariable<?>> newSeenList()
+ {
+ return new ArrayList<TypeVariable<?>>();
+ }
+
+ public static boolean hasTypeParameters(Type type)
+ {
+ if (type instanceof Class)
+ {
+ Class<?> classType = (Class<?>)type;
+ return classType.getTypeParameters().length > 0;
+ }
+ return false;
+ }
+
+ public static ParameterizedType getParameterizedType(Type type)
+ {
+ if (type instanceof ParameterizedType)
+ {
+ return (ParameterizedType)type;
+ }
+ else if (type instanceof Class)
+ {
+ Class<?> classType = (Class<?>)type;
+ return new OwbParametrizedTypeImpl(classType.getDeclaringClass(), classType, classType.getTypeParameters());
+ }
+ else
+ {
+ throw new IllegalArgumentException(type.getClass().getSimpleName() + " is not supported");
+ }
+ }
+
+ public static <T> Class<T> getRawType(Type type)
+ {
+ return getRawType(type, null);
+ }
+
+ static <T> Class<T> getRawType(Type type, Type actualType)
+ {
+ if (type instanceof Class)
+ {
+ return (Class<T>)type;
+ }
+ else if (type instanceof ParameterizedType)
+ {
+ ParameterizedType parameterizedType = (ParameterizedType)type;
+ return getRawType(parameterizedType.getRawType(), actualType);
+ }
+ else if (type instanceof TypeVariable)
+ {
+ TypeVariable<?> typeVariable = (TypeVariable<?>)type;
+ Type mostSpecificType = getMostSpecificType(getRawTypes(typeVariable.getBounds(), actualType), typeVariable.getBounds());
+ return getRawType(mostSpecificType, actualType);
+ }
+ else if (type instanceof WildcardType)
+ {
+ WildcardType wildcardType = (WildcardType)type;
+ Type mostSpecificType = getMostSpecificType(getRawTypes(wildcardType.getUpperBounds(), actualType), wildcardType.getUpperBounds());
+ return getRawType(mostSpecificType, actualType);
+ }
+ else if (type instanceof GenericArrayType)
+ {
+ GenericArrayType arrayType = (GenericArrayType)type;
+ return getRawType(createArrayType(getRawType(arrayType.getGenericComponentType(), actualType)), actualType);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
+ }
+ }
+
+ private static <T> Class<T>[] getRawTypes(Type[] types)
+ {
+ return getRawTypes(types, null);
+ }
+
+ private static <T> Class<T>[] getRawTypes(Type[] types, Type actualType)
+ {
+ Class<T>[] rawTypes = new Class[types.length];
+ for (int i = 0; i < types.length; i++)
+ {
+ rawTypes[i] = getRawType(types[i], actualType);
+ }
+ return rawTypes;
+ }
+
+ private static Type getMostSpecificType(Class<?>[] types, Type[] genericTypes)
+ {
+ Class<?> mostSpecificType = types[0];
+ int mostSpecificIndex = 0;
+ for (int i = 0; i < types.length; i++)
+ {
+ if (mostSpecificType.isAssignableFrom(types[i]))
+ {
+ mostSpecificType = types[i];
+ mostSpecificIndex = i;
+ }
+ }
+ return genericTypes[mostSpecificIndex];
+ }
+
+ private static Class<?>[] getClassTypes(Class<?>[] rawTypes)
+ {
+ List<Class<?>> classTypes = new ArrayList<Class<?>>();
+ for (Class<?> rawType : rawTypes)
+ {
+ if (!rawType.isInterface())
+ {
+ classTypes.add(rawType);
+ }
+ }
+ return classTypes.toArray(new Class[classTypes.size()]);
+ }
+
+ private static Type resolveTypeVariable(TypeVariable<?> variable, Type actualType, Collection<TypeVariable<?>> seen)
+ {
+ if (actualType == null)
+ {
+ return variable;
+ }
+ Class<?> declaringClass = getDeclaringClass(variable.getGenericDeclaration());
+ Class<?> actualClass = getRawType(actualType);
+ if (actualClass == declaringClass)
+ {
+ return resolveTypeVariable(variable, variable.getGenericDeclaration(), getParameterizedType(actualType), seen);
+ }
+ else if (actualClass.isAssignableFrom(declaringClass))
+ {
+ Class<?> directSubclass = getDirectSubclass(declaringClass, actualClass);
+ Type[] typeArguments = resolveTypeArguments(directSubclass, actualType);
+ Type directSubtype = new OwbParametrizedTypeImpl(directSubclass.getDeclaringClass(), directSubclass, typeArguments);
+ return resolveTypeVariable(variable, directSubtype, seen);
+ }
+ else // if (declaringClass.isAssignableFrom(actualClass))
+ {
+ Type genericSuperclass = getGenericSuperclass(actualClass, declaringClass);
+ if (genericSuperclass == null)
+ {
+ return variable;
+ }
+ else if (genericSuperclass instanceof Class)
+ {
+ // special handling for type erasure
+ Class<?> superclass = (Class<?>)genericSuperclass;
+ genericSuperclass = new OwbParametrizedTypeImpl(superclass.getDeclaringClass(), superclass, getRawTypes(superclass.getTypeParameters()));
+ }
+ else
+ {
+ ParameterizedType genericSupertype = getParameterizedType(genericSuperclass);
+ Type[] typeArguments = resolveTypeArguments(getParameterizedType(actualType), genericSupertype);
+ genericSuperclass = new OwbParametrizedTypeImpl(genericSupertype.getOwnerType(), genericSupertype.getRawType(), typeArguments);
+ }
+ Type resolvedType = resolveTypeVariable(variable, genericSuperclass, seen);
+ if (resolvedType instanceof TypeVariable)
+ {
+ TypeVariable<?> resolvedTypeVariable = (TypeVariable<?>)resolvedType;
+ TypeVariable<?>[] typeParameters = actualClass.getTypeParameters();
+ for (int i = 0; i < typeParameters.length; i++)
+ {
+ if (typeParameters[i].getName().equals(resolvedTypeVariable.getName()))
+ {
+ resolvedType = getParameterizedType(actualType).getActualTypeArguments()[i];
+ break;
+ }
+ }
+ }
+ return resolvedType;
+ }
+ }
+
+ private static Class<?> getDeclaringClass(GenericDeclaration declaration)
+ {
+ if (declaration instanceof Class)
+ {
+ return (Class<?>)declaration;
+ }
+ else if (declaration instanceof Member)
+ {
+ return ((Member)declaration).getDeclaringClass();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported type " + declaration.getClass());
+ }
+ }
+
+ private static Type resolveTypeVariable(TypeVariable<?> variable, GenericDeclaration declaration, ParameterizedType type,
+ Collection<TypeVariable<?>> seen)
+ {
+ int index = getIndex(declaration, variable);
+ if (declaration instanceof Class)
+ {
+ if (index >= 0)
+ {
+ return type.getActualTypeArguments()[index];
+ }
+ else
+ {
+ index = getIndex(type, variable);
+ if (index >= 0)
+ {
+ return declaration.getTypeParameters()[index];
+ }
+ }
+ }
+ else
+ {
+ if (seen.contains(variable))
+ {
+ return null;
+ }
+ seen.add(variable);
+
+ Type[] resolvedBounds = resolveTypes(declaration.getTypeParameters()[index].getBounds(), type, seen);
+ return OwbTypeVariableImpl.createTypeVariable(variable, resolvedBounds);
+ }
+ return variable;
+ }
+
+ private static int getIndex(GenericDeclaration declaration, TypeVariable<?> variable)
+ {
+ Type[] typeParameters = declaration.getTypeParameters();
+ for (int i = 0; i < typeParameters.length; i++)
+ {
+ if (typeParameters[i] instanceof TypeVariable)
+ {
+ TypeVariable<?> variableArgument = (TypeVariable<?>)typeParameters[i];
+ if (variableArgument.getName().equals(variable.getName()))
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ private static int getIndex(ParameterizedType type, TypeVariable<?> variable)
+ {
+ Type[] actualTypeArguments = type.getActualTypeArguments();
+ for (int i = 0; i < actualTypeArguments.length; i++)
+ {
+ if (actualTypeArguments[i] instanceof TypeVariable)
+ {
+ TypeVariable<?> variableArgument = (TypeVariable<?>)actualTypeArguments[i];
+ if (variableArgument.getName().equals(variable.getName()))
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ private static Class<?> getDirectSubclass(Class<?> declaringClass, Class<?> actualClass)
+ {
+ if (actualClass.isInterface())
+ {
+ Class<?> subclass = declaringClass;
+ for (Class<?> iface: declaringClass.getInterfaces())
+ {
+ if (iface == actualClass)
+ {
+ return subclass;
+ }
+ if (actualClass.isAssignableFrom(iface))
+ {
+ subclass = iface;
+ }
+ else
+ {
+ subclass = declaringClass.getSuperclass();
+ }
+ }
+ return getDirectSubclass(subclass, actualClass);
+ }
+ else
+ {
+ Class<?> directSubclass = declaringClass;
+ while (directSubclass.getSuperclass() != actualClass)
+ {
+ directSubclass = directSubclass.getSuperclass();
+ }
+ return directSubclass;
+ }
+ }
+
+ private static Type getGenericSuperclass(Class<?> subclass, Class<?> superclass)
+ {
+ if (!superclass.isInterface())
+ {
+ return subclass.getGenericSuperclass();
+ }
+ else
+ {
+ for (Type genericInterface: subclass.getGenericInterfaces())
+ {
+ if (getRawType(genericInterface) == superclass)
+ {
+ return genericInterface;
+ }
+ }
+ }
+ return superclass;
+ }
+
+ private static Type[] resolveTypeArguments(Class<?> subclass, Type supertype)
+ {
+ if (supertype instanceof ParameterizedType)
+ {
+ ParameterizedType parameterizedSupertype = (ParameterizedType)supertype;
+ return resolveTypeArguments(subclass, parameterizedSupertype);
+ }
+ else
+ {
+ return subclass.getTypeParameters();
+ }
+ }
+
+ private static Type[] resolveTypeArguments(Class<?> subclass, ParameterizedType parameterizedSupertype)
+ {
+ Type genericSuperclass = getGenericSuperclass(subclass, getRawType(parameterizedSupertype));
+ if (!(genericSuperclass instanceof ParameterizedType))
+ {
+ return subclass.getTypeParameters();
+ }
+ ParameterizedType parameterizedSuperclass = (ParameterizedType)genericSuperclass;
+ Type[] typeParameters = subclass.getTypeParameters();
+ Type[] actualTypeArguments = parameterizedSupertype.getActualTypeArguments();
+ return resolveTypeArguments(parameterizedSuperclass, typeParameters, actualTypeArguments);
+ }
+
+ private static Type[] resolveTypeArguments(ParameterizedType subtype, ParameterizedType parameterizedSupertype)
+ {
+ return resolveTypeArguments(getParameterizedType(getRawType(subtype)), parameterizedSupertype.getActualTypeArguments(), subtype.getActualTypeArguments());
+ }
+
+ private static Type[] resolveTypeArguments(ParameterizedType parameterizedType, Type[] typeParameters, Type[] actualTypeArguments)
+ {
+ Type[] resolvedTypeArguments = new Type[typeParameters.length];
+ for (int i = 0; i < typeParameters.length; i++)
+ {
+ resolvedTypeArguments[i] = resolveTypeArgument(parameterizedType, typeParameters[i], actualTypeArguments);
+ }
+ return resolvedTypeArguments;
+ }
+
+ private static Type resolveTypeArgument(ParameterizedType parameterizedType, Type typeParameter, Type[] actualTypeArguments)
+ {
+ if (typeParameter instanceof TypeVariable)
+ {
+ TypeVariable<?> variable = (TypeVariable<?>)typeParameter;
+ int index = getIndex(parameterizedType, variable);
+ if (index == -1)
+ {
+ return typeParameter;
+ }
+ else
+ {
+ return actualTypeArguments[index];
+ }
+ }
+ else if (typeParameter instanceof GenericArrayType)
+ {
+ GenericArrayType array = (GenericArrayType)typeParameter;
+ return createArrayType(resolveTypeArgument(parameterizedType, array.getGenericComponentType(), actualTypeArguments));
+ }
+ else
+ {
+ return typeParameter;
+ }
+ }
+
+ private static Type createArrayType(Type componentType)
+ {
+ if (componentType instanceof Class)
+ {
+ return Array.newInstance((Class<?>)componentType, 0).getClass();
+ }
+ else
+ {
+ return new OwbGenericArrayTypeImpl(componentType);
+ }
+ }
+
+ public static Type resolveType(ParameterizedType parameterizedType, Type metadataType)
+ {
+ return resolveType(parameterizedType, metadataType, newSeenList());
+ }
+}
Added: aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbGenericArrayTypeImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbGenericArrayTypeImpl.java?rev=1822706&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbGenericArrayTypeImpl.java (added)
+++ aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbGenericArrayTypeImpl.java Tue Jan 30 22:05:17 2018
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.aries.blueprint.utils.generics;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+
+
+public class OwbGenericArrayTypeImpl implements GenericArrayType
+{
+
+ private Type componentType;
+
+ public OwbGenericArrayTypeImpl(Type componentType)
+ {
+ this.componentType = componentType;
+ }
+
+ @Override
+ public Type getGenericComponentType()
+ {
+ return componentType;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ return componentType.hashCode();
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (obj instanceof GenericArrayType)
+ {
+ return ((GenericArrayType)obj).getGenericComponentType().equals(componentType);
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ public String toString()
+ {
+ return componentType + "[]";
+ }
+}
Added: aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbParametrizedTypeImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbParametrizedTypeImpl.java?rev=1822706&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbParametrizedTypeImpl.java (added)
+++ aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbParametrizedTypeImpl.java Tue Jan 30 22:05:17 2018
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.aries.blueprint.utils.generics;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+/**
+ * Custom parametrized type implementation.
+ * @version $Rev: 1621935 $ $Date: 2014-09-02 09:07:32 +0200 (Tue, 02 Sep 2014) $
+ *
+ */
+public class OwbParametrizedTypeImpl implements ParameterizedType
+{
+ /**Owner type*/
+ private final Type owner;
+
+ /**Raw type*/
+ private final Type rawType;
+
+ /**Actual type arguments*/
+ private final Type[] types;
+
+ /**
+ * New instance.
+ * @param owner owner
+ * @param raw raw
+ */
+ public OwbParametrizedTypeImpl(Type owner, Type raw, Type... types)
+ {
+ this.owner = owner;
+ rawType = raw;
+ this.types = types;
+ }
+
+ @Override
+ public Type[] getActualTypeArguments()
+ {
+ return types.clone();
+ }
+
+ @Override
+ public Type getOwnerType()
+ {
+ return owner;
+ }
+
+ @Override
+ public Type getRawType()
+ {
+ return rawType;
+ }
+
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ return Arrays.hashCode(types) ^ (owner == null ? 0 : owner.hashCode()) ^ (rawType == null ? 0 : rawType.hashCode());
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (obj instanceof ParameterizedType)
+ {
+ ParameterizedType that = (ParameterizedType) obj;
+ Type thatOwnerType = that.getOwnerType();
+ Type thatRawType = that.getRawType();
+ return (owner == null ? thatOwnerType == null : owner.equals(thatOwnerType))
+ && (rawType == null ? thatRawType == null : rawType.equals(thatRawType))
+ && Arrays.equals(types, that.getActualTypeArguments());
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(((Class<?>) rawType).getName());
+ Type[] actualTypes = getActualTypeArguments();
+ if(actualTypes.length > 0)
+ {
+ buffer.append("<");
+ int length = actualTypes.length;
+ for(int i=0;i<length;i++)
+ {
+ if (actualTypes[i] instanceof Class)
+ {
+ buffer.append(((Class<?>)actualTypes[i]).getSimpleName());
+ }
+ else
+ {
+ buffer.append(actualTypes[i].toString());
+ }
+ if(i != actualTypes.length-1)
+ {
+ buffer.append(", ");
+ }
+ }
+
+ buffer.append(">");
+ }
+
+ return buffer.toString();
+ }
+}
Added: aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbTypeVariableImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbTypeVariableImpl.java?rev=1822706&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbTypeVariableImpl.java (added)
+++ aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/generics/OwbTypeVariableImpl.java Tue Jan 30 22:05:17 2018
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.aries.blueprint.utils.generics;
+
+import java.lang.reflect.*;
+import java.util.Arrays;
+
+
+public class OwbTypeVariableImpl
+{
+ private static final Class<?>[] TYPE_VARIABLE_TYPES = new Class<?>[]{TypeVariable.class};
+
+ /**
+ * Java TypeVariable is different in various JDK versions. Thus it is not possible to e.g.
+ * write a custom TypeVariable which works in either Java7 and Java8 as they introduced
+ * new methods in Java8 which have return generics which only exist in Java8 :(
+ *
+ * As workaround we dynamically crate a proxy to wrap this and do the delegation manually.
+ * This is of course slower, but as we do not use it often it might not have much impact.
+ *
+ * @param typeVariable
+ * @param bounds
+ * @return the typeVariable with the defined bounds.
+ */
+ public static TypeVariable createTypeVariable(TypeVariable typeVariable, Type... bounds)
+ {
+ TypeVariable tv = (TypeVariable) Proxy.newProxyInstance(OwbTypeVariableImpl.class.getClassLoader(), TYPE_VARIABLE_TYPES,
+ new OwbTypeVariableInvocationHandler(typeVariable, bounds));
+
+ return tv;
+ }
+
+
+
+ public static class OwbTypeVariableInvocationHandler implements InvocationHandler
+ {
+
+ private String name;
+ private GenericDeclaration genericDeclaration;
+ private Type[] bounds;
+
+
+ public OwbTypeVariableInvocationHandler(TypeVariable typeVariable, Type... bounds)
+ {
+ name = typeVariable.getName();
+ genericDeclaration = typeVariable.getGenericDeclaration();
+ if (bounds == null || bounds.length == 0)
+ {
+ this.bounds = typeVariable.getBounds();
+ }
+ else
+ {
+ this.bounds = bounds;
+ }
+ }
+
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ String methodName = method.getName();
+ if ("equals".equals(methodName))
+ {
+ return typeVariableEquals(args[0]);
+ }
+ else if ("hashCode".equals(methodName))
+ {
+ return typeVariableHashCode();
+ }
+ else if ("toString".equals(methodName))
+ {
+ return typeVariableToString();
+ }
+ else if ("getName".equals(methodName))
+ {
+ return getName();
+ }
+ else if ("getGenericDeclaration".equals(methodName))
+ {
+ return getGenericDeclaration();
+ }
+ else if ("getBounds".equals(methodName))
+ {
+ return getBounds();
+ }
+
+
+ // new method from java8...
+ return null;
+ }
+
+ /** method from TypeVariable */
+ public String getName()
+ {
+ return name;
+ }
+
+ /** method from TypeVariable */
+ public GenericDeclaration getGenericDeclaration()
+ {
+ return genericDeclaration;
+ }
+
+ /** method from TypeVariable */
+ public Type[] getBounds()
+ {
+ return bounds.clone();
+ }
+
+ /** method from TypeVariable */
+ public int typeVariableHashCode()
+ {
+ return Arrays.hashCode(bounds) ^ name.hashCode() ^ genericDeclaration.hashCode();
+ }
+
+ /** method from TypeVariable */
+ public boolean typeVariableEquals(Object object)
+ {
+ if (this == object)
+ {
+ return true;
+ }
+ else if (object instanceof TypeVariable)
+ {
+ TypeVariable<?> that = (TypeVariable<?>)object;
+ return name.equals(that.getName()) && genericDeclaration.equals(that.getGenericDeclaration()) && Arrays.equals(bounds, that.getBounds());
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ /** method from TypeVariable */
+ public String typeVariableToString()
+ {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(name);
+ if (bounds.length > 0)
+ {
+ buffer.append(" extends ");
+ boolean first = true;
+ for (Type bound: bounds)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buffer.append(',');
+ }
+ buffer.append(' ').append(bound);
+ }
+ }
+ return buffer.toString();
+ }
+
+ }
+}