/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Denise Smith - January, 2010 - 2.0.1 ******************************************************************************/ package org.eclipse.persistence.jaxb.compiler; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.WildcardType; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlList; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.eclipse.persistence.internal.jaxb.AccessorFactoryWrapper; import org.eclipse.persistence.internal.jaxb.JaxbClassLoader; import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; import org.eclipse.persistence.jaxb.JAXBContextFactory; import org.eclipse.persistence.jaxb.JAXBContext; import org.eclipse.persistence.jaxb.TypeMappingInfo; import org.eclipse.persistence.jaxb.javamodel.Helper; import org.eclipse.persistence.jaxb.javamodel.JavaClass; import org.eclipse.persistence.jaxb.javamodel.JavaField; import org.eclipse.persistence.jaxb.javamodel.JavaMethod; import org.eclipse.persistence.jaxb.javamodel.reflection.JavaClassImpl; import org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes; /** * Helper class for code that needs to be shared between AnnotationsProcessor, * MappingsGenerator, SchemaGenerator */ public class CompilerHelper { public static final String XML_LOCATION_ANNOTATION_NAME = "com.sun.xml.bind.annotation.XmlLocation"; public static final String INTERNAL_XML_LOCATION_ANNOTATION_NAME = "com.sun.xml.internal.bind.annotation.XmlLocation"; private static final String XML_ACCESSOR_FACTORY_ANNOTATION_NAME = "com.sun.xml.bind.XmlAccessorFactory"; private static final String INTERNAL_ACCESSOR_FACTORY_ANNOTATION_NAME = "com.sun.xml.internal.bind.XmlAccessorFactory"; private static final String METADATA_MODEL_PACKAGE = "org.eclipse.persistence.jaxb.xmlmodel"; public static Class ACCESSOR_FACTORY_ANNOTATION_CLASS = null; public static Method ACCESSOR_FACTORY_VALUE_METHOD = null; public static Class INTERNAL_ACCESSOR_FACTORY_ANNOTATION_CLASS = null; public static Method INTERNAL_ACCESSOR_FACTORY_VALUE_METHOD = null; public static Class XML_LOCATION_ANNOTATION_CLASS = null; public static Class INTERNAL_XML_LOCATION_ANNOTATION_CLASS = null; private static JAXBContext xmlBindingsModelContext; static { try { ACCESSOR_FACTORY_ANNOTATION_CLASS = PrivilegedAccessHelper.getClassForName(XML_ACCESSOR_FACTORY_ANNOTATION_NAME, true, CompilerHelper.class.getClassLoader()); ACCESSOR_FACTORY_VALUE_METHOD = PrivilegedAccessHelper.getDeclaredMethod(ACCESSOR_FACTORY_ANNOTATION_CLASS, "value", new Class[]{}); } catch (Exception ex) { } try { XML_LOCATION_ANNOTATION_CLASS = PrivilegedAccessHelper.getClassForName(XML_LOCATION_ANNOTATION_NAME, true, CompilerHelper.class.getClassLoader()); } catch (Exception ex) { } try{ INTERNAL_ACCESSOR_FACTORY_ANNOTATION_CLASS = PrivilegedAccessHelper.getClassForName(INTERNAL_ACCESSOR_FACTORY_ANNOTATION_NAME); INTERNAL_ACCESSOR_FACTORY_VALUE_METHOD = PrivilegedAccessHelper.getDeclaredMethod(INTERNAL_ACCESSOR_FACTORY_ANNOTATION_CLASS, "value", new Class[]{}); } catch (Exception ex) { } try{ INTERNAL_XML_LOCATION_ANNOTATION_CLASS = PrivilegedAccessHelper.getClassForName(INTERNAL_XML_LOCATION_ANNOTATION_NAME); }catch (Exception ex) { } } /** * If 2 TypeMappingInfo objects would generate the same generated class (and * therefore complex type) then return the existing class otherwise return * null. */ static Class getExisitingGeneratedClass(TypeMappingInfo tmi, Map<TypeMappingInfo, Class> typeMappingInfoToGeneratedClasses, Map<TypeMappingInfo, Class> typeMappingInfoToAdapterClasses, ClassLoader loader) { Iterator<Map.Entry<TypeMappingInfo, Class>> iter = typeMappingInfoToGeneratedClasses.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<TypeMappingInfo, Class> next = iter.next(); TypeMappingInfo nextTMI = next.getKey(); if (CompilerHelper.generatesSameComplexType(tmi, nextTMI, loader)) { return next.getValue(); } } return null; } /** * Return true if the two TypeMappingInfoObjects should generate the same * complex type in the XSD */ private static boolean generatesSameComplexType(TypeMappingInfo tmi1, TypeMappingInfo tmi2, ClassLoader loader) { org.eclipse.persistence.jaxb.xmlmodel.XmlElement element1 = null; org.eclipse.persistence.jaxb.xmlmodel.XmlElement element2 = null; if (tmi1.getXmlElement() != null) { element1 = (org.eclipse.persistence.jaxb.xmlmodel.XmlElement) getXmlElement(tmi1.getXmlElement(), loader); } if (tmi2.getXmlElement() != null) { element2 = (org.eclipse.persistence.jaxb.xmlmodel.XmlElement) getXmlElement(tmi2.getXmlElement(), loader); } Type actualType1 = getActualType(tmi1, element1); Type actualType2 = getActualType(tmi2, element2); if (!areTypesEqual(actualType1, actualType2)) { return false; } if (!hasSameClassName(tmi1,tmi2)) { return false; } boolean isXmlList1 = isXmlList(tmi1, element1); boolean isXmlList2 = isXmlList(tmi2, element2); if (isXmlList1) { if (!isXmlList2) { return false; } } else if (isXmlList2) { return false; } return true; } /** * Return true if tmi1 and tmi2 are instances of Class and have same class name. * * @param tmi1 instance of TypeMappingInfo * @param tmi2 instance of typeMappingInfo * @return true if TypeMappingInfos are instances of Class and have same class name */ private static boolean hasSameClassName(TypeMappingInfo tmi1, TypeMappingInfo tmi2) { Type type1 = tmi1.getType(); Type type2 = tmi2.getType(); if (type1 != null && type2 == null) { return false; } else if (type1 == null && type2 != null) { return false; } else if (type1 instanceof Class && type2 instanceof Class){ String typeName1 = ((Class)type1).getName(); String typeName2 = ((Class)type2).getName(); if (!typeName1.equals(typeName2)) { return false; } } return true; } /** * Return if this TypeMappingInfo has an XmlList annotation or is specified * to be an xmlList in an XMLElement override */ private static boolean isXmlList(TypeMappingInfo tmi, org.eclipse.persistence.jaxb.xmlmodel.XmlElement element) { if (element != null && element.isXmlList()) { return true; } if (tmi.getAnnotations() != null) { for (int i = 0; i < tmi.getAnnotations().length; i++) { java.lang.annotation.Annotation nextAnnotation = tmi.getAnnotations()[i]; if (nextAnnotation != null && nextAnnotation instanceof XmlList) { return true; } } } return false; } /** * Return true if the Types are equal. Accounts for Classes and * Parameterized types or any combintation of the two. */ private static boolean areTypesEqual(java.lang.reflect.Type type, java.lang.reflect.Type type2) { // handle null if (type == null) { return type2 == null; } else if (type instanceof Class) { if (type2 instanceof ParameterizedType) { java.lang.reflect.Type rawType = ((ParameterizedType) type2).getRawType(); if (!areTypesEqual(type, rawType)) { return false; } java.lang.reflect.Type[] args = ((ParameterizedType) type2).getActualTypeArguments(); for (int i = 0; i < args.length; i++) { Type argType = getActualArgumentType(args[i]); if (!areTypesEqual(Object.class, argType)) { return false; } } return true; } else if (type2 instanceof Class) { return type.equals(type2); } else { return false; } } else if (type instanceof ParameterizedType) { if (type2 instanceof Class) { java.lang.reflect.Type rawType = ((ParameterizedType) type).getRawType(); if (!areTypesEqual(type2, rawType)) { return false; } java.lang.reflect.Type[] args = ((ParameterizedType) type).getActualTypeArguments(); for (int i = 0; i < args.length; i++) { Type argType = getActualArgumentType(args[i]); if (!areTypesEqual(Object.class, argType)) { return false; } } return true; } else if (type2 instanceof ParameterizedType) { // compare raw type if (!areTypesEqual(((ParameterizedType) type).getRawType(), ((ParameterizedType) type2).getRawType())) { return false; } java.lang.reflect.Type[] ta1 = ((ParameterizedType) type).getActualTypeArguments(); java.lang.reflect.Type[] ta2 = ((ParameterizedType) type2).getActualTypeArguments(); // check array length if (ta1.length != ta2.length) { return false; } for (int i = 0; i < ta1.length; i++) { Type componentType1 = getActualArgumentType(ta1[i]); Type componentType2 = getActualArgumentType(ta2[i]); if (!areTypesEqual(componentType1, componentType2)) { return false; } } return true; } else { return false; } } return false; } private static Type getActualArgumentType(Type argument){ if(argument instanceof WildcardType){ Type[] upperBounds = ((WildcardType)argument).getUpperBounds(); if(upperBounds != null && upperBounds.length >0){ return upperBounds[0]; }else{ return Object.class; } }else if (argument instanceof GenericArrayType){ return ((GenericArrayType)argument).getGenericComponentType(); } return argument; } /** * Convenience method for creating an XmlElement object based on a given * Element. The method will load the eclipselink metadata model and * unmarshal the Element. This assumes that the Element represents an * xml-element to be unmarshalled. * * @param xmlElementNode * @param classLoader * @return */ static org.eclipse.persistence.jaxb.xmlmodel.XmlElement getXmlElement(org.w3c.dom.Element xmlElementNode, ClassLoader classLoader) { try { Unmarshaller unmarshaller = CompilerHelper.getXmlBindingsModelContext().createUnmarshaller(); JAXBElement<org.eclipse.persistence.jaxb.xmlmodel.XmlElement> jelt = unmarshaller.unmarshal(xmlElementNode, org.eclipse.persistence.jaxb.xmlmodel.XmlElement.class); return jelt.getValue(); } catch (javax.xml.bind.JAXBException jaxbEx) { throw org.eclipse.persistence.exceptions.JAXBException.couldNotUnmarshalMetadata(jaxbEx); } } /** * If adapter class is null return null If there is a marshal method that * returns something other than Object on the adapter class return the * return type of that method Otherwise return Object.class */ static Class getTypeFromAdapterClass(Class adapterClass) { if (adapterClass != null) { Class declJavaType = Object.class; // look for marshal method Method[] tacMethods = PrivilegedAccessHelper.getMethods(adapterClass); for (int i = 0; i < tacMethods.length; i++) { Method method = tacMethods[i]; if (method.getName().equals("marshal")) { Class returnType = PrivilegedAccessHelper.getMethodReturnType(method); if (!(returnType == declJavaType)) { declJavaType = returnType; return declJavaType; } } } return declJavaType; } return null; } /** * If adapter class is null return null If there is a marshal method that * returns something other than Object on the adapter class return the * return type of that method Otherwise return Object.class */ public static JavaClass getTypeFromAdapterClass(JavaClass adapterClass, Helper helper) { if (adapterClass != null) { //JavaClass declJavaType = Object.class; JavaClass declJavaType = helper.getJavaClass(Object.class); // look for marshal method Object[] tacMethods = (Object[]) adapterClass.getMethods().toArray(); for (int i = 0; i < tacMethods.length; i++) { JavaMethod method = (JavaMethod)tacMethods[i]; if (method.getName().equals("marshal")) { JavaClass returnType = method.getReturnType(); if (!(returnType.getQualifiedName().equals(declJavaType.getQualifiedName()))) { declJavaType = returnType; return declJavaType; } } } return declJavaType; } return null; } /** * Return true if the type is a Collection, List or Set */ private static boolean isCollectionType(Type theType) { if (theType instanceof Class) { if (Collection.class.isAssignableFrom((Class) theType) || List.class.isAssignableFrom((Class) theType) || Set.class.isAssignableFrom((Class) theType)) { return true; } return false; } else if (theType instanceof ParameterizedType) { Type rawType = ((ParameterizedType) theType).getRawType(); return isCollectionType(rawType); } return false; } /** * The actual type accounts for adapter classes or xmlelemnt types specified * in either an annotation or an XML override * */ static Type getActualType(TypeMappingInfo tmi, org.eclipse.persistence.jaxb.xmlmodel.XmlElement element) { try { if (element == null) { if (tmi.getAnnotations() != null) { for (int i = 0; i < tmi.getAnnotations().length; i++) { java.lang.annotation.Annotation nextAnnotation = tmi.getAnnotations()[i]; if (nextAnnotation != null) { if (nextAnnotation instanceof XmlJavaTypeAdapter) { Class typeClass = ((XmlJavaTypeAdapter) nextAnnotation).type(); if (typeClass.getName().equals("javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter$DEFAULT")) { Class adapterClass = ((XmlJavaTypeAdapter) nextAnnotation).value(); return getTypeFromAdapterClass(adapterClass); } return typeClass; } else if (nextAnnotation instanceof XmlElement) { Class typeClass = ((XmlElement) nextAnnotation).type(); if (!typeClass.getName().equals("javax.xml.bind.annotation.XmlElement.DEFAULT")) { final Type tmiType = tmi.getType(); if (isCollectionType(tmiType)) { final Class itemType = typeClass; Type parameterizedType = new ParameterizedType() { Type[] typeArgs = { itemType }; public Type[] getActualTypeArguments() { return typeArgs; } public Type getOwnerType() { return null; } public Type getRawType() { return tmiType; } }; return parameterizedType; } else { return typeClass; } } } } } } return tmi.getType(); } else { // if it has an XMLElement specified // Check for an adapater, then check for XMLElement.type if (element.getXmlJavaTypeAdapter() != null) { String actualType = element.getXmlJavaTypeAdapter().getType(); if (actualType != null && !actualType.equals("javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter.DEFAULT")) { return PrivilegedAccessHelper.getClassForName(actualType); } else { String adapterClassName = element.getXmlJavaTypeAdapter().getValue(); Class adapterClass = PrivilegedAccessHelper.getClassForName(adapterClassName); return getTypeFromAdapterClass(adapterClass); } } if (!(element.getType().equals("javax.xml.bind.annotation.XmlElement.DEFAULT"))) { String actualType = element.getType(); final Type tmiType = tmi.getType(); if (isCollectionType(tmiType)) { final Class itemType = PrivilegedAccessHelper.getClassForName(actualType); Type parameterizedType = new ParameterizedType() { Type[] typeArgs = { itemType }; public Type[] getActualTypeArguments() { return typeArgs; } public Type getOwnerType() { return null; } public Type getRawType() { return tmiType; } }; return parameterizedType; } else { return PrivilegedAccessHelper.getClassForName(actualType); } } return tmi.getType(); } } catch (Exception e) { return tmi.getType(); } } /** * The method will load the eclipselink metadata model and return the * corresponding JAXBContext */ public static JAXBContext getXmlBindingsModelContext() { if (xmlBindingsModelContext == null) { try { xmlBindingsModelContext = (JAXBContext) JAXBContextFactory.createContext(METADATA_MODEL_PACKAGE,CompilerHelper.class.getClassLoader()); } catch (JAXBException e) { throw org.eclipse.persistence.exceptions.JAXBException.couldNotCreateContextForXmlModel(e); } if (xmlBindingsModelContext == null) { throw org.eclipse.persistence.exceptions.JAXBException.couldNotCreateContextForXmlModel(); } } return xmlBindingsModelContext; } public static JavaClass getNextMappedSuperClass(JavaClass cls, Map<String, TypeInfo> typeInfo, Helper helper) { JavaClass superClass = cls.getSuperclass(); if(superClass == null || helper.isBuiltInJavaType(cls) || superClass.getRawName().equals("java.lang.Object")){ return null; } TypeInfo parentTypeInfo = typeInfo.get(superClass.getQualifiedName()); if(parentTypeInfo == null || parentTypeInfo.isTransient()) { return getNextMappedSuperClass(superClass, typeInfo, helper); } return superClass; } public static void addClassToClassLoader(JavaClass cls, ClassLoader loader) { if(loader.getClass() == JaxbClassLoader.class && cls.getClass() == JavaClassImpl.class) { Class wrappedClass = ((JavaClassImpl)cls).getJavaClass(); ((JaxbClassLoader)loader).putClass(wrappedClass.getName(), wrappedClass); } } static boolean hasNonAttributeJoinNodes(Property property) { if(property.isSetXmlJoinNodes()) { for(XmlJoinNodes.XmlJoinNode next: property.getXmlJoinNodes().getXmlJoinNode()) { if(!(next.getXmlPath().startsWith("@"))) { return true; } } } else if(property.isSetXmlJoinNodesList()) { for(XmlJoinNodes nextNodes:property.getXmlJoinNodesList()) { for(XmlJoinNodes.XmlJoinNode next: nextNodes.getXmlJoinNode()) { if(!(next.getXmlPath().startsWith("@"))) { return true; } } } } return false; } public static Object createAccessorFor(JavaClass jClass, Property property, Helper helper, AccessorFactoryWrapper accessorFactory) { if(!(jClass.getClass() == JavaClassImpl.class)) { return null; } Class beanClass = ((JavaClassImpl)jClass).getJavaClass(); if(property.isMethodProperty()) { try { Method getMethod = null; Method setMethod = null; if(property.getGetMethodName() != null) { getMethod = PrivilegedAccessHelper.getMethod(beanClass, property.getGetMethodName(), new Class[]{}, true); } if(property.getSetMethodName() != null) { String setMethodParamTypeName = property.getType().getName(); JavaClassImpl paramType = (JavaClassImpl)helper.getJavaClass(setMethodParamTypeName); Class[] setMethodParams = new Class[]{paramType.getJavaClass()}; setMethod = PrivilegedAccessHelper.getMethod(beanClass, property.getSetMethodName(), setMethodParams, true); } return accessorFactory.createPropertyAccessor(beanClass, getMethod, setMethod); } catch(Exception ex) {} } else { try { Field field = PrivilegedAccessHelper.getField(beanClass, ((JavaField)property.getElement()).getName(), true); return accessorFactory.createFieldAccessor(beanClass, field, property.isReadOnly()); } catch(Exception ex) { ex.printStackTrace(); } } return null; } public static boolean isSimpleType(TypeInfo info) { if (info.isEnumerationType()) { return true; } Property xmlValueProperty = info.getXmlValueProperty(); boolean hasMappedAttributes = false; for (Property nextProp : info.getPropertyList()) { if (nextProp.isAttribute() && !nextProp.isTransient()) { hasMappedAttributes = true; } } hasMappedAttributes = hasMappedAttributes || info.hasPredicateProperties(); return (xmlValueProperty != null && !hasMappedAttributes); } }