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
The newest version!
/*
* Copyright (c) 2014, 2024 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 pt) {
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 pt) {
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 pt) {
// process pt of interface
ClassTypePair ctp = resolveTypeVariable(pt, (Class) pt.getRawType(), dc, tv, map);
if (ctp != null)
return ctp;
}
}
Type gsc = c.getGenericSuperclass();
if (gsc instanceof ParameterizedType pt) {
// process pt of class
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 arrayType) {
Type t = arrayType.getGenericComponentType();
if (t instanceof Class) {
return getArrayClass((Class) t);
}
} else if (type instanceof ParameterizedType subType) {
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);
}
}
}