| /* |
| * Copyright (c) 1998, 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 |
| */ |
| |
| // 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 org.eclipse.persistence.internal.core.helper.CoreClassConstants; |
| |
| import jakarta.xml.bind.JAXBElement; |
| |
| import org.eclipse.persistence.internal.oxm.Constants; |
| |
| /** |
| * 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 HashMap 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. |
| * |
| * @param model |
| */ |
| 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. |
| * |
| * @return |
| */ |
| private HashMap buildXMLToJavaTypeMap() { |
| HashMap 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. |
| * |
| * @param meth |
| * @return |
| */ |
| public JavaClass getGenericReturnType(JavaMethod meth) { |
| JavaClass result = meth.getReturnType(); |
| JavaClass jClass = null; |
| if (result == null) { return null; } |
| |
| Collection args = result.getActualTypeArguments(); |
| if (args.size() >0) { |
| jClass = (JavaClass) 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. |
| * |
| * @param javaClass |
| * @return |
| */ |
| 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. |
| * |
| * @param classes |
| * @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. |
| * |
| * @param javaClassName |
| * @return |
| */ |
| public JavaClass getJavaClass(String javaClassName) { |
| return jModel.getClass(javaClassName); |
| } |
| |
| /** |
| * Return a map of default Java types to XML types. |
| * @return |
| */ |
| public HashMap 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. |
| * |
| * @param element |
| * @param annotationClass |
| * @return |
| * @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. |
| * |
| * @param field |
| * @return |
| */ |
| 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. |
| * |
| * @return |
| */ |
| public JavaClass getJaxbElementClass() { |
| return jaxbElementClass; |
| } |
| |
| /** |
| * Return a JavaClass instance based on the @see java.lang.Object . |
| * |
| * Replacement of direct access to OBJECT_CLASS field. |
| * |
| * @return |
| */ |
| public JavaClass getObjectClass() { |
| return objectClass; |
| } |
| |
| /** |
| * Indicates if element contains a given annotation. |
| * |
| * @param element |
| * @param annotationClass |
| * @return |
| */ |
| 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" |
| * @param jClass |
| * @return |
| */ |
| 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. |
| * |
| * @param classA |
| * @param classB |
| * @return |
| */ |
| private boolean areClassesEqual(JavaClass classA, JavaClass classB) { |
| if (classA == classB) { |
| return true; |
| } |
| if (!(classA.getQualifiedName().equals(classB.getQualifiedName()))) { |
| return false; |
| } |
| |
| Collection classAargs = classA.getActualTypeArguments(); |
| Collection classBargs = classB.getActualTypeArguments(); |
| if (classAargs != null) { |
| if (classBargs == null) { |
| return false; |
| } |
| if (classAargs.size() != classBargs.size()) { |
| return false; |
| } |
| |
| Iterator classAargsIter = classAargs.iterator(); |
| Iterator classBargsIter = classBargs.iterator(); |
| |
| while(classAargsIter.hasNext()){ |
| JavaClass nestedClassA = (JavaClass) classAargsIter.next(); |
| JavaClass nestedClassB = (JavaClass) 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; |
| } |
| } |