| /* |
| * Copyright (c) 2014, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| package org.eclipse.persistence.internal.jaxb; |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.lang.reflect.TypeVariable; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| |
| /** |
| * Utility class for Generic class hierarchy. |
| * |
| */ |
| public class GenericsClassHelper { |
| |
| /** |
| * A tuple consisting of a concrete class, declaring class that declares a |
| * generic interface type. |
| */ |
| private static class DeclaringClassInterfacePair { |
| public final Class<?> concreteClass; |
| |
| public final Class<?> declaringClass; |
| |
| public final Type genericInterface; |
| |
| private DeclaringClassInterfacePair(Class<?> concreteClass, Class<?> declaringClass, Type genericInteface) { |
| this.concreteClass = concreteClass; |
| this.declaringClass = declaringClass; |
| this.genericInterface = genericInteface; |
| } |
| } |
| |
| /** |
| * Get the parameterized type arguments for a declaring class that |
| * declares a generic class or interface type. |
| * |
| * @param concrete the concrete class than directly or indirectly |
| * implements or extends an interface class. |
| * @param classOrIface the interface or class. |
| * @return the parameterized type arguments, or null if the generic |
| * interface type is not a parameterized type. |
| */ |
| public static Type[] getParameterizedTypeArguments(Class<?> concrete, Class<?> classOrIface) { |
| DeclaringClassInterfacePair declaringClassInterfacePair = getClass(concrete, classOrIface); |
| if (null != declaringClassInterfacePair) { |
| return getParameterizedTypeArguments(declaringClassInterfacePair); |
| } |
| return null; |
| } |
| |
| /** |
| * Get the parameterized type arguments for a declaring class that |
| * declares a generic interface type. |
| * |
| * @param p the declaring class |
| * @return the parameterized type arguments, or null if the generic |
| * interface type is not a parameterized type. |
| */ |
| private static Type[] getParameterizedTypeArguments(DeclaringClassInterfacePair p) { |
| if (p.genericInterface instanceof ParameterizedType) { |
| ParameterizedType pt = (ParameterizedType) p.genericInterface; |
| Type[] as = pt.getActualTypeArguments(); |
| Type[] ras = new Type[as.length]; |
| |
| for (int i = 0; i < as.length; i++) { |
| Type a = as[i]; |
| if (a instanceof Class) { |
| ras[i] = a; |
| } else if (a instanceof ParameterizedType) { |
| pt = (ParameterizedType) a; |
| ras[i] = a; |
| } else if (a instanceof TypeVariable) { |
| ClassTypePair ctp = resolveTypeVariable(p.concreteClass, p.declaringClass, (TypeVariable) a); |
| if (null != ctp) { |
| ras[i] = ctp.t; |
| } |
| } |
| } |
| return ras; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Find the declaring class that implements or extends an interface or class. |
| * |
| * @param concrete the concrete class than directly or indirectly |
| * implements or extends an interface class. |
| * @param classOrIface the interface or class. |
| * @return the tuple of the declaring class and the generic interface or class |
| * type. |
| */ |
| private static DeclaringClassInterfacePair getClass(Class<?> concrete, Class<?> classOrIface) { |
| return getClass(concrete, classOrIface, concrete); |
| } |
| |
| private static DeclaringClassInterfacePair getClass(Class<?> concrete, Class<?> classOrIface, Class<?> c) { |
| Type[] gis = null; |
| |
| if (null != c.getGenericSuperclass()) { |
| gis = new Type[] {c.getGenericSuperclass()}; |
| } |
| |
| if (null == gis) { |
| gis = c.getGenericInterfaces(); |
| } |
| |
| DeclaringClassInterfacePair p = getType(concrete, classOrIface, c, gis); |
| if (p != null) |
| return p; |
| |
| c = c.getSuperclass(); |
| if (c == null || c == Object.class) |
| return null; |
| |
| return getClass(concrete, classOrIface, c); |
| } |
| |
| private static DeclaringClassInterfacePair getType(Class<?> concrete, Class<?> classOrIface, Class<?> c, Type[] ts) { |
| for (Type t : ts) { |
| DeclaringClassInterfacePair p = getType(concrete, classOrIface, c, t); |
| if (p != null) |
| return p; |
| } |
| return null; |
| } |
| |
| private static DeclaringClassInterfacePair getType(Class<?> concrete, Class<?> classOrIface, Class<?> c, Type t) { |
| if (t instanceof Class) { |
| if (t == classOrIface) { |
| return new DeclaringClassInterfacePair(concrete, c, t); |
| } else { |
| return getClass(concrete, classOrIface, (Class) t); |
| } |
| } else if (t instanceof ParameterizedType) { |
| ParameterizedType pt = (ParameterizedType) t; |
| if (pt.getRawType() == classOrIface) { |
| return new DeclaringClassInterfacePair(concrete, c, t); |
| } else { |
| return getClass(concrete, classOrIface, (Class) pt.getRawType()); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * A tuple consisting of a class and type of the class. |
| */ |
| private static class ClassTypePair { |
| |
| /** |
| * The type of the class. |
| */ |
| public final Type t; |
| |
| public ClassTypePair(Class<?> c) { |
| this(c, c); |
| } |
| |
| public ClassTypePair(Class<?> c, Type t) { |
| this.t = t; |
| } |
| } |
| |
| /** |
| * Given a type variable resolve the Java class of that variable. |
| * |
| * @param c the concrete class from which all type variables are resolved |
| * @param dc the declaring class where the type variable was defined |
| * @param tv the type variable |
| * @return the resolved Java class and type, otherwise null if the type variable |
| * could not be resolved |
| */ |
| private static ClassTypePair resolveTypeVariable(Class<?> c, Class<?> dc, TypeVariable tv) { |
| return resolveTypeVariable(c, dc, tv, new HashMap<TypeVariable, Type>()); |
| } |
| |
| private static ClassTypePair resolveTypeVariable(Class<?> c, Class<?> dc, TypeVariable tv, |
| Map<TypeVariable, Type> map) { |
| Type[] gis = c.getGenericInterfaces(); |
| for (Type gi : gis) { |
| if (gi instanceof ParameterizedType) { |
| // process pt of interface |
| ParameterizedType pt = (ParameterizedType) gi; |
| ClassTypePair ctp = resolveTypeVariable(pt, (Class) pt.getRawType(), dc, tv, map); |
| if (ctp != null) |
| return ctp; |
| } |
| } |
| |
| Type gsc = c.getGenericSuperclass(); |
| if (gsc instanceof ParameterizedType) { |
| // process pt of class |
| ParameterizedType pt = (ParameterizedType) gsc; |
| return resolveTypeVariable(pt, c.getSuperclass(), dc, tv, map); |
| } else if (gsc instanceof Class) { |
| return resolveTypeVariable(c.getSuperclass(), dc, tv, map); |
| } |
| return null; |
| } |
| |
| private static ClassTypePair resolveTypeVariable(ParameterizedType pt, Class<?> c, Class<?> dc, TypeVariable tv, |
| Map<TypeVariable, Type> map) { |
| Type[] typeArguments = pt.getActualTypeArguments(); |
| |
| TypeVariable[] typeParameters = c.getTypeParameters(); |
| |
| Map<TypeVariable, Type> submap = new HashMap<TypeVariable, Type>(); |
| for (int i = 0; i < typeArguments.length; i++) { |
| // Substitute a type variable with the Java class |
| if (typeArguments[i] instanceof TypeVariable) { |
| Type t = map.get(typeArguments[i]); |
| submap.put(typeParameters[i], t); |
| } else { |
| submap.put(typeParameters[i], typeArguments[i]); |
| } |
| } |
| |
| if (c == dc) { |
| Type t = submap.get(tv); |
| if (t instanceof Class) { |
| return new ClassTypePair((Class) t); |
| } else if (t instanceof GenericArrayType) { |
| t = ((GenericArrayType) t).getGenericComponentType(); |
| if (t instanceof Class) { |
| c = (Class) t; |
| try { |
| return new ClassTypePair(getArrayClass(c)); |
| } catch (Exception e) { |
| } |
| return null; |
| } else if (t instanceof ParameterizedType) { |
| Type rt = ((ParameterizedType) t).getRawType(); |
| if (rt instanceof Class) { |
| c = (Class) rt; |
| } else { |
| return null; |
| } |
| try { |
| return new ClassTypePair(getArrayClass(c), t); |
| } catch (Exception e) { |
| return null; |
| } |
| } else { |
| return null; |
| } |
| } else if (t instanceof ParameterizedType) { |
| pt = (ParameterizedType) t; |
| if (pt.getRawType() instanceof Class) { |
| return new ClassTypePair((Class) pt.getRawType(), pt); |
| } else |
| return null; |
| } else { |
| return null; |
| } |
| } else { |
| return resolveTypeVariable(c, dc, tv, submap); |
| } |
| } |
| |
| protected static Class<?> getClassOfType(Type type) { |
| if (type instanceof Class) { |
| return (Class) type; |
| } else if (type instanceof GenericArrayType) { |
| GenericArrayType arrayType = (GenericArrayType) type; |
| Type t = arrayType.getGenericComponentType(); |
| if (t instanceof Class) { |
| return getArrayClass((Class) t); |
| } |
| } else if (type instanceof ParameterizedType) { |
| ParameterizedType subType = (ParameterizedType) type; |
| Type t = subType.getRawType(); |
| if (t instanceof Class) { |
| return (Class) t; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get Array class of component class. |
| * |
| * @param c the component class of the array |
| * @return the array class. |
| */ |
| private static Class<?> getArrayClass(Class<?> c) { |
| try { |
| Object o = Array.newInstance(c, 0); |
| return o.getClass(); |
| } catch (Exception e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| } |