blob: b9186fddb0b07c5c9a96045fc931e879ce6fe083 [file] [log] [blame]
/*
* Copyright (c) 1998, 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
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.jaxb.javamodel;
import static org.eclipse.persistence.jaxb.JAXBContextFactory.PKG_SEPARATOR;
import static org.eclipse.persistence.jaxb.compiler.XMLProcessor.DEFAULT;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import jakarta.xml.bind.JAXBElement;
import org.eclipse.persistence.internal.oxm.Constants;
import javax.xml.namespace.QName;
/**
* INTERNAL:
* <p><b>Purpose:</b>To provide helper methods and constants to assist
* in integrating TopLink JAXB 2.0 Generation with the JDEV JOT APIs.
* <p><b>Responsibilities:</b>
* <ul>
* <li>Make available a map of JOT - XML type pairs</li>
* <li>Redirect method calls to the current JavaModel implementation as
* required</li>
* <li>Provide methods for accessing generics, annotations, etc. on a
* given implementaiton's classes</li>
* <li>Provide a dynamic proxy instance for a given JavaAnnotation in
* the JOT implementation (for reflection a Java SDK annotation is
* returned)</li>
* </ul>
*
* @since Oracle TopLink 11.1.1.0.0
* @see JavaModel
* @see AnnotationProxy
*
*/
public class Helper {
protected ClassLoader loader;
protected JavaModel jModel;
private Map<String, QName> xmlToJavaTypeMap;
private boolean facets;
public final static String APBYTE = "byte[]";
public final static String BIGDECIMAL = "java.math.BigDecimal";
public final static String BIGINTEGER = "java.math.BigInteger";
public final static String PBOOLEAN = "boolean";
public final static String PBYTE = "byte";
public final static String CALENDAR = "java.util.Calendar";
public final static String CHARACTER = "java.lang.Character";
public final static String CHAR = "char";
public final static String OBJECT = "java.lang.Object";
public final static String CLASS = "java.lang.Class";
public final static String PDOUBLE = "double";
public final static String PFLOAT = "float";
public final static String PINT = "int";
public final static String PLONG = "long";
public final static String PSHORT = "short";
public final static String QNAME_CLASS = "javax.xml.namespace.QName";
public final static String STRING = "java.lang.String";
public final static String ABYTE = "java.lang.Byte[]";
public final static String BOOLEAN = "java.lang.Boolean";
public final static String BYTE = "java.lang.Byte";
public final static String GREGORIAN_CALENDAR = "java.util.GregorianCalendar";
public final static String DOUBLE = "java.lang.Double";
public final static String FLOAT = "java.lang.Float";
public final static String INTEGER = "java.lang.Integer";
public final static String UUID = "java.util.UUID";
public final static String LONG = "java.lang.Long";
public final static String SHORT = "java.lang.Short";
public final static String UTIL_DATE = "java.util.Date";
public final static String SQL_DATE = "java.sql.Date";
public final static String SQL_TIME = "java.sql.Time";
public final static String SQL_TIMESTAMP = "java.sql.Timestamp";
public final static String DURATION = "javax.xml.datatype.Duration";
public final static String XMLGREGORIANCALENDAR = "javax.xml.datatype.XMLGregorianCalendar";
public final static String URI = "java.net.URI";
public final static String URL = "java.net.URL";
protected final static String JAVA_PKG = "java.";
protected final static String JAVAX_PKG = "javax.";
protected final static String JAKARTA_PKG = "jakarta.";
protected final static String JAVAX_WS_PKG = "javax.xml.ws.";
protected final static String JAKARTA_WS_PKG = "jakarta.xml.ws.";
protected final static String JAVAX_RPC_PKG = "javax.xml.rpc.";
protected final static String JAKARTA_RPC_PKG = "jakarta.xml.rpc.";
private JavaClass collectionClass;
private JavaClass setClass;
private JavaClass listClass;
private JavaClass mapClass;
private JavaClass jaxbElementClass;
private JavaClass objectClass;
/**
* INTERNAL:
* This is the preferred constructor.
*
* This constructor builds the map of XML-Java type pairs,
* and sets the JavaModel and ClassLoader.
*
*/
public Helper(JavaModel model) {
xmlToJavaTypeMap = buildXMLToJavaTypeMap();
setJavaModel(model);
setClassLoader(model.getClassLoader());
collectionClass = getJavaClass(CoreClassConstants.Collection_Class);
listClass = getJavaClass(CoreClassConstants.List_Class);
setClass = getJavaClass(CoreClassConstants.Set_Class);
mapClass = getJavaClass(CoreClassConstants.Map_Class);
jaxbElementClass = getJavaClass(JAXBElement.class);
objectClass = getJavaClass(CoreClassConstants.OBJECT);
}
/**
* Builds a map of Java types to XML types.
*
*/
private Map<String, QName> buildXMLToJavaTypeMap() {
Map<String, QName> javaTypes = new HashMap<>();
// jaxb 2.0 spec pairs
javaTypes.put(APBYTE, Constants.BASE_64_BINARY_QNAME);
javaTypes.put(BIGDECIMAL, Constants.DECIMAL_QNAME);
javaTypes.put(BIGINTEGER, Constants.INTEGER_QNAME);
javaTypes.put(PBOOLEAN, Constants.BOOLEAN_QNAME);
javaTypes.put(PBYTE, Constants.BYTE_QNAME);
javaTypes.put(CALENDAR, Constants.DATE_TIME_QNAME);
javaTypes.put(PDOUBLE, Constants.DOUBLE_QNAME);
javaTypes.put(PFLOAT, Constants.FLOAT_QNAME);
javaTypes.put(PINT, Constants.INT_QNAME);
javaTypes.put(PLONG, Constants.LONG_QNAME);
javaTypes.put(PSHORT, Constants.SHORT_QNAME);
javaTypes.put(QNAME_CLASS, Constants.QNAME_QNAME);
javaTypes.put(STRING, Constants.STRING_QNAME);
javaTypes.put(CHAR, Constants.STRING_QNAME);
javaTypes.put(CHARACTER, Constants.STRING_QNAME);
// other pairs
javaTypes.put(ABYTE, Constants.BYTE_QNAME);
javaTypes.put(BOOLEAN, Constants.BOOLEAN_QNAME);
javaTypes.put(BYTE, Constants.BYTE_QNAME);
javaTypes.put(CLASS, Constants.STRING_QNAME);
javaTypes.put(GREGORIAN_CALENDAR, Constants.DATE_TIME_QNAME);
javaTypes.put(DOUBLE, Constants.DOUBLE_QNAME);
javaTypes.put(FLOAT, Constants.FLOAT_QNAME);
javaTypes.put(INTEGER, Constants.INT_QNAME);
javaTypes.put(LONG, Constants.LONG_QNAME);
javaTypes.put(OBJECT, Constants.ANY_TYPE_QNAME);
javaTypes.put(SHORT, Constants.SHORT_QNAME);
javaTypes.put(UTIL_DATE, Constants.DATE_TIME_QNAME);
javaTypes.put(SQL_DATE, Constants.DATE_QNAME);
javaTypes.put(SQL_TIME, Constants.TIME_QNAME);
javaTypes.put(SQL_TIMESTAMP, Constants.DATE_TIME_QNAME);
javaTypes.put(DURATION, Constants.DURATION_QNAME);
javaTypes.put(UUID, Constants.STRING_QNAME);
javaTypes.put(URI, Constants.STRING_QNAME);
javaTypes.put(URL, Constants.ANY_URI_QNAME);
return javaTypes;
}
/**
* Return a given method's generic return type as a JavaClass.
*
*/
public JavaClass getGenericReturnType(JavaMethod meth) {
JavaClass result = meth.getReturnType();
JavaClass jClass = null;
if (result == null) { return null; }
Collection<JavaClass> args = result.getActualTypeArguments();
if (args.size() >0) {
jClass = args.iterator().next();
}
return jClass;
}
/**
* Return a JavaClass instance created based the provided class.
* This assumes that the provided class exists on the classpath
* - null is returned otherwise.
*
*/
public JavaClass getJavaClass(Class javaClass) {
return jModel.getClass(javaClass);
}
/**
* Return array of JavaClass instances created based on the provided classes.
* This assumes provided classes exist on the classpath.
*
* @return JavaClass array
*/
public JavaClass[] getJavaClassArray(Class... classes) {
if (0 == classes.length) {
return new JavaClass[0];
}
JavaClass[] result = new JavaClass[classes.length];
int i = 0;
for (Class clazz : classes) {
result[i++] = getJavaClass(clazz);
}
return result;
}
/**
* Return a JavaClass instance created based on fully qualified
* class name. This assumes that a class with the provided name
* exists on the classpath - null is returned otherwise.
*
*/
public JavaClass getJavaClass(String javaClassName) {
return jModel.getClass(javaClassName);
}
/**
* Return a map of default Java types to XML types.
*/
public Map<String, QName> getXMLToJavaTypeMap() {
return xmlToJavaTypeMap;
}
/**
* Returns a either a dynamic proxy instance that allows an element
* to be treated as an annotation (for JOT), or a Java annotation
* (for Reflection), or null if the specified annotation does not
* exist.
* Intended to be used in conjunction with isAnnotationPresent.
*
* @see #isAnnotationPresent
*/
public Annotation getAnnotation(JavaHasAnnotations element, Class annotationClass) {
JavaAnnotation janno = element.getAnnotation(jModel.getClass(annotationClass));
if (janno == null) {
return null;
}
return jModel.getAnnotation(janno, annotationClass);
}
/**
* Returns a JavaClass instance wrapping the provided field's resolved
* type.
*
*/
public JavaClass getType(JavaField field) {
JavaClass type = field.getResolvedType();
try {
return jModel.getClass(type.getRawName());
} catch (Exception x) {}
return null;
}
/**
* Return a JavaClass instance based on the @see jakarta.xml.bind.JAXBElement .
*
* Replacement of direct access to JAXBELEMENT_CLASS field.
*
*/
public JavaClass getJaxbElementClass() {
return jaxbElementClass;
}
/**
* Return a JavaClass instance based on the @see java.lang.Object .
*
* Replacement of direct access to OBJECT_CLASS field.
*
*/
public JavaClass getObjectClass() {
return objectClass;
}
/**
* Indicates if element contains a given annotation.
*
*/
public boolean isAnnotationPresent(JavaHasAnnotations element, Class annotationClass) {
if (element == null || annotationClass == null) {
return false;
}
return (element.getAnnotation(jModel.getClass(annotationClass)) != null);
}
/**
* Indicates if a given JavaClass is a built-in Java type.
*
* A JavaClass is considered to be a built-in type if:
* 1 - the XMLToJavaTypeMap map contains a key equal to the provided
* JavaClass' raw name
* 2 - the provided JavaClass' raw name starts with "java."
* 3 - the provided JavaClass' raw name starts with "javax.", with
* the exception of "jakarta.xml.ws." and "javax.xml.rpc"
*/
public boolean isBuiltInJavaType(JavaClass jClass) {
String rawName = jClass.getRawName();
if(null == rawName) {
return true;
}
return (getXMLToJavaTypeMap().containsKey(rawName) || rawName.startsWith(JAVA_PKG) || ((rawName.startsWith(JAVAX_PKG) || rawName.startsWith(JAKARTA_PKG)) && !(
rawName.startsWith(JAVAX_WS_PKG) ||
rawName.startsWith(JAVAX_RPC_PKG) ||
rawName.startsWith(JAKARTA_WS_PKG) ||
rawName.startsWith(JAKARTA_RPC_PKG)
)));
}
public void setClassLoader(ClassLoader loader) {
this.loader = loader;
}
public void setJavaModel(JavaModel model) {
jModel = model;
}
public ClassLoader getClassLoader() {
return loader;
}
public Class getClassForJavaClass(JavaClass javaClass){
String javaClassName = javaClass.getRawName();
if (javaClass.isPrimitive() || javaClass.isArray() && javaClass.getComponentType().isPrimitive()){
if (CoreClassConstants.APBYTE.getCanonicalName().equals(javaClassName)){
return Byte[].class;
}
if (CoreClassConstants.PBYTE.getCanonicalName().equals(javaClassName)){
return Byte.class;
}
if (CoreClassConstants.PBOOLEAN.getCanonicalName().equals(javaClassName)){
return Boolean.class;
}
if (CoreClassConstants.PSHORT.getCanonicalName().equals(javaClassName)){
return Short.class;
}
if (CoreClassConstants.PFLOAT.getCanonicalName().equals(javaClassName)){
return Float.class;
}
if (CoreClassConstants.PCHAR.getCanonicalName().equals(javaClassName)){
return Character.class;
}
if (CoreClassConstants.PDOUBLE.getCanonicalName().equals(javaClassName)){
return Double.class;
}
if (CoreClassConstants.PINT.getCanonicalName().equals(javaClassName)){
return Integer.class;
}
if (CoreClassConstants.PLONG.getCanonicalName().equals(javaClassName)){
return Long.class;
}
return null;
}
return org.eclipse.persistence.internal.helper.Helper.getClassFromClasseName(javaClass.getQualifiedName(), loader);
}
/**
* Convenience method to determine if a class exists in a given ArrayList.
*/
public boolean classExistsInArray(JavaClass theClass, List<JavaClass> existingClasses) {
for (JavaClass jClass : existingClasses) {
if (areClassesEqual(jClass, theClass)) {
return true;
}
}
return false;
}
/**
* Convenience method to determine if two JavaClass instances are equal.
*
*/
private boolean areClassesEqual(JavaClass classA, JavaClass classB) {
if (classA == classB) {
return true;
}
if (!(classA.getQualifiedName().equals(classB.getQualifiedName()))) {
return false;
}
Collection<JavaClass> classAargs = classA.getActualTypeArguments();
Collection<JavaClass> classBargs = classB.getActualTypeArguments();
if (classAargs != null) {
if (classBargs == null) {
return false;
}
if (classAargs.size() != classBargs.size()) {
return false;
}
Iterator<JavaClass> classAargsIter = classAargs.iterator();
Iterator<JavaClass> classBargsIter = classBargs.iterator();
while(classAargsIter.hasNext()){
JavaClass nestedClassA = classAargsIter.next();
JavaClass nestedClassB = classBargsIter.next();
if (!areClassesEqual(nestedClassA, nestedClassB)) {
return false;
}
}
return true;
}
if (classBargs == null) {
return true;
}
return false;
}
/**
* Prepends a package name to a given java type name, if it is not already present.
*
* @param javaTypeName Java type name that may/may not contain 'packageName'
* @param packageName package name to prepend to javaTypeName if not already
* @return fully qualified java type name
*/
public static String getQualifiedJavaTypeName(String javaTypeName, String packageName) {
// prepend the package name if not already present
if (packageName != null && packageName.length() > 0 && !packageName.equals(DEFAULT) && !javaTypeName.contains(PKG_SEPARATOR)) {
return packageName + PKG_SEPARATOR + javaTypeName;
}
return javaTypeName;
}
public boolean isCollectionType(JavaClass type) {
if (collectionClass.isAssignableFrom(type)
|| listClass.isAssignableFrom(type)
|| setClass.isAssignableFrom(type)) {
return true;
}
return false;
}
public boolean isMapType(JavaClass type) {
return mapClass.isAssignableFrom(type);
}
public boolean isFacets() {
return facets;
}
public void setFacets(boolean facets) {
this.facets = facets;
}
}