org.eclipse.persistence.internal.jaxb.GenericsClassHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 2014, 2020 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());
}
private static ClassTypePair resolveTypeVariable(Class c, Class dc, TypeVariable tv,
Map 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 map) {
Type[] typeArguments = pt.getActualTypeArguments();
TypeVariable[] typeParameters = c.getTypeParameters();
Map submap = new HashMap();
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);
}
}
}