/******************************************************************************* * Copyright (c) 1998, 2016 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: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.jaxb.compiler; import java.awt.Image; import java.beans.Introspector; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.validation.constraints.DecimalMax; import javax.validation.constraints.DecimalMin; import javax.validation.constraints.Digits; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAccessorOrder; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAnyAttribute; import javax.xml.bind.annotation.XmlAnyElement; import javax.xml.bind.annotation.XmlAttachmentRef; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementDecl; import javax.xml.bind.annotation.XmlElementRef; import javax.xml.bind.annotation.XmlElementRefs; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlElements; import javax.xml.bind.annotation.XmlEnum; import javax.xml.bind.annotation.XmlEnumValue; import javax.xml.bind.annotation.XmlID; import javax.xml.bind.annotation.XmlIDREF; import javax.xml.bind.annotation.XmlInlineBinaryData; import javax.xml.bind.annotation.XmlList; import javax.xml.bind.annotation.XmlMimeType; import javax.xml.bind.annotation.XmlMixed; import javax.xml.bind.annotation.XmlNs; import javax.xml.bind.annotation.XmlNsForm; import javax.xml.bind.annotation.XmlRegistry; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSchema; import javax.xml.bind.annotation.XmlSchemaType; import javax.xml.bind.annotation.XmlSchemaTypes; import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlType.DEFAULT; import javax.xml.bind.annotation.XmlValue; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters; import javax.xml.namespace.QName; import javax.xml.transform.Source; import org.eclipse.persistence.exceptions.ConversionException; import org.eclipse.persistence.exceptions.JAXBException; import org.eclipse.persistence.internal.core.helper.CoreClassConstants; import org.eclipse.persistence.internal.helper.ClassConstants; import org.eclipse.persistence.internal.helper.ConversionManager; import org.eclipse.persistence.internal.jaxb.AccessorFactoryWrapper; import org.eclipse.persistence.internal.jaxb.JaxbClassLoader; import org.eclipse.persistence.internal.jaxb.many.ArrayValue; import org.eclipse.persistence.internal.jaxb.many.CollectionValue; import org.eclipse.persistence.internal.jaxb.many.ManyValue; import org.eclipse.persistence.internal.jaxb.many.MultiDimensionalArrayValue; import org.eclipse.persistence.internal.jaxb.many.MultiDimensionalCollectionValue; import org.eclipse.persistence.internal.libraries.asm.AnnotationVisitor; import org.eclipse.persistence.internal.libraries.asm.ClassWriter; import org.eclipse.persistence.internal.libraries.asm.FieldVisitor; import org.eclipse.persistence.internal.libraries.asm.Label; import org.eclipse.persistence.internal.libraries.asm.MethodVisitor; import org.eclipse.persistence.internal.libraries.asm.Opcodes; import org.eclipse.persistence.internal.libraries.asm.Type; import org.eclipse.persistence.internal.oxm.Constants; import org.eclipse.persistence.internal.oxm.Namespace; import org.eclipse.persistence.internal.oxm.XMLConversionManager; import org.eclipse.persistence.internal.oxm.XPathFragment; import org.eclipse.persistence.internal.oxm.mappings.Field; import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; import org.eclipse.persistence.jaxb.MOXySystemProperties; import org.eclipse.persistence.jaxb.TypeMappingInfo; import org.eclipse.persistence.jaxb.compiler.facets.DecimalMaxFacet; import org.eclipse.persistence.jaxb.compiler.facets.DecimalMinFacet; import org.eclipse.persistence.jaxb.compiler.facets.DigitsFacet; import org.eclipse.persistence.jaxb.compiler.facets.MaxFacet; import org.eclipse.persistence.jaxb.compiler.facets.MinFacet; import org.eclipse.persistence.jaxb.compiler.facets.PatternFacet; import org.eclipse.persistence.jaxb.compiler.facets.PatternListFacet; import org.eclipse.persistence.jaxb.compiler.facets.SizeFacet; import org.eclipse.persistence.jaxb.javamodel.AnnotationProxy; import org.eclipse.persistence.jaxb.javamodel.Helper; import org.eclipse.persistence.jaxb.javamodel.JavaAnnotation; import org.eclipse.persistence.jaxb.javamodel.JavaClass; import org.eclipse.persistence.jaxb.javamodel.JavaConstructor; import org.eclipse.persistence.jaxb.javamodel.JavaField; import org.eclipse.persistence.jaxb.javamodel.JavaHasAnnotations; import org.eclipse.persistence.jaxb.javamodel.JavaMethod; import org.eclipse.persistence.jaxb.javamodel.JavaPackage; import org.eclipse.persistence.jaxb.javamodel.reflection.JavaFieldImpl; import org.eclipse.persistence.jaxb.xmlmodel.XmlAccessOrder; import org.eclipse.persistence.jaxb.xmlmodel.XmlAccessType; import org.eclipse.persistence.jaxb.xmlmodel.XmlTransformation; import org.eclipse.persistence.jaxb.xmlmodel.XmlTransformation.XmlReadTransformer; import org.eclipse.persistence.jaxb.xmlmodel.XmlTransformation.XmlWriteTransformer; import org.eclipse.persistence.mappings.transformers.AttributeTransformer; import org.eclipse.persistence.mappings.transformers.FieldTransformer; import org.eclipse.persistence.oxm.NamespaceResolver; import org.eclipse.persistence.oxm.XMLField; import org.eclipse.persistence.oxm.XMLNameTransformer; import org.eclipse.persistence.oxm.annotations.XmlAccessMethods; import org.eclipse.persistence.oxm.annotations.XmlCDATA; import org.eclipse.persistence.oxm.annotations.XmlClassExtractor; import org.eclipse.persistence.oxm.annotations.XmlContainerProperty; import org.eclipse.persistence.oxm.annotations.XmlCustomizer; import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode; import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue; import org.eclipse.persistence.oxm.annotations.XmlElementNillable; import org.eclipse.persistence.oxm.annotations.XmlElementsJoinNodes; import org.eclipse.persistence.oxm.annotations.XmlIDExtension; import org.eclipse.persistence.oxm.annotations.XmlInverseReference; import org.eclipse.persistence.oxm.annotations.XmlIsSetNullPolicy; import org.eclipse.persistence.oxm.annotations.XmlJoinNode; import org.eclipse.persistence.oxm.annotations.XmlJoinNodes; import org.eclipse.persistence.oxm.annotations.XmlKey; import org.eclipse.persistence.oxm.annotations.XmlLocation; import org.eclipse.persistence.oxm.annotations.XmlNameTransformer; import org.eclipse.persistence.oxm.annotations.XmlNamedAttributeNode; import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraph; import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraphs; import org.eclipse.persistence.oxm.annotations.XmlNamedSubgraph; import org.eclipse.persistence.oxm.annotations.XmlNullPolicy; import org.eclipse.persistence.oxm.annotations.XmlParameter; import org.eclipse.persistence.oxm.annotations.XmlPath; import org.eclipse.persistence.oxm.annotations.XmlPaths; import org.eclipse.persistence.oxm.annotations.XmlProperties; import org.eclipse.persistence.oxm.annotations.XmlProperty; import org.eclipse.persistence.oxm.annotations.XmlReadOnly; import org.eclipse.persistence.oxm.annotations.XmlValueExtension; import org.eclipse.persistence.oxm.annotations.XmlVariableNode; import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods; import org.eclipse.persistence.oxm.annotations.XmlWriteOnly; import org.eclipse.persistence.oxm.annotations.XmlWriteTransformers; /** * INTERNAL: * <p> * <b>Purpose:</b>To perform some initial processing of Java classes and JAXB * 2.0 Annotations and generate meta data that can be used by the Mappings * Generator and Schema Generator * <p> * <b>Responsibilities:</b> * <ul> * <li>Generate a map of TypeInfo objects, keyed on class name</li> * <li>Generate a map of user defined schema types</li> * <li>Identify any class-based JAXB 2.0 callback methods, and create * MarshalCallback and UnmarshalCallback objects to wrap them.</li> * <li>Centralize processing which is common to both Schema Generation and * Mapping Generation tasks</li> * </ul> * <p> * This class does the initial processing of the JAXB 2.0 Generation. It * generates meta data that can be used by the later Schema Generation and * Mapping Generation steps. * * @see org.eclipse.persistence.jaxb.compiler.Generator * @author mmacivor * @since Oracle TopLink 11.1.1.0.0 */ public final class AnnotationsProcessor { static final String JAVAX_ACTIVATION_DATAHANDLER = "javax.activation.DataHandler"; static final String JAVAX_MAIL_INTERNET_MIMEMULTIPART = "javax.mail.internet.MimeMultipart"; private static final String JAVAX_XML_BIND_JAXBELEMENT = "javax.xml.bind.JAXBElement"; private static final String JAVAX_XML_BIND_ANNOTATION = "javax.xml.bind.annotation"; private static final String OXM_ANNOTATIONS = "org.eclipse.persistence.oxm.annotations"; private static final String TYPE_METHOD_NAME = "type"; private static final String VALUE_METHOD_NAME = "value"; private static final String ARRAY_PACKAGE_NAME = "jaxb.dev.java.net.array"; private static final String ARRAY_NAMESPACE = "http://jaxb.dev.java.net/array"; private static final String ARRAY_CLASS_NAME_SUFFIX = "Array"; private static final String JAXB_DEV = "jaxb.dev.java.net"; private static final String ORG_W3C_DOM = "org.w3c.dom"; private static final String CREATE = "create"; private static final String ELEMENT_DECL_GLOBAL = "javax.xml.bind.annotation.XmlElementDecl.GLOBAL"; private static final String ELEMENT_DECL_DEFAULT = "\u0000"; private static final String EMPTY_STRING = ""; private static final String JAVA_UTIL_LIST = "java.util.List"; private static final String JAVA_LANG_OBJECT = "java.lang.Object"; private static final String SLASH = "/"; private static final String SEMI_COLON = ";"; private static final String L = "L"; private static final String ITEM = "item"; private static final String IS_STR = "is"; private static final String GET_STR = "get"; private static final String SET_STR = "set"; private static final Character DOT_CHR = '.'; private static final Character DOLLAR_SIGN_CHR = '$'; private static final Character SLASH_CHR = '/'; private List<JavaClass> typeInfoClasses; private Map<String, PackageInfo> packageToPackageInfoMappings; private HashMap<String, XmlNillableInfo> packageToXmlNillableInfoMappings; private Map<String, MarshalCallback> marshalCallbacks; private Map<String, QName> userDefinedSchemaTypes; private Map<String, TypeInfo> typeInfos; private List<QName> typeQNames; private Map<String, UnmarshalCallback> unmarshalCallbacks; private Map<String, Map<QName, ElementDeclaration>> elementDeclarations; private Map<String, ElementDeclaration> xmlRootElements; private List<ElementDeclaration> localElements; private Map<String, JavaMethod> factoryMethods; private Map<String, org.eclipse.persistence.jaxb.xmlmodel.XmlRegistry> xmlRegistries; private List<String> objectFactoryClassNames; private List<JavaClass> classesToProcessPropertyTypes; private Map<String, Class> arrayClassesToGeneratedClasses; private Map<Class, JavaClass> generatedClassesToArrayClasses; private Map<java.lang.reflect.Type, Class> collectionClassesToGeneratedClasses; private Map<Class, java.lang.reflect.Type> generatedClassesToCollectionClasses; private Map<JavaClass, List<TypeMappingInfo>> javaClassToTypeMappingInfos; private Map<TypeMappingInfo, Class> typeMappingInfosToGeneratedClasses; private Map<TypeMappingInfo, Class> typeMappingInfoToAdapterClasses; private Map<TypeMappingInfo, QName> typeMappingInfosToSchemaTypes; private Helper helper; private String defaultTargetNamespace; private JAXBMetadataLogger logger; private boolean isDefaultNamespaceAllowed; private boolean xmlAccessorFactorySupport; private boolean hasSwaRef; private List<String> referencedByTransformer; private boolean hasXmlBindings = false; private boolean facets; public AnnotationsProcessor(Helper helper) { this.helper = helper; this.facets = helper.isFacets(); isDefaultNamespaceAllowed = true; hasSwaRef = false; } /** * This event is called when annotation processing is completed, * and provides a chance to deference anything that is no longer * needed (to reduce the memory footprint of this object). */ void postInitialize() { typeInfoClasses = null; packageToPackageInfoMappings = null; typeInfos = null; typeQNames = null; elementDeclarations = null; xmlRootElements = null; localElements = null; factoryMethods = null; xmlRegistries = null; objectFactoryClassNames = null; classesToProcessPropertyTypes = null; javaClassToTypeMappingInfos = null; typeMappingInfosToGeneratedClasses = null; typeMappingInfoToAdapterClasses = null; helper = null; logger = null; referencedByTransformer = null; } /** * Generate TypeInfo instances for a given array of JavaClasses. * * @param classes */ void processClassesAndProperties(JavaClass[] classes, TypeMappingInfo[] typeMappingInfos) { init(classes, typeMappingInfos); preBuildTypeInfo(classes); postBuildTypeInfo(classes); processPropertyTypes(this.typeInfoClasses.toArray(new JavaClass[this.typeInfoClasses.size()])); finalizeProperties(); createElementsForTypeMappingInfo(); checkForCallbackMethods(); } public void createElementsForTypeMappingInfo() { if (javaClassToTypeMappingInfos != null && !javaClassToTypeMappingInfos.isEmpty()) { Set<JavaClass> classes = this.javaClassToTypeMappingInfos.keySet(); for (JavaClass nextClass : classes) { List<TypeMappingInfo> nextInfos = this.javaClassToTypeMappingInfos.get(nextClass); for(TypeMappingInfo nextInfo:nextInfos) { if (nextInfo != null) { boolean xmlAttachmentRef = false; String xmlMimeType = null; java.lang.annotation.Annotation[] annotations = getAnnotations(nextInfo); Class adapterClass = typeMappingInfoToAdapterClasses.get(nextInfo); Class declJavaType = null; if (adapterClass != null) { declJavaType = CompilerHelper.getTypeFromAdapterClass(adapterClass); } if (annotations != null) { for (Annotation nextAnnotation : annotations) { if (nextAnnotation != null) { if (nextAnnotation instanceof XmlMimeType) { XmlMimeType javaAnnotation = (XmlMimeType) nextAnnotation; xmlMimeType = javaAnnotation.value(); } else if (nextAnnotation instanceof XmlAttachmentRef) { xmlAttachmentRef = true; if (!this.hasSwaRef) { this.hasSwaRef = true; } } } } } QName qname; String nextClassName = nextClass.getQualifiedName(); if (declJavaType != null) { nextClassName = declJavaType.getCanonicalName(); } if (typeMappingInfosToGeneratedClasses != null) { Class generatedClass = typeMappingInfosToGeneratedClasses.get(nextInfo); if (generatedClass != null) { nextClassName = generatedClass.getCanonicalName(); } } TypeInfo nextTypeInfo = typeInfos.get(nextClassName); if (nextTypeInfo != null) { qname = new QName(nextTypeInfo.getClassNamespace(), nextTypeInfo.getSchemaTypeName()); } else { qname = getUserDefinedSchemaTypes().get(nextClassName); if (qname == null) { if (nextClassName.equals(ClassConstants.APBYTE.getName()) || nextClassName.equals(Image.class.getName()) || nextClassName.equals(Source.class.getName()) || nextClassName.equals("javax.activation.DataHandler")) { if (xmlAttachmentRef) { qname = Constants.SWA_REF_QNAME; } else { qname = Constants.BASE_64_BINARY_QNAME; } } else if (nextClassName.equals(ClassConstants.OBJECT.getName())) { qname = Constants.ANY_TYPE_QNAME; } else if (nextClassName.equals(ClassConstants.XML_GREGORIAN_CALENDAR.getName())) { qname = Constants.ANY_SIMPLE_TYPE_QNAME; } else { Class theClass = helper.getClassForJavaClass(nextClass); qname = (QName) XMLConversionManager.getDefaultJavaTypes().get(theClass); } } } if (qname != null) { typeMappingInfosToSchemaTypes.put(nextInfo, qname); } if (nextInfo.getXmlTagName() != null) { ElementDeclaration element = new ElementDeclaration(nextInfo.getXmlTagName(), nextClass, nextClass.getQualifiedName(), false); element.setTypeMappingInfo(nextInfo); element.setXmlMimeType(xmlMimeType); element.setXmlAttachmentRef(xmlAttachmentRef); element.setNillable(nextInfo.isNillable()); if (declJavaType != null) { element.setJavaType(helper.getJavaClass(declJavaType)); } Class generatedClass = typeMappingInfosToGeneratedClasses.get(nextInfo); if (generatedClass != null) { element.setJavaType(helper.getJavaClass(generatedClass)); } if (nextInfo.getElementScope() == TypeMappingInfo.ElementScope.Global) { ElementDeclaration currentElement = this.getGlobalElements().get(element.getElementName()); if (currentElement == null) { addGlobalElement(element.getElementName(), element); } else { // if(currentElement.getTypeMappingInfo() == null) { // the global element that exists came from an annotation //} else { this.localElements.add(element); //} } } else { this.localElements.add(element); } String rootNamespace = element.getElementName().getNamespaceURI(); if (rootNamespace == null) { rootNamespace = Constants.EMPTY_STRING; } if (rootNamespace.equals(Constants.EMPTY_STRING)) { isDefaultNamespaceAllowed = false; } } } } } } } /** * Returns an array of Annotations for a given TypeMappingInfo. This array * will either be populated from the TypeMappingInfo's array of annotations, * or based on an xml-element if present. The xml-element will take * precedence over the annotation array; if there is an xml-element the * Array of Annotations will be ignored. * * @param tmInfo * @return */ private java.lang.annotation.Annotation[] getAnnotations(TypeMappingInfo tmInfo) { if (tmInfo.getXmlElement() != null) { ClassLoader loader = helper.getClassLoader(); // create a single ConversionManager for that will be shared by the // proxy objects ConversionManager cMgr = new ConversionManager(); cMgr.setLoader(loader); // unmarshal the node into an XmlElement org.eclipse.persistence.jaxb.xmlmodel.XmlElement xElt = CompilerHelper.getXmlElement(tmInfo.getXmlElement(), loader); List annotations = new ArrayList(); // where applicable, a given dynamic proxy will contain a Map of // method name/return value entries Map<String, Object> components = null; // handle @XmlElement: set 'type' method if (!(xElt.getType().equals("javax.xml.bind.annotation.XmlElement.DEFAULT"))) { components = new HashMap<String, Object>(); components.put(TYPE_METHOD_NAME, xElt.getType()); annotations.add(AnnotationProxy.getProxy(components, XmlElement.class, loader, cMgr)); } // handle @XmlList if (xElt.isXmlList()) { annotations.add(AnnotationProxy.getProxy(components, XmlList.class, loader, cMgr)); } // handle @XmlAttachmentRef if (xElt.isXmlAttachmentRef()) { annotations.add(AnnotationProxy.getProxy(components, XmlAttachmentRef.class, loader, cMgr)); } // handle @XmlMimeType: set 'value' method if (xElt.getXmlMimeType() != null) { components = new HashMap<String, Object>(); components.put(VALUE_METHOD_NAME, xElt.getXmlMimeType()); annotations.add(AnnotationProxy.getProxy(components, XmlMimeType.class, loader, cMgr)); } // handle @XmlJavaTypeAdapter: set 'type' and 'value' methods if (xElt.getXmlJavaTypeAdapter() != null) { components = new HashMap<String, Object>(); components.put(TYPE_METHOD_NAME, xElt.getXmlJavaTypeAdapter().getType()); components.put(VALUE_METHOD_NAME, xElt.getXmlJavaTypeAdapter().getValue()); annotations.add(AnnotationProxy.getProxy(components, XmlJavaTypeAdapter.class, loader, cMgr)); } // return the newly created array of dynamic proxy objects return (java.lang.annotation.Annotation[]) annotations.toArray(new java.lang.annotation.Annotation[annotations.size()]); } // no xml-element set on the info, (i.e. no xml overrides) so return the // array of Annotation objects return tmInfo.getAnnotations(); } /** * Initialize maps, lists, etc. Typically called prior to processing a set * of classes via preBuildTypeInfo, postBuildTypeInfo, processJavaClasses. */ void init(JavaClass[] classes, TypeMappingInfo[] typeMappingInfos) { typeInfoClasses = new ArrayList<JavaClass>(); referencedByTransformer = new ArrayList<String>(); typeInfos = new HashMap<String, TypeInfo>(); typeQNames = new ArrayList<QName>(); classesToProcessPropertyTypes = new ArrayList<JavaClass>(); objectFactoryClassNames = new ArrayList<String>(); userDefinedSchemaTypes = new HashMap<String, QName>(); if (packageToPackageInfoMappings == null) { packageToPackageInfoMappings = new HashMap<String, PackageInfo>(); } this.factoryMethods = new HashMap<String, JavaMethod>(); this.xmlRegistries = new HashMap<String, org.eclipse.persistence.jaxb.xmlmodel.XmlRegistry>(); this.xmlRootElements = new HashMap<String, ElementDeclaration>(); arrayClassesToGeneratedClasses = new HashMap<String, Class>(); collectionClassesToGeneratedClasses = new HashMap<java.lang.reflect.Type, Class>(); generatedClassesToArrayClasses = new HashMap<Class, JavaClass>(); generatedClassesToCollectionClasses = new HashMap<Class, java.lang.reflect.Type>(); typeMappingInfosToGeneratedClasses = new HashMap<TypeMappingInfo, Class>(); typeMappingInfosToSchemaTypes = new HashMap<TypeMappingInfo, QName>(); elementDeclarations = new HashMap<String, Map<QName, ElementDeclaration>>(); Map globalElements = new HashMap<QName, ElementDeclaration>(); elementDeclarations.put(XmlElementDecl.GLOBAL.class.getName(), globalElements); localElements = new ArrayList<ElementDeclaration>(); javaClassToTypeMappingInfos = new HashMap<JavaClass, List<TypeMappingInfo>>(); typeMappingInfoToAdapterClasses = new HashMap<TypeMappingInfo, Class>(); if (typeMappingInfos != null) { for (int i = 0; i < typeMappingInfos.length; i++) { List<TypeMappingInfo> infos = javaClassToTypeMappingInfos.get(classes[i]); if(infos == null) { infos = new ArrayList<TypeMappingInfo>(); javaClassToTypeMappingInfos.put(classes[i], infos); } infos.add(typeMappingInfos[i]); java.lang.annotation.Annotation[] annotations = getAnnotations(typeMappingInfos[i]); if (annotations != null) { for (java.lang.annotation.Annotation nextAnnotation : annotations) { if (nextAnnotation instanceof XmlJavaTypeAdapter) { typeMappingInfoToAdapterClasses.put(typeMappingInfos[i], ((XmlJavaTypeAdapter) nextAnnotation).value()); } } } } } } /** * Process class level annotations only. It is assumed that a call to init() * has been made prior to calling this method. After the types created via * this method have been modified (if necessary) postBuildTypeInfo and * processJavaClasses should be called to finish processing. * * @param javaClasses * @return */ public Map<String, TypeInfo> preBuildTypeInfo(JavaClass[] javaClasses) { for (JavaClass javaClass : javaClasses) { String qualifiedName = javaClass.getQualifiedName(); TypeInfo info = typeInfos.get(qualifiedName); if (javaClass == null || javaClass.isArray()|| (info!=null && info.isPreBuilt()) || !shouldGenerateTypeInfo(javaClass) || isXmlRegistry(javaClass) ) { continue; } if (javaClass.isEnum()) { info = new EnumTypeInfo(helper, javaClass); } else { info = new TypeInfo(helper, javaClass); } info.setJavaClassName(qualifiedName); info.setPreBuilt(true); // handle @XmlTransient if (helper.isAnnotationPresent(javaClass, XmlTransient.class)) { info.setXmlTransient(true); } // handle @XmlElementNillable processXmlElementNillable(javaClass, info); // handle @XmlExtensible processXmlExtensible(javaClass, info); // handle @XmlInlineBinaryData if (helper.isAnnotationPresent(javaClass, XmlInlineBinaryData.class)) { info.setInlineBinaryData(true); } // handle @NamedObjectGraph processNamedObjectGraphs(javaClass, info); // handle @XmlRootElement processXmlRootElement(javaClass, info); // handle @XmlSeeAlso processXmlSeeAlso(javaClass, info); PackageInfo packageInfo = getPackageInfoForPackage(javaClass); if(packageInfo != null && packageInfo.getPackageLevelAdaptersByClass().size() > 0){ for(String adapterClass :packageInfo.getPackageLevelAdaptersByClass().keySet()){ JavaClass boundType = packageInfo.getPackageLevelAdaptersByClass().get(adapterClass); info.getPackageLevelAdaptersByClass().put(adapterClass, boundType); } } NamespaceInfo namespaceInfo = packageInfo.getNamespaceInfo(); // handle @XmlType preProcessXmlType(javaClass, info, namespaceInfo); // handle @XmlAccessorType preProcessXmlAccessorType(javaClass, info, namespaceInfo); // handle @XmlAccessorOrder preProcessXmlAccessorOrder(javaClass, info, namespaceInfo); // handle package level @XmlJavaTypeAdapters processPackageLevelAdapters(javaClass, info); // handle Accessor Factory processAccessorFactory(javaClass, info); // handle class level @XmlJavaTypeAdapters processClassLevelAdapters(javaClass, info); // handle descriptor customizer preProcessCustomizer(javaClass, info); // handle package level @XmlSchemaType(s) processSchemaTypes(javaClass, info); // handle class extractor if (helper.isAnnotationPresent(javaClass, XmlClassExtractor.class)) { XmlClassExtractor classExtractor = (XmlClassExtractor) helper.getAnnotation(javaClass, XmlClassExtractor.class); info.setClassExtractorName(classExtractor.value().getName()); } // handle user properties if (helper.isAnnotationPresent(javaClass, XmlProperties.class)) { XmlProperties xmlProperties = (XmlProperties) helper.getAnnotation(javaClass, XmlProperties.class); Map<Object, Object> propertiesMap = createUserPropertiesMap(xmlProperties.value()); info.setUserProperties(propertiesMap); } else if (helper.isAnnotationPresent(javaClass, XmlProperty.class)) { XmlProperty xmlProperty = (XmlProperty) helper.getAnnotation(javaClass, XmlProperty.class); Map<Object, Object> propertiesMap = createUserPropertiesMap(new XmlProperty[] { xmlProperty }); info.setUserProperties(propertiesMap); } // handle class indicator field name if (helper.isAnnotationPresent(javaClass, XmlDiscriminatorNode.class)) { XmlDiscriminatorNode xmlDiscriminatorNode = (XmlDiscriminatorNode) helper.getAnnotation(javaClass, XmlDiscriminatorNode.class); info.setXmlDiscriminatorNode(xmlDiscriminatorNode.value()); } // handle class indicator if (helper.isAnnotationPresent(javaClass, XmlDiscriminatorValue.class)) { XmlDiscriminatorValue xmlDiscriminatorValue = (XmlDiscriminatorValue) helper.getAnnotation(javaClass, XmlDiscriminatorValue.class); info.setXmlDiscriminatorValue(xmlDiscriminatorValue.value()); } typeInfoClasses.add(javaClass); typeInfos.put(info.getJavaClassName(), info); } return typeInfos; } private void processXmlElementNillable(JavaClass javaClass, TypeInfo info) { if (helper.isAnnotationPresent(javaClass, XmlElementNillable.class)) { XmlElementNillable xmlElementNillable = (XmlElementNillable) helper.getAnnotation(javaClass, XmlElementNillable.class); info.setXmlElementNillable(xmlElementNillable.nillable()); } else if (hasExternalPackageMapping(javaClass)) { info.setXmlElementNillable(packageToXmlNillableInfoMappings.get(javaClass.getPackageName()).getXmlElementNillable().isNillable()); } else if (helper.isAnnotationPresent(javaClass.getPackage(), XmlElementNillable.class)) { XmlElementNillable xmlElementNillable = (XmlElementNillable) helper.getAnnotation(javaClass.getPackage(), XmlElementNillable.class); info.setXmlElementNillable(xmlElementNillable.nillable()); } } private boolean hasExternalPackageMapping(JavaClass javaClass) { if (null == packageToXmlNillableInfoMappings || !packageToXmlNillableInfoMappings.containsKey(javaClass.getPackageName())) { return false; } return null != packageToXmlNillableInfoMappings.get(javaClass.getPackageName()).getXmlElementNillable(); } private void processNamedObjectGraphs(JavaClass javaClass, TypeInfo info) { List<XmlNamedObjectGraph> objectGraphs = new ArrayList<XmlNamedObjectGraph>(); if(helper.isAnnotationPresent(javaClass, XmlNamedObjectGraphs.class)) { XmlNamedObjectGraphs graphs = (XmlNamedObjectGraphs)helper.getAnnotation(javaClass, XmlNamedObjectGraphs.class); Collections.addAll(objectGraphs, graphs.value()); } if(helper.isAnnotationPresent(javaClass, XmlNamedObjectGraph.class)) { objectGraphs.add((XmlNamedObjectGraph)helper.getAnnotation(javaClass, XmlNamedObjectGraph.class)); } for(XmlNamedObjectGraph next:objectGraphs) { org.eclipse.persistence.jaxb.xmlmodel.XmlNamedObjectGraph namedGraph = new org.eclipse.persistence.jaxb.xmlmodel.XmlNamedObjectGraph(); namedGraph.setName(next.name()); for(XmlNamedAttributeNode nextNode:next.attributeNodes()) { org.eclipse.persistence.jaxb.xmlmodel.XmlNamedAttributeNode namedNode = new org.eclipse.persistence.jaxb.xmlmodel.XmlNamedAttributeNode(); namedNode.setName(nextNode.value()); namedNode.setSubgraph(nextNode.subgraph()); namedGraph.getXmlNamedAttributeNode().add(namedNode); } for(XmlNamedSubgraph nextSubgraph:next.subgraphs()) { org.eclipse.persistence.jaxb.xmlmodel.XmlNamedSubgraph namedSubGraph = new org.eclipse.persistence.jaxb.xmlmodel.XmlNamedSubgraph(); namedSubGraph.setName(nextSubgraph.name()); namedSubGraph.setType(nextSubgraph.type().getName()); for(XmlNamedAttributeNode nextNode:nextSubgraph.attributeNodes()) { org.eclipse.persistence.jaxb.xmlmodel.XmlNamedAttributeNode namedNode = new org.eclipse.persistence.jaxb.xmlmodel.XmlNamedAttributeNode(); namedNode.setName(nextNode.value()); namedNode.setSubgraph(nextNode.subgraph()); namedSubGraph.getXmlNamedAttributeNode().add(namedNode); } namedGraph.getXmlNamedSubgraph().add(namedSubGraph); } for(XmlNamedSubgraph nextSubgraph:next.subclassSubgraphs()) { org.eclipse.persistence.jaxb.xmlmodel.XmlNamedSubgraph namedSubGraph = new org.eclipse.persistence.jaxb.xmlmodel.XmlNamedSubgraph(); namedSubGraph.setName(nextSubgraph.name()); namedSubGraph.setType(nextSubgraph.type().getName()); for(XmlNamedAttributeNode nextNode:nextSubgraph.attributeNodes()) { org.eclipse.persistence.jaxb.xmlmodel.XmlNamedAttributeNode namedNode = new org.eclipse.persistence.jaxb.xmlmodel.XmlNamedAttributeNode(); namedNode.setName(nextNode.value()); namedNode.setSubgraph(nextNode.subgraph()); namedSubGraph.getXmlNamedAttributeNode().add(namedNode); } namedGraph.getXmlNamedSubclassGraph().add(namedSubGraph); } info.getObjectGraphs().add(namedGraph); } } private void processAccessorFactory(JavaClass javaClass, TypeInfo info) { if (!xmlAccessorFactorySupport) { return; } Annotation xmlAccessorFactory = helper.getAnnotation(javaClass, CompilerHelper.ACCESSOR_FACTORY_ANNOTATION_CLASS); Method valueMethod = null; if(xmlAccessorFactory != null) { valueMethod = CompilerHelper.ACCESSOR_FACTORY_VALUE_METHOD; } else { //try for internal annotation xmlAccessorFactory = helper.getAnnotation(javaClass, CompilerHelper.INTERNAL_ACCESSOR_FACTORY_ANNOTATION_CLASS); if(xmlAccessorFactory != null) { valueMethod = CompilerHelper.INTERNAL_ACCESSOR_FACTORY_VALUE_METHOD; } } if(xmlAccessorFactory != null) { Class xmlAccessorFactoryClass = null; try { xmlAccessorFactoryClass = (Class)PrivilegedAccessHelper.invokeMethod(valueMethod, xmlAccessorFactory, new Object[]{}); info.setXmlAccessorFactory(new AccessorFactoryWrapper(PrivilegedAccessHelper.newInstanceFromClass(xmlAccessorFactoryClass))); } catch (Exception ex) { throw JAXBException.errorInstantiatingAccessorFactory(xmlAccessorFactoryClass, ex); } } PackageInfo pInfo = getPackageInfoForPackage(javaClass); if(pInfo != null) { info.setPackageLevelXmlAccessorFactory(pInfo.getAccessorFactory()); } } /** * Process any additional classes (i.e. inner classes, @XmlSeeAlso, * {@literal @XmlRegistry}, etc.) for a given set of JavaClasses, then complete * building all of the required TypeInfo objects. This method * is typically called after init and preBuildTypeInfo have * been called. * * @param javaClasses * @return updated array of JavaClasses, made up of the original classes * plus any additional ones */ public JavaClass[] postBuildTypeInfo(JavaClass[] javaClasses) { if (javaClasses.length == 0) { return javaClasses; } List<JavaClass> originalList = Arrays.asList(javaClasses); // create type info instances for any additional classes javaClasses = processAdditionalClasses(javaClasses); preBuildTypeInfo(javaClasses); buildTypeInfo(javaClasses); updateGlobalElements(javaClasses); if(javaClasses.length > originalList.size()) { List<JavaClass> newClasses = new ArrayList<JavaClass>(javaClasses.length - originalList.size()); for(JavaClass next:javaClasses) { if(!(originalList.contains(next))) { newClasses.add(next); } } postBuildTypeInfo(newClasses.toArray(new JavaClass[newClasses.size()])); } return javaClasses; } /** * INTERNAL: * * Complete building TypeInfo objects for a given set of JavaClass * instances. This method assumes that init, preBuildTypeInfo, and * postBuildTypeInfo have been called. * * @param allClasses * @return */ private Map<String, TypeInfo> buildTypeInfo(JavaClass[] allClasses) { for (JavaClass javaClass : allClasses) { if (javaClass == null) { continue; } TypeInfo info = typeInfos.get(javaClass.getQualifiedName()); if (info == null || info.isPostBuilt()) { continue; } info.setPostBuilt(true); // handle factory methods processFactoryMethods(javaClass, info); PackageInfo packageInfo = getPackageInfoForPackage(javaClass); XMLNameTransformer transformer = info.getXmlNameTransformer(); if(transformer == TypeInfo.DEFAULT_NAME_TRANSFORMER){ XMLNameTransformer nsInfoXmlNameTransformer = packageInfo.getXmlNameTransformer(); if (nsInfoXmlNameTransformer != null) { info.setXmlNameTransformer(nsInfoXmlNameTransformer); } else if (helper.isAnnotationPresent(javaClass, XmlNameTransformer.class)) { XmlNameTransformer xmlNameTransformer = (XmlNameTransformer) helper.getAnnotation(javaClass, XmlNameTransformer.class); Class nameTransformerClass = xmlNameTransformer.value(); try { info.setXmlNameTransformer((XMLNameTransformer) nameTransformerClass.newInstance()); } catch (InstantiationException ex) { throw JAXBException.exceptionWithNameTransformerClass(nameTransformerClass.getName(), ex); } catch (IllegalAccessException ex) { throw JAXBException.exceptionWithNameTransformerClass(nameTransformerClass.getName(), ex); } } else if (helper.isAnnotationPresent(javaClass.getPackage(), XmlNameTransformer.class)) { XmlNameTransformer xmlNameTransformer = (XmlNameTransformer) helper.getAnnotation(javaClass.getPackage(), XmlNameTransformer.class); Class nameTransformerClass = xmlNameTransformer.value(); try { info.setXmlNameTransformer((XMLNameTransformer) nameTransformerClass.newInstance()); } catch (InstantiationException ex) { throw JAXBException.exceptionWithNameTransformerClass(nameTransformerClass.getName(), ex); } catch (IllegalAccessException ex) { throw JAXBException.exceptionWithNameTransformerClass(nameTransformerClass.getName(), ex); } } } // handle @XmlAccessorType postProcessXmlAccessorType(info, packageInfo); // handle @XmlType postProcessXmlType(javaClass, info, packageInfo); // handle @XmlEnum if (info.isEnumerationType()) { addEnumTypeInfo(javaClass, ((EnumTypeInfo) info)); continue; } // process schema type name processTypeQName(javaClass, info, packageInfo.getNamespaceInfo()); // handle superclass if necessary JavaClass superClass = javaClass.getSuperclass(); processReferencedClass(superClass); processPropertiesSuperClass(javaClass, info); // add properties info.setProperties(getPropertiesForClass(javaClass, info)); // process properties processTypeInfoProperties(javaClass, info); // handle @XmlAccessorOrder postProcessXmlAccessorOrder(info, packageInfo); validatePropOrderForInfo(info); } return typeInfos; } private TypeInfo processReferencedClass(JavaClass referencedClass){ if (shouldGenerateTypeInfo(referencedClass)) { String qName = referencedClass.getQualifiedName(); TypeInfo existingInfo = typeInfos.get(qName); if (existingInfo == null || !existingInfo.isPreBuilt()) { PackageInfo pInfo = getPackageInfoForPackage(referencedClass); JavaClass adapterClass = pInfo.getPackageLevelAdaptersByClass().get(qName); if (adapterClass == null) { CompilerHelper.addClassToClassLoader(referencedClass, helper.getClassLoader()); JavaClass[] jClassArray = new JavaClass[] { referencedClass }; buildNewTypeInfo(jClassArray); } return typeInfos.get(qName); } else { if (!existingInfo.isPostBuilt()) { PackageInfo pInfo = getPackageInfoForPackage(referencedClass); JavaClass adapterClass = pInfo.getPackageLevelAdaptersByClass().get(qName); if (adapterClass == null) { CompilerHelper.addClassToClassLoader(referencedClass, helper.getClassLoader()); JavaClass[] javaClasses = new JavaClass[] { referencedClass }; javaClasses = postBuildTypeInfo(javaClasses); for(JavaClass next:javaClasses) { processPropertyTypes(next); } } } return existingInfo; } } return null; } /* * Get virtual property and XmlID information from parent and set it on info if available */ public void processPropertiesSuperClass(JavaClass cls, TypeInfo info) { JavaClass superClass = cls.getSuperclass(); if (superClass == null) { return; } TypeInfo superClassInfo = this.typeInfos.get(superClass.getQualifiedName()); if(superClassInfo != null) { processPropertiesSuperClass(superClass, superClassInfo); classesToProcessPropertyTypes.add(superClass); if(superClassInfo.getXmlVirtualAccessMethods() != null && info.getXmlVirtualAccessMethods() == null) { info.setXmlVirtualAccessMethods(superClassInfo.getXmlVirtualAccessMethods()); } if(superClassInfo.isIDSet()){ info.setIDProperty(superClassInfo.getIDProperty()); } } } /** * Perform any final generation and/or validation operations on TypeInfo * properties. * */ public void finalizeProperties() { for (TypeInfo tInfo: getTypeInfos().values()) { // don't need to validate props on a transient class at this point if (tInfo.isTransient()) { continue; } JavaClass jClass = tInfo.getJavaClass(); String[] propOrder = tInfo.getPropOrder(); boolean hasPropOrder = propOrder.length > 0 && !(propOrder.length == 1 && propOrder[0].equals(Constants.EMPTY_STRING)); // If a property is marked transient, ensure it doesn't exist in the propOrder List<String> propOrderList = Arrays.asList(tInfo.getPropOrder()); List<Property> propsList = tInfo.getPropertyList(); for (Property p : propsList) { if (p.isTransient() && propOrderList.contains(p.getPropertyName())) { throw JAXBException.transientInProporder(p.getPropertyName()); } if (hasPropOrder && !p.isAttribute() && !p.isTransient() && !p.isInverseReference()) { if (!propOrderList.contains(p.getPropertyName())) { throw JAXBException.missingPropertyInPropOrder(p.getPropertyName(), tInfo.getJavaClassName()); } } } if (!jClass.isInterface() && !tInfo.isEnumerationType() && !jClass.isAbstract()) { if (tInfo.getFactoryMethodName() == null && tInfo.getObjectFactoryClassName() == null) { JavaConstructor zeroArgConstructor = jClass.getDeclaredConstructor(new JavaClass[] {}); if (zeroArgConstructor == null) { if (tInfo.isSetXmlJavaTypeAdapter()) { tInfo.setTransient(true); } else { if(!referencedByTransformer.contains(jClass.getName())){ throw org.eclipse.persistence.exceptions.JAXBException.factoryMethodOrConstructorRequired(jClass.getName()); } } } } } // validate XmlValue if (tInfo.getXmlValueProperty() != null) { validateXmlValueFieldOrProperty(jClass, tInfo.getXmlValueProperty()); } // Keep a list of "any" properties to verify if multiples exist // that they have different element wrappers List<Property> anyElementProperties = new ArrayList<Property>(); for (Property property : tInfo.getPropertyList()) { // Check that @XmlAttribute references a Java type that maps to text in XML if (property.isAttribute()) { validateXmlAttributeFieldOrProperty(tInfo, property); } JavaClass propertyClass = property.getActualType(); if (property.isChoice()) { Collection<Property> choiceProps = property.getChoiceProperties(); for (Property nextChoiceProp : choiceProps) { JavaClass nextChoicePropTypeClass = nextChoiceProp.getActualType(); TypeInfo targetInfo = typeInfos.get(nextChoicePropTypeClass.getQualifiedName()); finalizeProperty(property, targetInfo, nextChoicePropTypeClass, jClass); } } else { TypeInfo targetInfo = typeInfos.get(propertyClass.getQualifiedName()); finalizeProperty(property, targetInfo, propertyClass, jClass); } // only one XmlValue is allowed per class, and if there is one // only XmlAttributes are allowed if (tInfo.isSetXmlValueProperty()) { if (property.isXmlValue() && !(tInfo.getXmlValueProperty().getPropertyName().equals(property.getPropertyName()))) { throw JAXBException.xmlValueAlreadySet(property.getPropertyName(), tInfo.getXmlValueProperty().getPropertyName(), jClass.getName()); } if (!property.isXmlValue() && !property.isAttribute() && !property.isInverseReference() && !property.isTransient()) { throw JAXBException.propertyOrFieldShouldBeAnAttribute(property.getPropertyName()); } } // handle XmlElementRef(s) - validate and build the required // ElementDeclaration object if (property.isReference()) { processReferenceProperty(property, tInfo, jClass); } if (property.isSwaAttachmentRef() && !this.hasSwaRef) { this.hasSwaRef = true; } if (property.isXmlId()) { // there can only be one XmlID per type info if (tInfo.getIDProperty() != null && !(tInfo.getIDProperty().getPropertyName().equals(property.getPropertyName()))) { throw JAXBException.idAlreadySet(property.getPropertyName(), tInfo.getIDProperty().getPropertyName(), jClass.getName()); } // XmlID property should be of java.lang.String type validateXmlIdStringType(property); } // there can only be one XmlAnyAttribute per type info if (property.isAnyAttribute() && tInfo.isSetAnyAttributePropertyName() && !(tInfo.getAnyAttributePropertyName().equals(property.getPropertyName()))) { throw JAXBException.multipleAnyAttributeMapping(jClass.getName()); } // there can only be one XmlAnyElement per type info if (property.isAny()) { if(!anyElementProperties.isEmpty()) { for(Property nextAny:anyElementProperties) { if(!property.isSetXmlElementWrapper() && !nextAny.isSetXmlElementWrapper()) { throw JAXBException.xmlAnyElementAlreadySet(property.getPropertyName(), nextAny.getPropertyName(), jClass.getName()); } org.eclipse.persistence.jaxb.xmlmodel.XmlElementWrapper wrapper = property.getXmlElementWrapper(); org.eclipse.persistence.jaxb.xmlmodel.XmlElementWrapper targetWrapper = nextAny.getXmlElementWrapper(); if(wrapper != null && targetWrapper != null) { if(wrapper.getName().equals(targetWrapper.getName()) && wrapper.getNamespace().equals(targetWrapper.getNamespace())) { throw JAXBException.xmlAnyElementAlreadySet(property.getPropertyName(), nextAny.getPropertyName(), jClass.getName()); } } } } anyElementProperties.add(property); } // an XmlAttachmentRef can only appear on a DataHandler property if (property.isSwaAttachmentRef() && !areEquals(property.getActualType(), JAVAX_ACTIVATION_DATAHANDLER)) { throw JAXBException.invalidAttributeRef(property.getPropertyName(), jClass.getQualifiedName()); } // an XmlElementWrapper can only appear on a Collection or Array if (property.getXmlElementWrapper() != null) { if (!helper.isCollectionType(property.getType()) && !property.getType().isArray() && !helper.isMapType(property.getType())) { throw JAXBException.invalidElementWrapper(property.getPropertyName()); } } // handle XmlTransformation - validate transformer class/method if (property.isXmlTransformation()) { validateXmlTransformationProperty(property); } // validate XmlJoinNodes if (property.isSetXmlJoinNodes()) { TypeInfo targetInfo = typeInfos.get(propertyClass.getQualifiedName()); // the target class must have an associated TypeInfo if (targetInfo == null) { throw JAXBException.invalidXmlJoinNodeReferencedClass(property.getPropertyName(), propertyClass.getQualifiedName()); } // validate each referencedXmlPath - target TypeInfo should // have XmlID/XmlKey property with matching XmlPath if (targetInfo.getIDProperty() == null && targetInfo.getXmlKeyProperties() == null) { throw JAXBException.noKeyOrIDPropertyOnJoinTarget(jClass.getQualifiedName(), property.getPropertyName(), propertyClass.getQualifiedName()); } } } } } private void validateXmlIdStringType(Property property) { if (!"java.lang.String".equals(property.getActualType().getQualifiedName()) && !MOXySystemProperties.xmlIdExtension && !helper.isAnnotationPresent(property.getElement(), XmlIDExtension.class) && !property.isXmlIdExtension()) { throw JAXBException.invalidId(property.getPropertyName()); } } private void finalizeProperty(Property property, TypeInfo targetInfo, JavaClass typeClass, JavaClass jClass){ if (targetInfo != null && targetInfo.isTransient() && property.getXmlElements() == null) { property.setTransientType(true); } // validate XmlIDREF if (property.isXmlIdRef()) { // the target class must have an associated TypeInfo unless // it is Object if (targetInfo == null && !typeClass.getQualifiedName().equals(JAVA_LANG_OBJECT)) { throw JAXBException.invalidIDREFClass(jClass.getQualifiedName(), property.getPropertyName(), typeClass.getQualifiedName()); } // if the property is an XmlIDREF, the target must have an // XmlID set if (targetInfo != null && targetInfo.getIDProperty() == null) { throw JAXBException.invalidIdRef(property.getPropertyName(), typeClass.getQualifiedName()); } } } /** * Process a given TypeInfo instance's properties. * * @param info */ private void processTypeInfoProperties(JavaClass javaClass, TypeInfo info) { List<Property> properties = info.getPropertyList(); for (Property property : properties) { // handle @XmlID processXmlID(property, javaClass, info); // handle @XmlIDREF - validate these properties after processing of // all types is completed processXmlIDREF(property); if (property.isMap()){ processReferencedClass(property.getKeyType()); processReferencedClass(property.getActualValueType()); } } } void processPropertyTypes(JavaClass[] classes) { for (JavaClass next : classes) { processPropertyTypes(next); classesToProcessPropertyTypes.remove(next); } for (int i =0; i< classesToProcessPropertyTypes.size(); i++) { // throws cme when using foreach JavaClass next = classesToProcessPropertyTypes.get(i); processPropertyTypes(next); } } private void processPropertyTypes(JavaClass next){ TypeInfo info = getTypeInfos().get(next.getQualifiedName()); if (info != null) { for (Property property : info.getPropertyList()) { if (property.isXmlLocation()) info.setLocationAware(true); if (property.isTransient()) continue; JavaClass type = property.getActualType(); if (property.isReference()) { processReferencePropertyTypes(property, info, next); } if (property.isChoice()) { processChoiceProperty(property, info, next, type); for (Property choiceProp : property.getChoiceProperties()) { type = choiceProp.getActualType(); processReferencedClass(type); } } else { processReferencedClass(type); } } } } /** * Process any additional classes, such as inner classes, @XmlRegistry or * from @XmlSeeAlso. * * @param classes * @return */ private JavaClass[] processAdditionalClasses(JavaClass[] classes) { ArrayList<JavaClass> extraClasses = new ArrayList<JavaClass>(); ArrayList<JavaClass> classesToProcess = new ArrayList<JavaClass>(); for (JavaClass jClass : classes) { List<TypeMappingInfo> infos = this.javaClassToTypeMappingInfos.get(jClass); if(infos != null && infos.size() > 0) { for(TypeMappingInfo next:infos) { processAdditionalClasses(jClass, next, extraClasses, classesToProcess); } } else { processAdditionalClasses(jClass, null, extraClasses, classesToProcess); } } // process @XmlRegistry, @XmlSeeAlso and inner classes for (JavaClass javaClass : extraClasses) { processClass(javaClass, classesToProcess); } return classesToProcess.toArray(new JavaClass[classesToProcess.size()]); } private void processAdditionalClasses(JavaClass cls, TypeMappingInfo tmi, ArrayList<JavaClass> extraClasses, ArrayList<JavaClass> classesToProcess) { Class xmlElementType = null; JavaClass javaClass = cls; if (tmi != null) { Class adapterClass = this.typeMappingInfoToAdapterClasses.get(tmi); if (adapterClass != null) { JavaClass adapterJavaClass = helper.getJavaClass(adapterClass); JavaClass newType = helper.getJavaClass(Object.class); // look for marshal method for (Object nextMethod : adapterJavaClass.getDeclaredMethods()) { JavaMethod method = (JavaMethod) nextMethod; if (method.getName().equals("marshal")) { JavaClass returnType = method.getReturnType(); if (!returnType.getQualifiedName().equals(newType.getQualifiedName())) { newType = returnType; break; } } } if (!helper.isBuiltInJavaType(javaClass)) { extraClasses.add(javaClass); } javaClass = newType; } java.lang.annotation.Annotation[] annotations = getAnnotations(tmi); if (annotations != null) { for (Annotation nextAnnotation : annotations) { if (nextAnnotation != null && nextAnnotation instanceof XmlElement) { XmlElement javaAnnotation = (XmlElement) nextAnnotation; if (javaAnnotation.type() != XmlElement.DEFAULT.class) { xmlElementType = javaAnnotation.type(); } } } } } if (areEquals(javaClass, byte[].class) || areEquals(javaClass, JAVAX_ACTIVATION_DATAHANDLER) || areEquals(javaClass, Source.class) || areEquals(javaClass, Image.class) || areEquals(javaClass, JAVAX_MAIL_INTERNET_MIMEMULTIPART)) { if (tmi == null || tmi.getXmlTagName() == null) { ElementDeclaration declaration = new ElementDeclaration(null, javaClass, javaClass.getQualifiedName(), false, XmlElementDecl.GLOBAL.class); declaration.setTypeMappingInfo(tmi); this.localElements.add(declaration); } } else if (javaClass.isArray()) { if (!helper.isBuiltInJavaType(javaClass.getComponentType())) { extraClasses.add(javaClass.getComponentType()); } Class generatedClass; if (null == tmi) { generatedClass = arrayClassesToGeneratedClasses.get(javaClass.getName()); } else { generatedClass = CompilerHelper.getExisitingGeneratedClass(tmi, typeMappingInfosToGeneratedClasses, typeMappingInfoToAdapterClasses, helper.getClassLoader()); } if (generatedClass == null) { generatedClass = generateWrapperForArrayClass(javaClass, tmi, xmlElementType, extraClasses); extraClasses.add(helper.getJavaClass(generatedClass)); arrayClassesToGeneratedClasses.put(javaClass.getName(), generatedClass); } generatedClassesToArrayClasses.put(generatedClass, javaClass); typeMappingInfosToGeneratedClasses.put(tmi, generatedClass); } else if (helper.isCollectionType(javaClass)) { JavaClass componentClass; Collection args = javaClass.getActualTypeArguments(); if (!args.isEmpty()) { componentClass = (JavaClass) args.iterator().next(); if (!componentClass.isPrimitive()) { extraClasses.add(componentClass); } } else { componentClass = helper.getJavaClass(Object.class); } Class generatedClass = CompilerHelper.getExisitingGeneratedClass(tmi, typeMappingInfosToGeneratedClasses, typeMappingInfoToAdapterClasses, helper.getClassLoader()); if (generatedClass == null) { generatedClass = generateCollectionValue(javaClass, tmi, xmlElementType, extraClasses); extraClasses.add(helper.getJavaClass(generatedClass)); } typeMappingInfosToGeneratedClasses.put(tmi, generatedClass); } else if (helper.isMapType(javaClass)) { JavaClass keyClass; JavaClass valueClass; Collection args = javaClass.getActualTypeArguments(); Iterator argsIter = args.iterator(); if (!args.isEmpty()) { keyClass = (JavaClass) argsIter.next(); if (!helper.isBuiltInJavaType(keyClass)) { extraClasses.add(keyClass); } valueClass = (JavaClass) argsIter.next(); if (!helper.isBuiltInJavaType(valueClass)) { extraClasses.add(valueClass); } } else { keyClass = helper.getJavaClass(Object.class); valueClass = helper.getJavaClass(Object.class); } Class generatedClass = CompilerHelper.getExisitingGeneratedClass(tmi, typeMappingInfosToGeneratedClasses, typeMappingInfoToAdapterClasses, helper.getClassLoader()); if (generatedClass == null) { generatedClass = generateWrapperForMapClass(javaClass, keyClass, valueClass, tmi); extraClasses.add(helper.getJavaClass(generatedClass)); } typeMappingInfosToGeneratedClasses.put(tmi, generatedClass); } else { // process @XmlRegistry, @XmlSeeAlso and inner classes processClass(javaClass, classesToProcess); } } /** * Adds additional classes to the given List, from inner classes, * * See @XmlRegistry or @XmlSeeAlso. * * @param javaClass * @param classesToProcess */ private void processClass(JavaClass javaClass, ArrayList<JavaClass> classesToProcess) { if (shouldGenerateTypeInfo(javaClass)) { if (isXmlRegistry(javaClass)) { this.processObjectFactory(javaClass, classesToProcess); } else { classesToProcess.add(javaClass); // handle @XmlSeeAlso TypeInfo info = typeInfos.get(javaClass.getQualifiedName()); if (info != null && info.isSetXmlSeeAlso()) { for (String jClassName : info.getXmlSeeAlso()) { classesToProcess.add(helper.getJavaClass(jClassName)); } } } } } /** * Process an @XmlSeeAlso annotation. TypeInfo instances will be created for * each class listed. * * @param javaClass */ private void processXmlSeeAlso(JavaClass javaClass, TypeInfo info) { // reflectively load @XmlSeeAlso class to avoid dependency Class xmlSeeAlsoClass = null; Method valueMethod = null; try { xmlSeeAlsoClass = PrivilegedAccessHelper.getClassForName("javax.xml.bind.annotation.XmlSeeAlso", false, helper.getClassLoader()); valueMethod = PrivilegedAccessHelper.getDeclaredMethod(xmlSeeAlsoClass, "value", new Class[] {}); } catch (ClassNotFoundException ex) { // Ignore this exception. If SeeAlso isn't available, don't try to // process } catch (NoSuchMethodException ex) { } if (xmlSeeAlsoClass != null && helper.isAnnotationPresent(javaClass, xmlSeeAlsoClass)) { Object seeAlso = helper.getAnnotation(javaClass, xmlSeeAlsoClass); Class[] values = null; try { values = (Class[]) PrivilegedAccessHelper.invokeMethod(valueMethod, seeAlso, new Object[] {}); } catch (Exception ex) { } if (values != null) { List<String> seeAlsoClassNames = new ArrayList<String>(); for (Class next : values) { seeAlsoClassNames.add(next.getName()); } info.setXmlSeeAlso(seeAlsoClassNames); } } } /** * Process any factory methods. * * @param javaClass * @param info */ private void processFactoryMethods(JavaClass javaClass, TypeInfo info) { JavaMethod factoryMethod = this.factoryMethods.get(javaClass.getRawName()); if (factoryMethod != null) { // set up factory method info for mappings. info.setFactoryMethodName(factoryMethod.getName()); info.setObjectFactoryClassName(factoryMethod.getOwningClass().getQualifiedName()); JavaClass[] paramTypes = factoryMethod.getParameterTypes(); if (paramTypes != null && paramTypes.length > 0) { String[] paramTypeNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { processReferencedClass(paramTypes[i]); paramTypeNames[i] = paramTypes[i].getQualifiedName(); } info.setFactoryMethodParamTypes(paramTypeNames); } } } /** * Process any package-level @XmlJavaTypeAdapters. * * @param javaClass * @param info */ private void processPackageLevelAdapters(JavaClass javaClass, TypeInfo info) { JavaPackage pack = javaClass.getPackage(); if (helper.isAnnotationPresent(pack, XmlJavaTypeAdapters.class)) { XmlJavaTypeAdapters adapters = (XmlJavaTypeAdapters) helper.getAnnotation(pack, XmlJavaTypeAdapters.class); XmlJavaTypeAdapter[] adapterArray = adapters.value(); for (XmlJavaTypeAdapter next : adapterArray) { processPackageLevelAdapter(next, info); } } if (helper.isAnnotationPresent(pack, XmlJavaTypeAdapter.class)) { XmlJavaTypeAdapter adapter = (XmlJavaTypeAdapter) helper.getAnnotation(pack, XmlJavaTypeAdapter.class); processPackageLevelAdapter(adapter, info); } } private void processPackageLevelAdapter(XmlJavaTypeAdapter next, TypeInfo info) { JavaClass adapterClass = helper.getJavaClass(next.value()); JavaClass boundType = helper.getJavaClass(next.type()); if (boundType != null) { info.addPackageLevelAdapterClass(adapterClass, boundType); } else { getLogger().logWarning(JAXBMetadataLogger.INVALID_BOUND_TYPE, new Object[] { null, adapterClass }); } } /** * Process any class-level @XmlJavaTypeAdapters. * * @param javaClass * @param info */ private void processClassLevelAdapters(JavaClass javaClass, TypeInfo info) { if (helper.isAnnotationPresent(javaClass, XmlJavaTypeAdapter.class)) { XmlJavaTypeAdapter adapter = (XmlJavaTypeAdapter) helper.getAnnotation(javaClass, XmlJavaTypeAdapter.class); String boundType = adapter.type().getName(); if (boundType == null || boundType.equals("javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter.DEFAULT")) { boundType = javaClass.getRawName(); } org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter xja = new org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter(); xja.setValue(adapter.value().getName()); xja.setType(boundType); info.setXmlJavaTypeAdapter(xja); } } /** * Process any @XmlSchemaType(s). * * @param javaClass * @param info */ private void processSchemaTypes(JavaClass javaClass, TypeInfo info) { JavaPackage pack = javaClass.getPackage(); if (helper.isAnnotationPresent(pack, XmlSchemaTypes.class)) { XmlSchemaTypes types = (XmlSchemaTypes) helper.getAnnotation(pack, XmlSchemaTypes.class); XmlSchemaType[] typeArray = types.value(); for (XmlSchemaType next : typeArray) { processSchemaType(next); } } else if (helper.isAnnotationPresent(pack, XmlSchemaType.class)) { processSchemaType((XmlSchemaType) helper.getAnnotation(pack, XmlSchemaType.class)); } } /** * Process @XmlRootElement annotation on a given JavaClass. * * @param javaClass * @param info */ private void processXmlRootElement(JavaClass javaClass, TypeInfo info) { if (helper.isAnnotationPresent(javaClass, XmlRootElement.class)) { XmlRootElement rootElemAnnotation = (XmlRootElement) helper.getAnnotation(javaClass, XmlRootElement.class); org.eclipse.persistence.jaxb.xmlmodel.XmlRootElement xmlRE = new org.eclipse.persistence.jaxb.xmlmodel.XmlRootElement(); xmlRE.setName(rootElemAnnotation.name()); xmlRE.setNamespace(rootElemAnnotation.namespace()); info.setXmlRootElement(xmlRE); } } /** * Process @XmlExtensible annotation on a given JavaClass. * * @param javaClass * @param info */ private void processXmlExtensible(JavaClass javaClass, TypeInfo info) { if (helper.isAnnotationPresent(javaClass, XmlVirtualAccessMethods.class)) { XmlVirtualAccessMethods extAnnotation = (XmlVirtualAccessMethods) helper.getAnnotation(javaClass, XmlVirtualAccessMethods.class); org.eclipse.persistence.jaxb.xmlmodel.XmlVirtualAccessMethods xmlExt = new org.eclipse.persistence.jaxb.xmlmodel.XmlVirtualAccessMethods(); xmlExt.setGetMethod(extAnnotation.getMethod()); xmlExt.setSetMethod(extAnnotation.setMethod()); xmlExt.setSchema(org.eclipse.persistence.jaxb.xmlmodel.XmlVirtualAccessMethodsSchema.valueOf(extAnnotation.schema().toString())); info.setXmlVirtualAccessMethods(xmlExt); } } /** * Process @XmlType annotation on a given JavaClass and update the TypeInfo * for pre-processing. Note that if no @XmlType annotation is present we * still create a new XmlType an set it on the TypeInfo. * * @param javaClass * @param info * @param packageNamespace */ private void preProcessXmlType(JavaClass javaClass, TypeInfo info, NamespaceInfo packageNamespace) { org.eclipse.persistence.jaxb.xmlmodel.XmlType xmlType = new org.eclipse.persistence.jaxb.xmlmodel.XmlType(); // 14 xmlType=XmlType - default settings: name=null, namespace=null, factoryClass=null, factoryMethod=null, propOrder=null. if (helper.isAnnotationPresent(javaClass, XmlType.class)) { XmlType typeAnnotation = (XmlType) helper.getAnnotation(javaClass, XmlType.class); // 15 typeAnnotation=com.sun.proxy.$Proxy6"@javax.xml.bind.annotation.XmlType(factoryMethod=, name=OneClass, propOrder=[car], factoryClass=class javax.xml.bind.annotation.XmlType$DEFAULT, namespace=##default)" // set name xmlType.setName(typeAnnotation.name()); // 16 XmlType - name="OneClass // set namespace xmlType.setNamespace(typeAnnotation.namespace()); // 17 xmlType - namespace="##default" // set propOrder String[] propOrder = typeAnnotation.propOrder(); // 18 propOrder = ["car"] // initializes xmlType.propOrder to an empty ArrayList if (propOrder != null) { xmlType.getPropOrder(); // 19 OK, so this only initializes xmlType.propOrder to an empty ArrayList } for (String prop : propOrder) { xmlType.getPropOrder().add(prop); // 20 - puts "car" into xmlType.propOrder } // set factoryClass Class factoryClass = typeAnnotation.factoryClass(); // 21 factoryClass=java.lang.Class"class javax.xml.bind.annotation.XmlType$DEFAULT" if (factoryClass == DEFAULT.class) { xmlType.setFactoryClass("javax.xml.bind.annotation.XmlType.DEFAULT"); // 22 } else { xmlType.setFactoryClass(factoryClass.getCanonicalName()); } // set factoryMethodName xmlType.setFactoryMethod(typeAnnotation.factoryMethod()); // 23 defaults to factoryMethod="" } else { // set defaults xmlType.setNamespace(packageNamespace.getNamespace()); } info.setXmlType(xmlType); // 24 } /** * Process XmlType for a given TypeInfo. Here we assume that the TypeInfo * has an XmlType set - typically via preProcessXmlType or XmlProcessor * override. * * @param javaClass * @param info * @param packageNamespace */ private void postProcessXmlType(JavaClass javaClass, TypeInfo info, PackageInfo packageNamespace) { // assumes that the TypeInfo has an XmlType set from org.eclipse.persistence.jaxb.xmlmodel.XmlType xmlType = info.getXmlType(); // set/validate factoryClass and factoryMethod String factoryClassName = xmlType.getFactoryClass(); String factoryMethodName = xmlType.getFactoryMethod(); if (factoryClassName.equals("javax.xml.bind.annotation.XmlType.DEFAULT")) { if (factoryMethodName != null && !factoryMethodName.equals(EMPTY_STRING)) { // factory method applies to the current class - verify method // exists JavaMethod method = javaClass.getDeclaredMethod(factoryMethodName, new JavaClass[] {}); if (method == null) { throw org.eclipse.persistence.exceptions.JAXBException.factoryMethodNotDeclared(factoryMethodName, javaClass.getName()); } info.setFactoryMethodName(factoryMethodName); } } else { if (factoryMethodName == null || factoryMethodName.equals(EMPTY_STRING)) { throw org.eclipse.persistence.exceptions.JAXBException.factoryClassWithoutFactoryMethod(javaClass.getName()); } info.setObjectFactoryClassName(factoryClassName); info.setFactoryMethodName(factoryMethodName); } // figure out type name String typeName = xmlType.getName(); if (typeName.equals(XMLProcessor.DEFAULT)) { try { typeName = info.getXmlNameTransformer().transformTypeName(javaClass.getName()); } catch (Exception ex) { throw org.eclipse.persistence.exceptions.JAXBException.exceptionDuringNameTransformation(javaClass.getName(), info.getXmlNameTransformer().getClass().getName(), ex); } } info.setSchemaTypeName(typeName); // set propOrder if (xmlType.isSetPropOrder()) { List<String> props = xmlType.getPropOrder(); if (props.size() == 0) { info.setPropOrder(new String[0]); } else if (props.get(0).equals(EMPTY_STRING)) { info.setPropOrder(new String[] { EMPTY_STRING }); } else { info.setPropOrder(xmlType.getPropOrder().toArray(new String[xmlType.getPropOrder().size()])); } } // figure out namespace if (xmlType.getNamespace().equals(XMLProcessor.DEFAULT)) { info.setClassNamespace(packageNamespace.getNamespace()); } else { info.setClassNamespace(xmlType.getNamespace()); } } /** * Process @XmlAccessorType annotation on a given JavaClass and update the * TypeInfo for pre-processing. * * @param javaClass * @param info * @param packageNamespace */ private void preProcessXmlAccessorType(JavaClass javaClass, TypeInfo info, NamespaceInfo packageNamespace) { org.eclipse.persistence.jaxb.xmlmodel.XmlAccessType xmlAccessType; if (javaClass.getDeclaredAnnotation(helper.getJavaClass(XmlAccessorType.class)) != null) { XmlAccessorType accessorType = (XmlAccessorType) helper.getAnnotation(javaClass, XmlAccessorType.class); xmlAccessType = org.eclipse.persistence.jaxb.xmlmodel.XmlAccessType.fromValue(accessorType.value().name()); info.setXmlAccessType(xmlAccessType); } } /** * Post process XmlAccessorType. In some cases, such as @XmlSeeAlso classes, * the access type may not have been set * * @param info */ private void postProcessXmlAccessorType(TypeInfo info, PackageInfo packageNamespace) { if (!info.isSetXmlAccessType()) { // Check for super class JavaClass next = helper.getJavaClass(info.getJavaClassName()).getSuperclass(); while (next != null && !(next.getName().equals(JAVA_LANG_OBJECT))) { processReferencedClass(next); TypeInfo parentInfo = this.typeInfos.get(next.getName()); if (parentInfo != null && parentInfo.isSetXmlAccessType()) { info.setXmlAccessType(parentInfo.getXmlAccessType()); break; } next = next.getSuperclass(); } // use value in package-info.java as last resort - will default if // not set if(!(info.isSetXmlAccessType())) { info.setXmlAccessType(org.eclipse.persistence.jaxb.xmlmodel.XmlAccessType.fromValue(packageNamespace.getAccessType().name())); } } } /** * Process package and class @XmlAccessorOrder. Class level annotation * overrides a package level annotation. * * @param javaClass * @param info * @param packageNamespace */ private void preProcessXmlAccessorOrder(JavaClass javaClass, TypeInfo info, NamespaceInfo packageNamespace) { XmlAccessorOrder order = null; // class level annotation overrides package level annotation if (helper.isAnnotationPresent(javaClass, XmlAccessorOrder.class)) { order = (XmlAccessorOrder) helper.getAnnotation(javaClass, XmlAccessorOrder.class); info.setXmlAccessOrder(XmlAccessOrder.fromValue(order.value().name())); } } /** * Post process XmlAccessorOrder. This method assumes that the given * TypeInfo has already had its order set (via annotations in * preProcessXmlAccessorOrder or via xml metadata override in XMLProcessor). * * @param info * @param packageNamespace */ private void postProcessXmlAccessorOrder(TypeInfo info, PackageInfo packageNamespace) { if (!info.isSetXmlAccessOrder()) { // use value in package-info.java as last resort - will default if // not set info.setXmlAccessOrder(org.eclipse.persistence.jaxb.xmlmodel.XmlAccessOrder.fromValue(packageNamespace.getAccessOrder().name())); } info.orderProperties(); } /** * Process @XmlElement annotation on a given property. * * @param property */ private void processXmlElement(Property property, TypeInfo info) { if (helper.isAnnotationPresent(property.getElement(), XmlElementNillable.class)) { XmlElementNillable elementNillable = (XmlElementNillable) helper.getAnnotation(property.getElement(), XmlElementNillable.class); property.setNillable(elementNillable.nillable()); } else if (info.isXmlElementNillable()) { property.setNillable(true); } if (helper.isAnnotationPresent(property.getElement(), XmlElement.class)) { XmlElement element = (XmlElement) helper.getAnnotation(property.getElement(), XmlElement.class); property.setIsRequired(element.required()); property.setNillable(element.nillable()); if (element.type() != XmlElement.DEFAULT.class && !(property.isSwaAttachmentRef())) { property.setOriginalType(property.getType()); if (helper.isCollectionType(property.getType()) || property.getType().isArray()) { property.setGenericType(helper.getJavaClass(element.type())); } else { JavaClass originalType = property.getType(); JavaClass newType =helper.getJavaClass(element.type()); if(!originalType.getName().equals(newType.getName())){ property.setTyped(true); property.setSchemaType((QName) helper.getXMLToJavaTypeMap().get(newType.getName())); } property.setType(newType); } property.setHasXmlElementType(true); } // handle default value if (!element.defaultValue().equals(ELEMENT_DECL_DEFAULT)) { property.setDefaultValue(element.defaultValue()); } if (facets) addFacets(property); } } /** * @since 2.6 * @author Marcel Valovy * @param property property for which facets will be generated */ private void addFacets(Property property) { final JavaHasAnnotations element = property.getElement(); if (helper.isAnnotationPresent(element, DecimalMin.class)) { DecimalMin a = (DecimalMin) helper.getAnnotation(element, DecimalMin.class); DecimalMinFacet facet = new DecimalMinFacet(a.value(), a.inclusive()); property.addFacet(facet); } if (helper.isAnnotationPresent(element, DecimalMax.class)) { DecimalMax a = (DecimalMax) helper.getAnnotation(element, DecimalMax.class); DecimalMaxFacet facet = new DecimalMaxFacet(a.value(), a.inclusive()); property.addFacet(facet); } if (helper.isAnnotationPresent(element, Digits.class)) { Digits a = (Digits) helper.getAnnotation(element, Digits.class); DigitsFacet facet = new DigitsFacet(a.integer(), a.fraction()); property.addFacet(facet); } if (helper.isAnnotationPresent(element, Max.class)) { Max a = (Max) helper.getAnnotation(element, Max.class); MaxFacet facet = new MaxFacet(a.value()); property.addFacet(facet); } if (helper.isAnnotationPresent(element, Min.class)) { Min a = (Min) helper.getAnnotation(element, Min.class); MinFacet facet = new MinFacet(a.value()); property.addFacet(facet); } if (helper.isAnnotationPresent(element, NotNull.class)) { property.setNotNullAnnotated(true); } if (helper.isAnnotationPresent(element, Pattern.class)) { Pattern a = (Pattern) helper.getAnnotation(element, Pattern.class); PatternFacet facet = new PatternFacet(a.regexp(), a.flags()); property.addFacet(facet); } /* Example: @Pattern.List({ @Pattern(regexp = "first_expression", message = "first.Pattern.message"), @Pattern(regexp = "second_expression", message = "second.Pattern.message"), @Pattern(regexp = "third_expression", message = "third.Pattern.message") }) */ if (helper.isAnnotationPresent(element, Pattern.List.class)) { Pattern.List a = (Pattern.List) helper.getAnnotation(element, Pattern.List.class); PatternListFacet facet = new PatternListFacet(new ArrayList<PatternFacet>()); for (Pattern pat : a.value()) { PatternFacet pf = new PatternFacet(pat.regexp(), pat.flags()); facet.addPattern(pf); } property.addFacet(facet); } if (helper.isAnnotationPresent(element, Size.class)) { Size a = (Size) helper.getAnnotation(element, Size.class); final int min = a.min(); final int max = a.max(); if (min != 0 || max != Integer.MAX_VALUE) { // Fixes generation of an empty facet. if ("java.lang.String".equals(property.getType().getName())) { // @Size serves for both length facet and occurs restriction. SizeFacet facet = new SizeFacet(min, max); // For minLength, maxLength. property.addFacet(facet); } else { // For minOccurs, maxOccurs. if (min > 0) property.setMinOccurs(min); // 0 is default minBoundary. if (max < Integer.MAX_VALUE) property.setMaxOccurs(max); // 2^31 is default maxBoundary. } } } } /** * Process @XmlID annotation on a given property * * @param property * @param info */ private void processXmlID(Property property, JavaClass javaClass, TypeInfo info) { if (helper.isAnnotationPresent(property.getElement(), XmlID.class)) { property.setIsXmlId(true); info.setIDProperty(property); } } /** * Process @XmlIDREF on a given property. * * @param property */ private void processXmlIDREF(Property property) { if (helper.isAnnotationPresent(property.getElement(), XmlIDREF.class)) { property.setIsXmlIdRef(true); } } /** * Process @XmlJavaTypeAdapter on a given property. * * @param property * @param info * @param javaClass */ private void processXmlJavaTypeAdapter(Property property, TypeInfo info, JavaClass javaClass) { JavaClass adapterClass = null; JavaClass ptype = property.getActualType(); if (helper.isAnnotationPresent(property.getElement(), XmlJavaTypeAdapter.class)) { XmlJavaTypeAdapter adapter = (XmlJavaTypeAdapter) helper.getAnnotation(property.getElement(), XmlJavaTypeAdapter.class); org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter xja = new org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter(); xja.setValue(adapter.value().getName()); xja.setType(adapter.type().getName()); property.setXmlJavaTypeAdapter(xja); } else { TypeInfo ptypeInfo = typeInfos.get(ptype.getQualifiedName()); org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter xmlJavaTypeAdapter; if (ptypeInfo == null && shouldGenerateTypeInfo(ptype)) { if (helper.isAnnotationPresent(ptype, XmlJavaTypeAdapter.class)) { XmlJavaTypeAdapter adapter = (XmlJavaTypeAdapter) helper.getAnnotation(ptype, XmlJavaTypeAdapter.class); org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter xja = new org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter(); xja.setValue(adapter.value().getName()); /*String boundType = adapter.type().getName(); if (boundType == null || boundType.equals("javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter.DEFAULT")) { boundType = ptype.getRawName(); } value from boundType is not used - fix if you know what it should do. */ xja.setType(adapter.type().getName()); property.setXmlJavaTypeAdapter(xja); } } if (ptypeInfo != null) { if (null != (xmlJavaTypeAdapter = ptypeInfo.getXmlJavaTypeAdapter())) { try { property.setXmlJavaTypeAdapter(xmlJavaTypeAdapter); } catch (JAXBException e) { throw JAXBException.invalidTypeAdapterClass(xmlJavaTypeAdapter.getValue(), javaClass.getName()); } } } if(info.hasPackageLevelAdaptersByClass()) { if (info.getPackageLevelAdaptersByClass().get(ptype.getQualifiedName()) != null && !property.isSetXmlJavaTypeAdapter()) { adapterClass = info.getPackageLevelAdapterClass(ptype); org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter xja = new org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter(); xja.setValue(adapterClass.getQualifiedName()); xja.setType(ptype.getQualifiedName()); property.setXmlJavaTypeAdapter(xja); } } } } /** * Store a QName (if necessary) based on a given TypeInfo's schema type * name. * * @param javaClass * @param info */ private void processTypeQName(JavaClass javaClass, TypeInfo info, NamespaceInfo packageNamespace) { if(info.isTransient()) { return; } String typeName = info.getSchemaTypeName(); if (typeName != null && !(EMPTY_STRING.equals(typeName))) { QName typeQName = new QName(info.getClassNamespace(), typeName); boolean containsQName = typeQNames.contains(typeQName); if (containsQName) { throw JAXBException.nameCollision(typeQName.getNamespaceURI(), typeQName.getLocalPart()); } else { typeQNames.add(typeQName); } } } public boolean shouldGenerateTypeInfo(JavaClass javaClass) { if (javaClass == null || javaClass.isPrimitive() || javaClass.isAnnotation() || ORG_W3C_DOM.equals(javaClass.getPackageName())) { return false; } if (userDefinedSchemaTypes.get(javaClass.getQualifiedName()) != null) { return false; } if (javaClass.isArray()) { String javaClassName = javaClass.getName(); if (!(javaClassName.equals(CoreClassConstants.APBYTE.getName()))&& !(javaClassName.equals(CoreClassConstants.ABYTE.getName()))) { return true; } } if (helper.isBuiltInJavaType(javaClass) && !javaClass.isEnum()) { return false; } if (helper.isCollectionType(javaClass) || helper.isMapType(javaClass)) { return false; } return true; } public ArrayList<Property> getPropertiesForClass(JavaClass cls, TypeInfo info) { ArrayList<Property> returnList = new ArrayList<Property>(); if (!info.isTransient()) { JavaClass superClass = cls.getSuperclass(); if (null != superClass) { TypeInfo superClassInfo = typeInfos.get(superClass.getQualifiedName()); ArrayList<Property> superClassProperties; while (superClassInfo != null && superClassInfo.isTransient()) { if (info.getXmlAccessType() == XmlAccessType.FIELD) { superClassProperties = getFieldPropertiesForClass(superClass, superClassInfo, false); } else if (info.getXmlAccessType() == XmlAccessType.PROPERTY) { superClassProperties = getPropertyPropertiesForClass(superClass, superClassInfo, false); } else if (info.getXmlAccessType() == XmlAccessType.PUBLIC_MEMBER) { superClassProperties = getPublicMemberPropertiesForClass(superClass, superClassInfo); } else { superClassProperties = getNoAccessTypePropertiesForClass(superClass, superClassInfo); } superClass = superClass.getSuperclass(); superClassInfo = typeInfos.get(superClass.getQualifiedName()); for(Property next:superClassProperties) { next.setIsSuperClassProperty(true); } returnList.addAll(0, superClassProperties); } } } if (info.isTransient()) { returnList.addAll(getNoAccessTypePropertiesForClass(cls, info)); } else if (info.getXmlAccessType() == XmlAccessType.FIELD) { returnList.addAll(getFieldPropertiesForClass(cls, info, false)); returnList.addAll(getPropertyPropertiesForClass(cls, info, false, true)); } else if (info.getXmlAccessType() == XmlAccessType.PROPERTY) { returnList.addAll(getFieldPropertiesForClass(cls, info, false, true)); returnList.addAll(getPropertyPropertiesForClass(cls, info, false)); } else if (info.getXmlAccessType() == XmlAccessType.PUBLIC_MEMBER) { returnList.addAll(getPublicMemberPropertiesForClass(cls, info)); } else { returnList.addAll(getNoAccessTypePropertiesForClass(cls, info)); } return returnList; } public ArrayList<Property> getFieldPropertiesForClass(JavaClass cls, TypeInfo info, boolean onlyPublic) { return getFieldPropertiesForClass(cls, info, onlyPublic, false); } public ArrayList<Property> getFieldPropertiesForClass(JavaClass cls, TypeInfo info, boolean onlyPublic, boolean onlyExplicit) { ArrayList<Property> properties = new ArrayList<Property>(); if (cls == null) { return properties; } for (JavaField javaField : (Iterable<JavaField>) cls.getDeclaredFields()) { Property property = null; int modifiers = javaField.getModifiers(); if (!Modifier.isTransient(modifiers) && ((Modifier.isPublic(javaField.getModifiers()) && onlyPublic) || !onlyPublic || hasJAXBAnnotations(javaField))) { if (!Modifier.isStatic(modifiers)) { if ((onlyExplicit && hasJAXBAnnotations(javaField)) || !onlyExplicit) { try { property = buildNewProperty(info, cls, javaField, javaField.getName(), javaField.getResolvedType()); properties.add(property); } catch (JAXBException ex) { if (ex.getErrorCode() != JAXBException.INVALID_INTERFACE || !helper.isAnnotationPresent(javaField, XmlTransient.class)) { throw ex; } } } } else { try { property = buildNewProperty(info, cls, javaField, javaField.getName(), javaField.getResolvedType()); if (helper.isAnnotationPresent(javaField, XmlAttribute.class)) { Object value = ((JavaFieldImpl) javaField).get(null); if (value != null) { String stringValue = (String) XMLConversionManager.getDefaultXMLManager().convertObject(value, String.class, property.getSchemaType()); property.setFixedValue(stringValue); } } property.setWriteOnly(true); if (!hasJAXBAnnotations(javaField)) { property.setTransient(true); } properties.add(property); } catch (ClassCastException e) { // do Nothing } catch (IllegalAccessException e) { // do Nothing } catch (JAXBException ex) { if (ex.getErrorCode() != JAXBException.INVALID_INTERFACE || !helper.isAnnotationPresent(javaField, XmlTransient.class)) { throw ex; } } } } if (helper.isAnnotationPresent(javaField, XmlTransient.class)) { if (property != null) { property.setTransient(true); } } } return properties; } /* * Create a new Property Object and process the annotations that are common * to fields and methods */ Property buildNewProperty(TypeInfo info, JavaClass cls, JavaHasAnnotations javaHasAnnotations, String propertyName, JavaClass ptype) { Property property = null; if (helper.isAnnotationPresent(javaHasAnnotations, XmlElements.class)) { property = buildChoiceProperty(javaHasAnnotations); } else if (helper.isAnnotationPresent(javaHasAnnotations, XmlElementRef.class) || helper.isAnnotationPresent(javaHasAnnotations, XmlElementRefs.class)) { findAndProcessObjectFactory(cls); property = buildReferenceProperty(info, javaHasAnnotations, propertyName, ptype); if (helper.isAnnotationPresent(javaHasAnnotations, XmlAnyElement.class)) { XmlAnyElement anyElement = (XmlAnyElement) helper.getAnnotation(javaHasAnnotations, XmlAnyElement.class); property.setIsAny(true); if (anyElement.value() != null) { property.setDomHandlerClassName(anyElement.value().getName()); } property.setLax(anyElement.lax()); info.setAnyElementPropertyName(propertyName); } } else if (helper.isAnnotationPresent(javaHasAnnotations, XmlAnyElement.class)) { findAndProcessObjectFactory(cls); XmlAnyElement anyElement = (XmlAnyElement) helper.getAnnotation(javaHasAnnotations, XmlAnyElement.class); property = new Property(helper); property.setIsAny(true); if (anyElement.value() != null) { property.setDomHandlerClassName(anyElement.value().getName()); } property.setLax(anyElement.lax()); info.setAnyElementPropertyName(propertyName); } else if (helper.isAnnotationPresent(javaHasAnnotations, org.eclipse.persistence.oxm.annotations.XmlTransformation.class) || helper.isAnnotationPresent(javaHasAnnotations, org.eclipse.persistence.oxm.annotations.XmlReadTransformer.class) || helper.isAnnotationPresent(javaHasAnnotations, org.eclipse.persistence.oxm.annotations.XmlWriteTransformer.class) || helper.isAnnotationPresent(javaHasAnnotations, XmlWriteTransformers.class)) { property = buildTransformationProperty(javaHasAnnotations, cls); } else { property = new Property(helper); } property.setPropertyName(propertyName); property.setElement(javaHasAnnotations); // if there is a TypeInfo for ptype check it for transient, otherwise // check the class if (helper.isCollectionType(ptype)) { JavaClass componentType = helper.getJavaClass(Object.class); Collection typeArgs = ptype.getActualTypeArguments(); if(!typeArgs.isEmpty()) { componentType = (JavaClass) typeArgs.iterator().next(); } updatePropertyType(property, ptype, componentType); }else{ updatePropertyType(property, ptype, ptype); } if(helper.isAnnotationPresent(javaHasAnnotations, XmlVariableNode.class)){ XmlVariableNode variableNode = (XmlVariableNode) helper.getAnnotation(javaHasAnnotations, XmlVariableNode.class); if(variableNode.type() != XmlVariableNode.DEFAULT.class){ property.setVariableClassName(variableNode.type().getName()); JavaClass componentType = helper.getJavaClass(variableNode.type()); if(helper.isCollectionType(ptype)){ property.setGenericType(componentType); }else{ property.setType(componentType); } } if(!variableNode.value().equals("##default")){ property.setVariableAttributeName(variableNode.value()); } property.setVariableNodeAttribute(variableNode.attribute()); } if((ptype.isArray() && !areEquals(ptype, byte[].class)) || (helper.isCollectionType(ptype) && !helper.isAnnotationPresent(javaHasAnnotations, XmlList.class)) ){ property.setNillable(true); } processPropertyAnnotations(info, cls, javaHasAnnotations, property); if (helper.isAnnotationPresent(javaHasAnnotations, XmlPath.class)) { XmlPath xmlPath = (XmlPath) helper.getAnnotation(javaHasAnnotations, XmlPath.class); property.setXmlPath(xmlPath.value()); Field tempField = new XMLField(xmlPath.value()); boolean isAttribute = tempField.getLastXPathFragment().isAttribute(); property.setIsAttribute(isAttribute); // set schema name String schemaName = XMLProcessor.getNameFromXPath(xmlPath.value(), property.getPropertyName(), isAttribute); QName qName; NamespaceInfo nsInfo = getPackageInfoForPackage(cls).getNamespaceInfo(); if(isAttribute){ if (nsInfo.isAttributeFormQualified()) { qName = new QName(nsInfo.getNamespace(), schemaName); } else { qName = new QName(schemaName); } }else{ if (nsInfo.isElementFormQualified()) { qName = new QName(nsInfo.getNamespace(), schemaName); } else { qName = new QName(schemaName); } } property.setSchemaName(qName); //create properties for any predicates XPathFragment fragment = tempField.getXPathFragment(); String currentPath = ""; while(fragment != null && !(fragment.nameIsText()) && !(fragment.isAttribute())) { if(fragment.getPredicate() != null) { //can't append xpath directly since it will contain the predicate String fragmentPath = fragment.getLocalName(); if(fragment.getPrefix() != null && !(Constants.EMPTY_STRING.equals(fragment.getPrefix()))) { fragmentPath = fragment.getPrefix() + ":" + fragmentPath; } currentPath += fragmentPath; String predicatePath = currentPath; TypeInfo targetInfo = info; if(fragment.getNextFragment() == null) { //if this is the last fragment, and there's no text after, then this is //complex. May need to add the attribute property to the target type. processReferencedClass(ptype); TypeInfo predicateTypeInfo = typeInfos.get(ptype.getQualifiedName()); if(predicateTypeInfo != null) { targetInfo = predicateTypeInfo; predicatePath = ""; } } Property predicateProperty = new Property(helper); predicateProperty.setType(helper.getJavaClass("java.lang.String")); if(predicatePath.length() > 0) { predicatePath += "/"; } predicatePath += fragment.getPredicate().getXPathFragment().getXPath(); predicateProperty.setXmlPath(predicatePath); predicateProperty.setIsAttribute(true); String predschemaName = XMLProcessor.getNameFromXPath(predicatePath, property.getPropertyName(), true); QName predQname; if (nsInfo.isAttributeFormQualified()) { predQname = new QName(nsInfo.getNamespace(), predschemaName); } else { predQname = new QName(predschemaName); } predicateProperty.setSchemaName(predQname); if(!targetInfo.hasPredicateProperty(predicateProperty)) { targetInfo.getPredicateProperties().add(predicateProperty); } } else { currentPath += fragment.getXPath(); } currentPath += "/"; fragment = fragment.getNextFragment(); } } else { property.setSchemaName(getQNameForProperty(property, propertyName, javaHasAnnotations, getPackageInfoForPackage(cls).getNamespaceInfo(), info)); } ptype = property.getActualType(); if (ptype.isPrimitive()) { if (property.getType().isArray() && helper.isAnnotationPresent(javaHasAnnotations, XmlElement.class)) { XmlElement elemAnno = (XmlElement) helper.getAnnotation(javaHasAnnotations, XmlElement.class); property.setIsRequired(elemAnno.required()); } else { property.setIsRequired(true); } } // apply class level adapters - don't override property level adapter if (!property.isSetXmlJavaTypeAdapter()) { TypeInfo refClassInfo = getTypeInfos().get(ptype.getQualifiedName()); if (refClassInfo != null && refClassInfo.isSetXmlJavaTypeAdapter()) { org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter xmlJavaTypeAdapter = null; try { xmlJavaTypeAdapter = refClassInfo.getXmlJavaTypeAdapter(); property.setXmlJavaTypeAdapter(refClassInfo.getXmlJavaTypeAdapter()); } catch (JAXBException e) { throw JAXBException.invalidTypeAdapterClass(xmlJavaTypeAdapter.getValue(), cls.getName()); } } } if(property.isXmlTransformation()){ referencedByTransformer.add(property.getType().getName()); } return property; } private void updatePropertyType(Property property, JavaClass ptype, JavaClass componentType){ TypeInfo componentTypeInfo = typeInfos.get(componentType.getQualifiedName()); if((componentTypeInfo != null && !componentTypeInfo.isTransient()) || !helper.isAnnotationPresent(componentType, XmlTransient.class)){ property.setType(ptype); }else{ JavaClass parent = componentType.getSuperclass(); while (parent != null) { if (parent.getName().equals(JAVA_LANG_OBJECT)) { property.setTransientType(true); property.setType(ptype); break; } // if there is a TypeInfo for parent check it for transient, // otherwise check the class TypeInfo parentTypeInfo = typeInfos.get(parent.getQualifiedName()); if ((parentTypeInfo != null && !parentTypeInfo.isTransient()) || !helper.isAnnotationPresent(parent, XmlTransient.class)) { property.setType(parent); break; } parent = parent.getSuperclass(); } } } /** * Build a new 'choice' property. Here, we flag a new property as a 'choice' * and create/set an XmlModel XmlElements object based on the @XmlElements * annotation. * * Validation and building of the XmlElement properties will be done during * finalizeProperties in the processChoiceProperty method. * * @param javaHasAnnotations * @return */ private Property buildChoiceProperty(JavaHasAnnotations javaHasAnnotations) { Property choiceProperty = new Property(helper); choiceProperty.setChoice(true); boolean isIdRef = helper.isAnnotationPresent(javaHasAnnotations, XmlIDREF.class); choiceProperty.setIsXmlIdRef(isIdRef); // build an XmlElement to set on the Property org.eclipse.persistence.jaxb.xmlmodel.XmlElements xmlElements = new org.eclipse.persistence.jaxb.xmlmodel.XmlElements(); XmlElement[] elements = ((XmlElements) helper.getAnnotation(javaHasAnnotations, XmlElements.class)).value(); for (XmlElement next : elements) { org.eclipse.persistence.jaxb.xmlmodel.XmlElement xmlElement = new org.eclipse.persistence.jaxb.xmlmodel.XmlElement(); xmlElement.setDefaultValue(next.defaultValue()); xmlElement.setName(next.name()); xmlElement.setNamespace(next.namespace()); xmlElement.setNillable(next.nillable()); xmlElement.setRequired(next.required()); xmlElement.setType(next.type().getName()); xmlElements.getXmlElement().add(xmlElement); } choiceProperty.setXmlElements(xmlElements); // handle XmlElementsJoinNodes if (helper.isAnnotationPresent(javaHasAnnotations, XmlElementsJoinNodes.class)) { org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes xmlJoinNodes; org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode xmlJoinNode; List<org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes> xmlJoinNodesList = new ArrayList<org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes>(); List<org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode> xmlJoinNodeList = null; for (XmlJoinNodes xmlJNs : ((XmlElementsJoinNodes) helper.getAnnotation(javaHasAnnotations, XmlElementsJoinNodes.class)).value()) { xmlJoinNodeList = new ArrayList<org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode>(); for (XmlJoinNode xmlJN : xmlJNs.value()) { xmlJoinNode = new org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode(); xmlJoinNode.setXmlPath(xmlJN.xmlPath()); xmlJoinNode.setReferencedXmlPath(xmlJN.referencedXmlPath()); xmlJoinNodeList.add(xmlJoinNode); } if (xmlJoinNodeList.size() > 0) { xmlJoinNodes = new org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes(); xmlJoinNodes.setXmlJoinNode(xmlJoinNodeList); xmlJoinNodesList.add(xmlJoinNodes); } } choiceProperty.setXmlJoinNodesList(xmlJoinNodesList); } return choiceProperty; } private Property buildTransformationProperty(JavaHasAnnotations javaHasAnnotations, JavaClass cls) { Property property = new Property(helper); org.eclipse.persistence.oxm.annotations.XmlTransformation transformationAnnotation = (org.eclipse.persistence.oxm.annotations.XmlTransformation) helper.getAnnotation(javaHasAnnotations, org.eclipse.persistence.oxm.annotations.XmlTransformation.class); XmlTransformation transformation = new XmlTransformation(); if (transformationAnnotation != null) { transformation.setOptional(transformationAnnotation.optional()); } // Read Transformer org.eclipse.persistence.oxm.annotations.XmlReadTransformer readTransformer = (org.eclipse.persistence.oxm.annotations.XmlReadTransformer) helper.getAnnotation(javaHasAnnotations, org.eclipse.persistence.oxm.annotations.XmlReadTransformer.class); if (readTransformer != null) { org.eclipse.persistence.jaxb.xmlmodel.XmlTransformation.XmlReadTransformer xmlReadTransformer = new org.eclipse.persistence.jaxb.xmlmodel.XmlTransformation.XmlReadTransformer(); if (!(readTransformer.transformerClass() == AttributeTransformer.class)) { xmlReadTransformer.setTransformerClass(readTransformer.transformerClass().getName()); } else if (!(readTransformer.method().equals(EMPTY_STRING))) { xmlReadTransformer.setMethod(readTransformer.method()); } transformation.setXmlReadTransformer(xmlReadTransformer); } // Handle Write Transformers org.eclipse.persistence.oxm.annotations.XmlWriteTransformer[] transformers = null; if (helper.isAnnotationPresent(javaHasAnnotations, org.eclipse.persistence.oxm.annotations.XmlWriteTransformer.class)) { org.eclipse.persistence.oxm.annotations.XmlWriteTransformer writeTransformer = (org.eclipse.persistence.oxm.annotations.XmlWriteTransformer) helper.getAnnotation(javaHasAnnotations, org.eclipse.persistence.oxm.annotations.XmlWriteTransformer.class); transformers = new org.eclipse.persistence.oxm.annotations.XmlWriteTransformer[] { writeTransformer }; } else if (helper.isAnnotationPresent(javaHasAnnotations, XmlWriteTransformers.class)) { XmlWriteTransformers writeTransformers = (XmlWriteTransformers) helper.getAnnotation(javaHasAnnotations, XmlWriteTransformers.class); transformers = writeTransformers.value(); } if (transformers != null) { for (org.eclipse.persistence.oxm.annotations.XmlWriteTransformer next : transformers) { org.eclipse.persistence.jaxb.xmlmodel.XmlTransformation.XmlWriteTransformer xmlWriteTransformer = new org.eclipse.persistence.jaxb.xmlmodel.XmlTransformation.XmlWriteTransformer(); if (!(next.transformerClass() == FieldTransformer.class)) { xmlWriteTransformer.setTransformerClass(next.transformerClass().getName()); } else if (!(next.method().equals(EMPTY_STRING))) { xmlWriteTransformer.setMethod(next.method()); } xmlWriteTransformer.setXmlPath(next.xmlPath()); transformation.getXmlWriteTransformer().add(xmlWriteTransformer); } } property.setXmlTransformation(transformation); property.setIsXmlTransformation(true); return property; } /** * Complete creation of a 'choice' property. Here, a Property is created for * each XmlElement in the XmlElements list. Validation is performed as well. * Each created Property is added to the owning Property's list of choice * properties. * * @param choiceProperty * @param info * @param cls * @param propertyType */ private void processChoiceProperty(Property choiceProperty, TypeInfo info, JavaClass cls, JavaClass propertyType) { String propertyName = choiceProperty.getPropertyName(); // validate XmlElementsXmlJoinNodes (if set) if (choiceProperty.isSetXmlJoinNodesList()) { // there must be one XmlJoinNodes entry per XmlElement if (choiceProperty.getXmlElements().getXmlElement().size() != choiceProperty.getXmlJoinNodesList().size()) { throw JAXBException.incorrectNumberOfXmlJoinNodesOnXmlElements(propertyName, cls.getQualifiedName()); } } XmlPath[] paths = null; if (helper.isAnnotationPresent(choiceProperty.getElement(), XmlPaths.class)) { XmlPaths pathAnnotation = (XmlPaths) helper.getAnnotation(choiceProperty.getElement(), XmlPaths.class); paths = pathAnnotation.value(); } List<Property> choiceProperties = new ArrayList<Property>(); for (int i = 0; i < choiceProperty.getXmlElements().getXmlElement().size(); i++) { org.eclipse.persistence.jaxb.xmlmodel.XmlElement next = choiceProperty.getXmlElements().getXmlElement().get(i); Property choiceProp = new Property(helper); String name; String namespace; choiceProp.setNillable(next.isNillable()); choiceProp.setIsRequired(next.isRequired()); // handle XmlPath - if xml-path is set, we ignore name/namespace if (paths != null && next.getXmlPath() == null) { // Only set the path, if the path hasn't already been set from // xml XmlPath nextPath = paths[i]; next.setXmlPath(nextPath.value()); } if (next.getXmlPath() != null) { choiceProp.setXmlPath(next.getXmlPath()); boolean isAttribute = new XMLField(next.getXmlPath()).getLastXPathFragment().isAttribute(); // validate attribute - must be in nested path, not at root if (isAttribute && !next.getXmlPath().contains(SLASH)) { throw JAXBException.invalidXmlPathWithAttribute(propertyName, cls.getQualifiedName(), next.getXmlPath()); } choiceProp.setIsAttribute(isAttribute); name = XMLProcessor.getNameFromXPath(next.getXmlPath(), propertyName, isAttribute); namespace = XMLProcessor.DEFAULT; } else { // no xml-path, so use name/namespace from xml-element name = next.getName(); namespace = next.getNamespace(); } if (name == null || name.equals(XMLProcessor.DEFAULT)) { if (next.getJavaAttribute() != null) { name = next.getJavaAttribute(); } else { name = propertyName; } } // if the property has xml-idref, the target type of each // xml-element in the list must have an xml-id property if (choiceProperty.isXmlIdRef()) { JavaClass nextCls = helper.getJavaClass(next.getType()); processReferencedClass(nextCls); TypeInfo tInfo = typeInfos.get(next.getType()); if (tInfo == null || !tInfo.isIDSet()) { throw JAXBException.invalidXmlElementInXmlElementsList(propertyName, name); } } QName qName = null; if (!namespace.equals(XMLProcessor.DEFAULT)) { qName = new QName(namespace, name); } else { NamespaceInfo namespaceInfo = getPackageInfoForPackage(cls).getNamespaceInfo(); if (namespaceInfo.isElementFormQualified()) { qName = new QName(namespaceInfo.getNamespace(), name); } else { qName = new QName(name); } } choiceProp.setPropertyName(name); // figure out the property's type - note that for DEFAULT, if from // XML the value will // be "XmlElement.DEFAULT", and from annotations the value will be // "XmlElement$DEFAULT" if (next.getType().equals("javax.xml.bind.annotation.XmlElement.DEFAULT") || next.getType().equals("javax.xml.bind.annotation.XmlElement$DEFAULT")) { choiceProp.setType(propertyType); } else { choiceProp.setType(helper.getJavaClass(next.getType())); } // handle case of XmlJoinNodes w/XmlElements if (choiceProperty.isSetXmlJoinNodesList()) { // assumes one corresponding xml-join-nodes entry per // xml-element org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes xmlJoinNodes = choiceProperty.getXmlJoinNodesList().get(i); if (xmlJoinNodes != null) { choiceProp.setXmlJoinNodes(xmlJoinNodes); // set type if (!xmlJoinNodes.getType().equals(XMLProcessor.DEFAULT)) { JavaClass pType = helper.getJavaClass(xmlJoinNodes.getType()); if (helper.isCollectionType(choiceProp.getType())) { choiceProp.setGenericType(pType); } else { choiceProp.setType(pType); } } } } choiceProp.setSchemaName(qName); choiceProp.setSchemaType(getSchemaTypeFor(choiceProp.getType())); choiceProp.setIsXmlIdRef(choiceProperty.isXmlIdRef()); choiceProp.setXmlElementWrapper(choiceProperty.getXmlElementWrapper()); choiceProperties.add(choiceProp); processReferencedClass(choiceProp.getType()); TypeInfo newInfo = typeInfos.get(choiceProp.getType().getQualifiedName()); if (newInfo != null && newInfo.isTransient()) { throw JAXBException.invalidReferenceToTransientClass(info.getJavaClassName(), choiceProperty.getPropertyName(), newInfo.getJavaClassName()); } } choiceProperty.setChoiceProperties(choiceProperties); } /** * Build a reference property. Here we will build a list of XML model * XmlElementRef objects, based on the @XmlElement(s) annotation, to store * on the Property. Processing of the elements and validation will be * performed during the finalize property phase via the * processReferenceProperty method. * * @param info * @param javaHasAnnotations * @param propertyName * @param ptype * @return */ private Property buildReferenceProperty(TypeInfo info, JavaHasAnnotations javaHasAnnotations, String propertyName, JavaClass ptype) { Property property = new Property(helper); property.setType(ptype); XmlElementRef[] elementRefs; XmlElementRef ref = (XmlElementRef) helper.getAnnotation(javaHasAnnotations, XmlElementRef.class); if (ref != null) { elementRefs = new XmlElementRef[] { ref }; } else { XmlElementRefs refs = (XmlElementRefs) helper.getAnnotation(javaHasAnnotations, XmlElementRefs.class); elementRefs = refs.value(); info.setElementRefsPropertyName(propertyName); } List<org.eclipse.persistence.jaxb.xmlmodel.XmlElementRef> eltRefs = new ArrayList<org.eclipse.persistence.jaxb.xmlmodel.XmlElementRef>(); for (XmlElementRef nextRef : elementRefs) { org.eclipse.persistence.jaxb.xmlmodel.XmlElementRef eltRef = new org.eclipse.persistence.jaxb.xmlmodel.XmlElementRef(); eltRef.setName(nextRef.name()); eltRef.setNamespace(nextRef.namespace()); eltRef.setType(nextRef.type().getName()); property.setIsRequired(true); try{ Method requireMethod = PrivilegedAccessHelper.getMethod(XmlElementRef.class, "required", new Class[0], true); if(requireMethod != null){ Boolean val = (Boolean)PrivilegedAccessHelper.invokeMethod(requireMethod, nextRef); property.setIsRequired(val); } } catch (Exception exception){ } eltRefs.add(eltRef); } property.setIsReference(true); property.setXmlElementRefs(eltRefs); return property; } /** * Build a reference property. * * @param property * @param info * @param cls * @return */ private Property processReferenceProperty(Property property, TypeInfo info, JavaClass cls) { for (org.eclipse.persistence.jaxb.xmlmodel.XmlElementRef nextRef : property.getXmlElementRefs()) { JavaClass type = property.getType(); String typeName; if (helper.isCollectionType(property.getType())) { if (type.hasActualTypeArguments()) { type = property.getGenericType(); typeName = type.getQualifiedName(); } } if (!(nextRef.getType().equals("javax.xml.bind.annotation.XmlElementRef.DEFAULT") || nextRef.getType().equals("javax.xml.bind.annotation.XmlElementRef$DEFAULT"))) { typeName = nextRef.getType(); type = helper.getJavaClass(typeName); } boolean missingReference = true; for (Entry<String, ElementDeclaration> entry : xmlRootElements.entrySet()) { ElementDeclaration entryValue = entry.getValue(); if (!(areEquals(type, Object.class)) && type.isAssignableFrom(entryValue.getJavaType())) { addReferencedElement(property, entryValue); missingReference = false; } } if (missingReference) { String name = nextRef.getName(); String namespace = nextRef.getNamespace(); if (namespace.equals(XMLProcessor.DEFAULT)) { namespace = EMPTY_STRING; } QName qname = new QName(namespace, name); JavaClass scopeClass = cls; ElementDeclaration referencedElement = null; while (!(scopeClass.getName().equals(JAVA_LANG_OBJECT))) { Map<QName, ElementDeclaration> elements = getElementDeclarationsForScope(scopeClass.getName()); if (elements != null) { referencedElement = elements.get(qname); } if (referencedElement != null) { break; } scopeClass = scopeClass.getSuperclass(); } if (referencedElement == null) { referencedElement = this.getGlobalElements().get(qname); } if (referencedElement != null) { addReferencedElement(property, referencedElement); } else { throw org.eclipse.persistence.exceptions.JAXBException.invalidElementRef(property.getPropertyName(), cls.getName()); } } } return property; } private void processReferencePropertyTypes(Property property, TypeInfo info, JavaClass theClass) { for (org.eclipse.persistence.jaxb.xmlmodel.XmlElementRef nextRef : property.getXmlElementRefs()) { JavaClass type = property.getType(); String typeName = type.getQualifiedName(); if (helper.isCollectionType(property.getType())) { if (type.hasActualTypeArguments()) { type = property.getGenericType(); typeName = type.getQualifiedName(); } } if(JAVAX_XML_BIND_JAXBELEMENT.equals(typeName)){ Collection args = type.getActualTypeArguments(); if(args.size() > 0){ JavaClass theType = (JavaClass) args.iterator().next(); processReferencedClass(theType); } } // for DEFAULT, if from XML the type will be // "XmlElementRef.DEFAULT", // and from annotations the value will be "XmlElementref$DEFAULT" if (!(nextRef.getType().equals("javax.xml.bind.annotation.XmlElementRef.DEFAULT") || nextRef.getType().equals("javax.xml.bind.annotation.XmlElementRef$DEFAULT"))) { typeName = nextRef.getType(); type = helper.getJavaClass(typeName); } processReferencedClass(type); } } private void processPropertyAnnotations(TypeInfo info, JavaClass cls, JavaHasAnnotations propertyElement, Property property) { // Check for mixed context if (helper.isAnnotationPresent(propertyElement, XmlMixed.class)) { info.setMixed(true); property.setMixedContent(true); findAndProcessObjectFactory(cls); } if (helper.isAnnotationPresent(propertyElement, XmlContainerProperty.class)) { XmlContainerProperty container = (XmlContainerProperty) helper.getAnnotation(propertyElement, XmlContainerProperty.class); property.setInverseReferencePropertyName(container.value()); property.setInverseReferencePropertyGetMethodName(container.getMethodName()); property.setInverseReferencePropertySetMethodName(container.setMethodName()); } else if (helper.isAnnotationPresent(propertyElement, XmlInverseReference.class)) { XmlInverseReference inverseReference = (XmlInverseReference) helper.getAnnotation(propertyElement, XmlInverseReference.class); property.setInverseReferencePropertyName(inverseReference.mappedBy()); TypeInfo targetInfo = this.getTypeInfos().get(property.getActualType().getName()); if (targetInfo != null && targetInfo.getXmlAccessType() == XmlAccessType.PROPERTY) { String propName = property.getPropertyName(); propName = Character.toUpperCase(propName.charAt(0)) + propName.substring(1); property.setInverseReferencePropertyGetMethodName(GET_STR + propName); property.setInverseReferencePropertySetMethodName(SET_STR + propName); } property.setInverseReference(true, helper.isAnnotationPresent(propertyElement, XmlElement.class)); } processXmlJavaTypeAdapter(property, info, cls); if (helper.isAnnotationPresent(propertyElement, XmlAttachmentRef.class) && areEquals(property.getActualType(), JAVAX_ACTIVATION_DATAHANDLER)) { property.setIsSwaAttachmentRef(true); property.setSchemaType(Constants.SWA_REF_QNAME); } processXmlElement(property, info); // JavaClass ptype = property.getActualType(); if (!(property.isSwaAttachmentRef()) && isMtomAttachment(property)) { property.setIsMtomAttachment(true); property.setSchemaType(Constants.BASE_64_BINARY_QNAME); } if (helper.isAnnotationPresent(propertyElement, XmlMimeType.class)) { property.setMimeType(((XmlMimeType) helper.getAnnotation(propertyElement, XmlMimeType.class)).value()); } // set indicator for inlining binary data - setting this to true on a // non-binary data type won't have any affect if (helper.isAnnotationPresent(propertyElement, XmlInlineBinaryData.class) || info.isBinaryDataToBeInlined()) { property.setisInlineBinaryData(true); } // Get schema-type info if specified and set it on the property for // later use: if (helper.isAnnotationPresent(propertyElement, XmlSchemaType.class)) { XmlSchemaType schemaType = (XmlSchemaType) helper.getAnnotation(propertyElement, XmlSchemaType.class); QName schemaTypeQname = new QName(schemaType.namespace(), schemaType.name()); property.setSchemaType(schemaTypeQname); } if (helper.isAnnotationPresent(propertyElement, XmlAttribute.class)) { property.setIsAttribute(true); property.setIsRequired(((XmlAttribute) helper.getAnnotation(propertyElement, XmlAttribute.class)).required()); } if (helper.isAnnotationPresent(propertyElement, XmlAnyAttribute.class)) { if (info.isSetAnyAttributePropertyName() && !info.getAnyAttributePropertyName().equals(property.getPropertyName())) { throw org.eclipse.persistence.exceptions.JAXBException.multipleAnyAttributeMapping(cls.getName()); } if (!helper.isMapType(property.getType())) { throw org.eclipse.persistence.exceptions.JAXBException.anyAttributeOnNonMap(property.getPropertyName()); } property.setIsAnyAttribute(true); info.setAnyAttributePropertyName(property.getPropertyName()); } // Make sure XmlElementWrapper annotation is on a collection or array if (helper.isAnnotationPresent(propertyElement, XmlElementWrapper.class)) { XmlElementWrapper wrapper = (XmlElementWrapper) helper.getAnnotation(propertyElement, XmlElementWrapper.class); org.eclipse.persistence.jaxb.xmlmodel.XmlElementWrapper xmlEltWrapper = new org.eclipse.persistence.jaxb.xmlmodel.XmlElementWrapper(); String wrapperName = wrapper.name(); if (wrapperName.equals(XMLProcessor.DEFAULT)) { wrapperName = info.getXmlNameTransformer().transformElementName(property.getPropertyName()); } xmlEltWrapper.setName(wrapperName); xmlEltWrapper.setNamespace(wrapper.namespace()); xmlEltWrapper.setNillable(wrapper.nillable()); xmlEltWrapper.setRequired(wrapper.required()); property.setXmlElementWrapper(xmlEltWrapper); } if (helper.isAnnotationPresent(propertyElement, XmlList.class)) { // Make sure XmlList annotation is on a collection or array if (!helper.isCollectionType(property.getType()) && !property.getType().isArray()) { throw JAXBException.invalidList(property.getPropertyName()); } property.setIsXmlList(true); } if (helper.isAnnotationPresent(propertyElement, XmlValue.class)) { property.setIsXmlValue(true); info.setXmlValueProperty(property); } if (helper.isAnnotationPresent(propertyElement, XmlReadOnly.class)) { property.setReadOnly(true); } if (helper.isAnnotationPresent(propertyElement, XmlWriteOnly.class)) { property.setWriteOnly(true); } if (helper.isAnnotationPresent(propertyElement, XmlCDATA.class)) { property.setCdata(true); } if (helper.isAnnotationPresent(propertyElement, XmlAccessMethods.class)) { XmlAccessMethods accessMethods = (XmlAccessMethods) helper.getAnnotation(propertyElement, XmlAccessMethods.class); if (!(accessMethods.getMethodName().equals(EMPTY_STRING))) { property.setGetMethodName(accessMethods.getMethodName()); } if (!(accessMethods.setMethodName().equals(EMPTY_STRING))) { property.setSetMethodName(accessMethods.setMethodName()); } if (!(property.isMethodProperty())) { property.setMethodProperty(true); } } // handle user properties if (helper.isAnnotationPresent(propertyElement, XmlProperties.class)) { XmlProperties xmlProperties = (XmlProperties) helper.getAnnotation(propertyElement, XmlProperties.class); Map<Object, Object> propertiesMap = createUserPropertiesMap(xmlProperties.value()); property.setUserProperties(propertiesMap); } else if (helper.isAnnotationPresent(propertyElement, XmlProperty.class)) { XmlProperty xmlProperty = (XmlProperty) helper.getAnnotation(propertyElement, XmlProperty.class); Map<Object, Object> propertiesMap = createUserPropertiesMap(new XmlProperty[] { xmlProperty }); property.setUserProperties(propertiesMap); } // handle XmlKey if (helper.isAnnotationPresent(propertyElement, XmlKey.class)) { info.addXmlKeyProperty(property); } // handle XmlJoinNode(s) processXmlJoinNodes(property); processXmlNullPolicy(property, cls, info); // Handle XmlLocation JavaHasAnnotations elem = propertyElement; if (helper.isAnnotationPresent(elem, XmlLocation.class) || helper.isAnnotationPresent(elem, CompilerHelper.XML_LOCATION_ANNOTATION_CLASS) || helper.isAnnotationPresent(elem, CompilerHelper.INTERNAL_XML_LOCATION_ANNOTATION_CLASS)) { if (!helper.getJavaClass(Constants.LOCATOR_CLASS).isAssignableFrom(property.getType())) { throw JAXBException.invalidXmlLocation(property.getPropertyName(), property.getType().getName()); } property.setXmlLocation(true); } } /** * Process XmlJoinNode(s) for a given Property. An * org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNode(s) will be * created/populated using the annotation, and set on the Property for later * processing. * * It is assumed that for a single join node XmlJoinNode will be used, and * for multiple join nodes XmlJoinNodes will be used. * * @param property * Property that may contain @XmlJoinNodes/@XmlJoinNode */ private void processXmlJoinNodes(Property property) { List<org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode> xmlJoinNodeList; org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode xmlJoinNode; org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes xmlJoinNodes; // handle XmlJoinNodes if (helper.isAnnotationPresent(property.getElement(), XmlJoinNodes.class)) { xmlJoinNodeList = new ArrayList<org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode>(); for (XmlJoinNode xmlJN : ((XmlJoinNodes) helper.getAnnotation(property.getElement(), XmlJoinNodes.class)).value()) { xmlJoinNode = new org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode(); xmlJoinNode.setXmlPath(xmlJN.xmlPath()); xmlJoinNode.setReferencedXmlPath(xmlJN.referencedXmlPath()); xmlJoinNodeList.add(xmlJoinNode); } xmlJoinNodes = new org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes(); xmlJoinNodes.setXmlJoinNode(xmlJoinNodeList); property.setXmlJoinNodes(xmlJoinNodes); } // handle XmlJoinNode else if (helper.isAnnotationPresent(property.getElement(), XmlJoinNode.class)) { XmlJoinNode xmlJN = (XmlJoinNode) helper.getAnnotation(property.getElement(), XmlJoinNode.class); xmlJoinNode = new org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode(); xmlJoinNode.setXmlPath(xmlJN.xmlPath()); xmlJoinNode.setReferencedXmlPath(xmlJN.referencedXmlPath()); xmlJoinNodeList = new ArrayList<org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode>(); xmlJoinNodeList.add(xmlJoinNode); xmlJoinNodes = new org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes(); xmlJoinNodes.setXmlJoinNode(xmlJoinNodeList); property.setXmlJoinNodes(xmlJoinNodes); } } /** * Responsible for validating transformer settings on a given property. * Validates that for field transformers either a transformer class OR * method name is set (not both) and that an xml-path is set. Validates that * for attribute transformers either a transformer class OR method name is * set (not both). * * @param property */ private void validateXmlTransformationProperty(Property property) { if (property.isSetXmlTransformation()) { XmlTransformation xmlTransformation = property.getXmlTransformation(); // validate transformer(s) if (xmlTransformation.isSetXmlReadTransformer()) { // validate read transformer XmlReadTransformer readTransformer = xmlTransformation.getXmlReadTransformer(); if (readTransformer.isSetTransformerClass()) { // handle read transformer class if (readTransformer.isSetMethod()) { // cannot have both class and method set throw JAXBException.readTransformerHasBothClassAndMethod(property.getPropertyName()); } } else { // handle read transformer method if (!readTransformer.isSetMethod()) { // require class or method to be set throw JAXBException.readTransformerHasNeitherClassNorMethod(property.getPropertyName()); } } } if (xmlTransformation.isSetXmlWriteTransformers()) { // handle write transformer(s) for (XmlWriteTransformer writeTransformer : xmlTransformation.getXmlWriteTransformer()) { // must have an xml-path set if (!writeTransformer.isSetXmlPath()) { throw JAXBException.writeTransformerHasNoXmlPath(property.getPropertyName()); } if (writeTransformer.isSetTransformerClass()) { // handle write transformer class if (writeTransformer.isSetMethod()) { // cannot have both class and method set throw JAXBException.writeTransformerHasBothClassAndMethod(property.getPropertyName(), writeTransformer.getXmlPath()); } } else { // handle write transformer method if (!writeTransformer.isSetMethod()) { // require class or method to be set throw JAXBException.writeTransformerHasNeitherClassNorMethod(property.getPropertyName(), writeTransformer.getXmlPath()); } } } } } } /** * Compares a JavaModel JavaClass to a Class. Equality is based on the raw * name of the JavaClass compared to the canonical name of the Class. * * @param src * @param tgt * @return */ protected boolean areEquals(JavaClass src, Class tgt) { if (src == null || tgt == null) { return false; } return src.getRawName().equals(tgt.getCanonicalName()); } private void processXmlNullPolicy(Property property, JavaClass cls, TypeInfo info) { if (propertyHasXmlNullPolicyAnnotation(property)) { setNullPolicyOnProperty(property, helper.getAnnotation(property.getElement(), XmlNullPolicy.class)); } else if (existsExternalMappingWithJavaTypeXmlNullPolicy(info)) { property.setNullPolicy(info.getXmlNullPolicy()); } else if (javaTypeHasXmlNullPolicyAnnotation(cls)) { setNullPolicyOnProperty(property, helper.getAnnotation(cls, XmlNullPolicy.class)); } else if (existsExternaMappingWithPackageXmlNullPolicy(cls)) { property.setNullPolicy(packageToXmlNillableInfoMappings.get(cls.getPackageName()).getXmlNullPolicy()); } else if (helper.isAnnotationPresent(cls.getPackage(), XmlNullPolicy.class)) { setNullPolicyOnProperty(property, helper.getAnnotation(cls.getPackage(), XmlNullPolicy.class)); } else if (helper.isAnnotationPresent(property.getElement(), XmlIsSetNullPolicy.class)) { XmlIsSetNullPolicy nullPolicy = (XmlIsSetNullPolicy) helper.getAnnotation(property.getElement(), XmlIsSetNullPolicy.class); org.eclipse.persistence.jaxb.xmlmodel.XmlIsSetNullPolicy policy = new org.eclipse.persistence.jaxb.xmlmodel.XmlIsSetNullPolicy(); policy.setEmptyNodeRepresentsNull(nullPolicy.emptyNodeRepresentsNull()); policy.setXsiNilRepresentsNull(nullPolicy.xsiNilRepresentsNull()); policy.setNullRepresentationForXml(org.eclipse.persistence.jaxb.xmlmodel.XmlMarshalNullRepresentation.valueOf(nullPolicy.nullRepresentationForXml().toString())); policy.setIsSetMethodName(nullPolicy.isSetMethodName()); for (XmlParameter next : nullPolicy.isSetParameters()) { org.eclipse.persistence.jaxb.xmlmodel.XmlIsSetNullPolicy.IsSetParameter param = new org.eclipse.persistence.jaxb.xmlmodel.XmlIsSetNullPolicy.IsSetParameter(); param.setValue(next.value()); param.setType(next.type().getName()); policy.getIsSetParameter().add(param); } property.setNullPolicy(policy); } } private boolean existsExternaMappingWithPackageXmlNullPolicy(JavaClass cls) { if (null == packageToXmlNillableInfoMappings || !packageToXmlNillableInfoMappings.containsKey(cls.getPackageName())) { return false; } return null != packageToXmlNillableInfoMappings.get(cls.getPackageName()).getXmlNullPolicy(); } private boolean javaTypeHasXmlNullPolicyAnnotation(JavaClass cls) { return helper.isAnnotationPresent(cls, XmlNullPolicy.class); } private boolean existsExternalMappingWithJavaTypeXmlNullPolicy(TypeInfo info) { return null != info.getXmlNullPolicy(); } private boolean propertyHasXmlNullPolicyAnnotation(Property property) { return helper.isAnnotationPresent(property.getElement(), XmlNullPolicy.class); } private void setNullPolicyOnProperty(Property property, Annotation nullPolicyAnnotation) { XmlNullPolicy nullPolicy = (XmlNullPolicy) nullPolicyAnnotation; org.eclipse.persistence.jaxb.xmlmodel.XmlNullPolicy policy = new org.eclipse.persistence.jaxb.xmlmodel.XmlNullPolicy(); policy.setEmptyNodeRepresentsNull(nullPolicy.emptyNodeRepresentsNull()); policy.setIsSetPerformedForAbsentNode(nullPolicy.isSetPerformedForAbsentNode()); policy.setXsiNilRepresentsNull(Boolean.valueOf(nullPolicy.xsiNilRepresentsNull())); policy.setNullRepresentationForXml(org.eclipse.persistence.jaxb.xmlmodel.XmlMarshalNullRepresentation.valueOf(nullPolicy.nullRepresentationForXml().toString())); property.setNullPolicy(policy); } /** * Compares a JavaModel JavaClass to a Class. Equality is based on the raw * name of the JavaClass compared to the canonical name of the Class. * * @param src * @param tgtCanonicalName * @return */ protected boolean areEquals(JavaClass src, String tgtCanonicalName) { if (src == null || tgtCanonicalName == null) { return false; } return src.getRawName().equals(tgtCanonicalName); } public ArrayList<Property> getPropertyPropertiesForClass(JavaClass cls, TypeInfo info, boolean onlyPublic) { return getPropertyPropertiesForClass(cls, info, onlyPublic, false); } public ArrayList<Property> getPropertyPropertiesForClass(JavaClass cls, TypeInfo info, boolean onlyPublic, boolean onlyExplicit) { ArrayList<Property> properties = new ArrayList<Property>(); if (cls == null) { return properties; } // First collect all the getters and setters ArrayList<JavaMethod> propertyMethods = new ArrayList<JavaMethod>(); for (JavaMethod next : new ArrayList<JavaMethod>(cls.getDeclaredMethods())) { if(!next.isSynthetic()){ if (((next.getName().startsWith(GET_STR) && next.getName().length() > 3) || (next.getName().startsWith(IS_STR) && next.getName().length() > 2)) && next.getParameterTypes().length == 0 && next.getReturnType() != helper.getJavaClass(java.lang.Void.class)) { int modifiers = next.getModifiers(); if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers) && ((onlyPublic && Modifier.isPublic(next.getModifiers())) || !onlyPublic || hasJAXBAnnotations(next))) { propertyMethods.add(next); } } else if (next.getName().startsWith(SET_STR) && next.getName().length() > 3 && next.getParameterTypes().length == 1) { int modifiers = next.getModifiers(); if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers) && ((onlyPublic && Modifier.isPublic(next.getModifiers())) || !onlyPublic || hasJAXBAnnotations(next))) { propertyMethods.add(next); } } } } // Next iterate over the getters and find their setter methods, add // whichever one is // annotated to the properties list. If neither is, use the getter // keep track of property names to avoid processing the same property // twice (for getter and setter) List<String> propertyNames = new ArrayList<String>(); for (JavaMethod propertyMethod1 : propertyMethods) { boolean isPropertyTransient = false; JavaMethod nextMethod = propertyMethod1; String propertyName = EMPTY_STRING; JavaMethod getMethod; JavaMethod setMethod; JavaMethod propertyMethod = null; if (!nextMethod.getName().startsWith(SET_STR)) { if (nextMethod.getName().startsWith(GET_STR)) { propertyName = nextMethod.getName().substring(3); } else if (nextMethod.getName().startsWith(IS_STR)) { propertyName = nextMethod.getName().substring(2); } getMethod = nextMethod; String setMethodName = SET_STR + propertyName; // use the JavaBean API to correctly decapitalize the first // character, if necessary propertyName = Introspector.decapitalize(propertyName); JavaClass[] paramTypes = { getMethod.getReturnType() }; setMethod = cls.getDeclaredMethod(setMethodName, paramTypes); if (setMethod == null) { //if there's no locally declared set method, check for an inherited //set method setMethod = cls.getMethod(setMethodName, paramTypes); } if (setMethod == null && !(hasJAXBAnnotations(getMethod))) { //if there's no corresponding setter, and not explicitly //annotated, don't process isPropertyTransient = true; } if (setMethod != null && hasJAXBAnnotations(setMethod)) { // use the set method if it exists and is annotated boolean isTransient = helper.isAnnotationPresent(setMethod, XmlTransient.class); boolean isLocation = helper.isAnnotationPresent(setMethod, XmlLocation.class) || helper.isAnnotationPresent(setMethod, CompilerHelper.XML_LOCATION_ANNOTATION_CLASS) || helper.isAnnotationPresent(setMethod, CompilerHelper.INTERNAL_XML_LOCATION_ANNOTATION_CLASS); propertyMethod = setMethod; if (isTransient) { isPropertyTransient = true; // XmlLocation can also be transient if (isLocation) { info.setLocationAware(true); } } } else if ((onlyExplicit && hasJAXBAnnotations(getMethod)) || !onlyExplicit) { boolean isTransient = helper.isAnnotationPresent(getMethod, XmlTransient.class); boolean isLocation = helper.isAnnotationPresent(getMethod, XmlLocation.class) || helper.isAnnotationPresent(setMethod, CompilerHelper.XML_LOCATION_ANNOTATION_CLASS) || helper.isAnnotationPresent(setMethod, CompilerHelper.INTERNAL_XML_LOCATION_ANNOTATION_CLASS); propertyMethod = getMethod; if (isTransient) { isPropertyTransient = true; // XmlLocation can also be transient if (isLocation) { info.setLocationAware(true); } } } else if (onlyExplicit) { continue; } } else { propertyName = nextMethod.getName().substring(3); setMethod = nextMethod; String getMethodName = GET_STR + propertyName; getMethod = cls.getDeclaredMethod(getMethodName, new JavaClass[] { }); if (getMethod == null) { // try is instead of get getMethodName = IS_STR + propertyName; getMethod = cls.getDeclaredMethod(getMethodName, new JavaClass[] { }); } //may look for get method on parent class if (getMethod == null) { //look for inherited getMethod getMethod = cls.getMethod(GET_STR + propertyName, new JavaClass[] { }); if (getMethod == null) { getMethod = cls.getMethod(IS_STR + propertyName, new JavaClass[] { }); } } if (getMethod == null && !(hasJAXBAnnotations(setMethod))) { isPropertyTransient = true; } if (getMethod != null && hasJAXBAnnotations(getMethod)) { // use the get method if it exists and is annotated boolean isTransient = helper.isAnnotationPresent(getMethod, XmlTransient.class); boolean isLocation = helper.isAnnotationPresent(getMethod, XmlLocation.class) || helper.isAnnotationPresent(getMethod, CompilerHelper.XML_LOCATION_ANNOTATION_CLASS) || helper.isAnnotationPresent(getMethod, CompilerHelper.INTERNAL_XML_LOCATION_ANNOTATION_CLASS); propertyMethod = getMethod; if (isTransient) { isPropertyTransient = true; // XmlLocation can also be transient if (isLocation) { info.setLocationAware(true); } } } else if ((onlyExplicit && hasJAXBAnnotations(setMethod)) || !onlyExplicit) { boolean isTransient = helper.isAnnotationPresent(setMethod, XmlTransient.class); boolean isLocation = helper.isAnnotationPresent(setMethod, XmlLocation.class) || helper.isAnnotationPresent(getMethod, CompilerHelper.XML_LOCATION_ANNOTATION_CLASS) || helper.isAnnotationPresent(getMethod, CompilerHelper.INTERNAL_XML_LOCATION_ANNOTATION_CLASS); propertyMethod = setMethod; if (isTransient) { isPropertyTransient = true; // XmlLocation can also be transient if (isLocation) { info.setLocationAware(true); } } } else if (onlyExplicit) { continue; } // use the JavaBean API to correctly decapitalize the first // character, if necessary propertyName = Introspector.decapitalize(propertyName); } JavaClass ptype = null; if (getMethod != null) { ptype = getMethod.getReturnType(); } else { ptype = setMethod.getParameterTypes()[0]; } if (!propertyNames.contains(propertyName)) { try { Property property = buildNewProperty(info, cls, propertyMethod, propertyName, ptype); propertyNames.add(propertyName); property.setTransient(isPropertyTransient); if (getMethod != null) { property.setOriginalGetMethodName(getMethod.getName()); if (property.getGetMethodName() == null) { property.setGetMethodName(getMethod.getName()); } } if (setMethod != null) { property.setOriginalSetMethodName(setMethod.getName()); if (property.getSetMethodName() == null) { property.setSetMethodName(setMethod.getName()); } } property.setMethodProperty(true); //boolean isTransient = helper.isAnnotationPresent(property.getElement(), XmlTransient.class); //boolean isLocation = helper.isAnnotationPresent(property.getElement(), XmlLocation.class) || // helper.isAnnotationPresent(setMethod, CompilerHelper.XML_LOCATION_ANNOTATION_CLASS) || // helper.isAnnotationPresent(setMethod, CompilerHelper.INTERNAL_XML_LOCATION_ANNOTATION_CLASS); //if (!isTransient || (isTransient && isLocation)) { properties.add(property); //} } catch (JAXBException ex) { if (ex.getErrorCode() != JAXBException.INVALID_INTERFACE || !isPropertyTransient) { throw ex; } } } } properties = removeSuperclassProperties(cls, properties); // default to alphabetical ordering // RI compliancy Collections.sort(properties, new PropertyComparitor()); return properties; } private ArrayList<Property> removeSuperclassProperties(JavaClass cls, ArrayList<Property> properties) { ArrayList<Property> revisedProperties = new ArrayList<Property>(); revisedProperties.addAll(properties); // Check for any get() methods that are overridden in the subclass. // If we find any, remove the property, because it is already defined on // the superclass. JavaClass superClass = cls.getSuperclass(); if (null != superClass) { TypeInfo superClassInfo = typeInfos.get(superClass.getQualifiedName()); if (superClassInfo != null && !superClassInfo.isTransient()) { for (Property prop : properties) { for (Property superProp : superClassInfo.getProperties().values()) { if (superProp.getGetMethodName() != null && superProp.getGetMethodName().equals(prop.getGetMethodName()) && !superProp.isTransient()) { revisedProperties.remove(prop); } } } } } return revisedProperties; } public ArrayList getPublicMemberPropertiesForClass(JavaClass cls, TypeInfo info) { ArrayList<Property> fieldProperties = getFieldPropertiesForClass(cls, info, !hasXmlBindings()); ArrayList<Property> methodProperties = getPropertyPropertiesForClass(cls, info, !hasXmlBindings()); // filter out non-public properties that aren't annotated ArrayList<Property> publicFieldProperties = new ArrayList<Property>(); ArrayList<Property> publicMethodProperties = new ArrayList<Property>(); for (Property next : fieldProperties) { if (Modifier.isPublic(((JavaField) next.getElement()).getModifiers())) { publicFieldProperties.add(next); } else { if (hasJAXBAnnotations(next.getElement())) { publicFieldProperties.add(next); } } } for (Property next : methodProperties) { if (next.getElement() != null) { if (Modifier.isPublic(((JavaMethod) next.getElement()).getModifiers())) { publicMethodProperties.add(next); } else { if (hasJAXBAnnotations(next.getElement())) { publicMethodProperties.add(next); } } } } // Not sure who should win if a property exists for both or the correct // order if (publicFieldProperties.size() >= 0 && publicMethodProperties.size() == 0) { return publicFieldProperties; } else if (publicMethodProperties.size() > 0 && publicFieldProperties.size() == 0) { return publicMethodProperties; } else { // add any non-duplicate method properties to the collection. // - in the case of a collision if one is annotated use it, // otherwise // use the field. HashMap<String, Property> fieldPropertyMap = getPropertyMapFromArrayList(publicFieldProperties); for (Property publicMethodProperty : publicMethodProperties) { Property next = publicMethodProperty; Property fieldProp = fieldPropertyMap.get(next.getPropertyName()); if (fieldProp == null) { publicFieldProperties.add(next); } else if (fieldProp.isTransient()) { //bug 346461 - if a public field is transient and the public methods are not // then use the method publicFieldProperties.remove(fieldProp); publicFieldProperties.add(next); } } return publicFieldProperties; } } public HashMap<String, Property> getPropertyMapFromArrayList(ArrayList<Property> props) { HashMap propMap = new HashMap(props.size()); for (Object next : props) { propMap.put(((Property)next).getPropertyName(), next); } return propMap; } public ArrayList getNoAccessTypePropertiesForClass(JavaClass cls, TypeInfo info) { ArrayList<Property> list = new ArrayList<Property>(); if (cls == null) { return list; } // Iterate over the field and method properties. If ANYTHING contains an // annotation and // doesn't appear in the other list, add it to the final list List<Property> fieldProperties = getFieldPropertiesForClass(cls, info, false); Map<String, Property> fields = new HashMap<String, Property>(fieldProperties.size()); for (Property next : fieldProperties) { JavaHasAnnotations elem = next.getElement(); if (!hasJAXBAnnotations(elem)) { next.setTransient(true); } list.add(next); fields.put(next.getPropertyName(), next); } List<Property> methodProperties = getPropertyPropertiesForClass(cls, info, false); for (Property next : methodProperties) { JavaHasAnnotations elem = next.getElement(); if (hasJAXBAnnotations(elem)) { // If the property is annotated remove the corresponding field Property fieldProperty = fields.get(next.getPropertyName()); list.remove(fieldProperty); list.add(next); } else { // If the property is not annotated only add it if there is no // corresponding field. next.setTransient(true); if (fields.get(next.getPropertyName()) == null) { list.add(next); } } } return list; } /** * Use name, namespace and type information to setup a user-defined schema * type. This method will typically be called when processing an * * {@literal @XmlSchemaType(s)} annotation or xml-schema-type(s) metadata. * * @param name * @param namespace * @param jClassQualifiedName */ public void processSchemaType(String name, String namespace, String jClassQualifiedName) { this.userDefinedSchemaTypes.put(jClassQualifiedName, new QName(namespace, name)); } public void processSchemaType(XmlSchemaType type) { JavaClass jClass = helper.getJavaClass(type.type()); if (jClass == null) { return; } processSchemaType(type.name(), type.namespace(), jClass.getQualifiedName()); } public void addEnumTypeInfo(JavaClass javaClass, EnumTypeInfo info) { if (javaClass == null) { return; } info.setClassName(javaClass.getQualifiedName()); Class restrictionClass = String.class; QName restrictionBase = getSchemaTypeFor(helper.getJavaClass(restrictionClass)); if (helper.isAnnotationPresent(javaClass, XmlEnum.class)) { XmlEnum xmlEnum = (XmlEnum) helper.getAnnotation(javaClass, XmlEnum.class); restrictionClass = xmlEnum.value(); JavaClass restrictionJavaClass= helper.getJavaClass(restrictionClass); boolean restrictionIsEnum = helper.isAnnotationPresent(restrictionJavaClass, XmlEnum.class); if(!restrictionIsEnum){ if(helper.isBuiltInJavaType(restrictionJavaClass)){ restrictionBase = getSchemaTypeFor(helper.getJavaClass(restrictionClass)); }else{ TypeInfo restrictionInfo = typeInfos.get(restrictionJavaClass.getQualifiedName()); if(restrictionInfo == null){ JavaClass[] jClasses = new JavaClass[] { restrictionJavaClass }; buildNewTypeInfo(jClasses); restrictionInfo = typeInfos.get(restrictionJavaClass.getQualifiedName()); }else if(!restrictionInfo.isPostBuilt()){ postBuildTypeInfo(new JavaClass[] { restrictionJavaClass }); } Property xmlValueProp = restrictionInfo.getXmlValueProperty(); if(xmlValueProp != null){ restrictionJavaClass = xmlValueProp.getActualType(); restrictionBase = getSchemaTypeFor(restrictionJavaClass); restrictionClass = helper.getClassForJavaClass(restrictionJavaClass); } } }else{ while (restrictionIsEnum) { TypeInfo restrictionTypeInfo = processReferencedClass(restrictionJavaClass); restrictionBase = new QName(restrictionTypeInfo.getClassNamespace(), restrictionTypeInfo.getSchemaTypeName()); xmlEnum = (XmlEnum) helper.getAnnotation(restrictionJavaClass, XmlEnum.class); restrictionClass = xmlEnum.value(); restrictionJavaClass= helper.getJavaClass(restrictionClass); restrictionIsEnum = helper.isAnnotationPresent(restrictionJavaClass, XmlEnum.class); } } } info.setRestrictionBase(restrictionBase); for (JavaField field : (Iterable<JavaField>) javaClass.getDeclaredFields()) { if (field.isEnumConstant()) { Object enumValue = field.getName(); if (helper.isAnnotationPresent(field, XmlEnumValue.class)) { enumValue = ((XmlEnumValue) helper.getAnnotation(field, XmlEnumValue.class)).value(); } if (restrictionClass != null) { try { enumValue = XMLConversionManager.getDefaultXMLManager().convertObject(enumValue, restrictionClass); } catch (ConversionException e) { throw JAXBException.invalidEnumValue(enumValue, restrictionClass.getName(), e); } } info.addJavaFieldToXmlEnumValuePair(field.getName(), enumValue); } } // Add a non-named element declaration for each enumeration to trigger // class generation if(info.getXmlRootElement() == null) { //process the root element and use that as the element ElementDeclaration elem = new ElementDeclaration(null, javaClass, javaClass.getQualifiedName(), false); this.getLocalElements().add(elem); } } public QName getSchemaTypeOrNullFor(JavaClass javaClass) { if (javaClass == null) { return null; } // check user defined types first QName schemaType = userDefinedSchemaTypes.get(javaClass.getQualifiedName()); if (schemaType == null) { schemaType = (QName) helper.getXMLToJavaTypeMap().get(javaClass.getRawName()); } return schemaType; } public QName getSchemaTypeFor(JavaClass javaClass) { QName schemaType = getSchemaTypeOrNullFor(javaClass); if (schemaType == null) { return Constants.ANY_SIMPLE_TYPE_QNAME; } return schemaType; } public NamespaceInfo processNamespaceInformation(XmlSchema xmlSchema) { NamespaceInfo info = new NamespaceInfo(); info.setNamespaceResolver(new NamespaceResolver()); String packageNamespace = null; if (xmlSchema != null) { String namespaceMapping = xmlSchema.namespace(); if (!(namespaceMapping.equals(EMPTY_STRING) || namespaceMapping.equals(XMLProcessor.DEFAULT))) { packageNamespace = namespaceMapping; } else if (namespaceMapping.equals(XMLProcessor.DEFAULT)) { packageNamespace = this.defaultTargetNamespace; } info.setNamespace(packageNamespace); XmlNs[] xmlns = xmlSchema.xmlns(); for (XmlNs next : xmlns) { info.getNamespaceResolver().put(next.prefix(), next.namespaceURI()); } info.setAttributeFormQualified(xmlSchema.attributeFormDefault() == XmlNsForm.QUALIFIED); info.setElementFormQualified(xmlSchema.elementFormDefault() == XmlNsForm.QUALIFIED); // reflectively load XmlSchema class to avoid dependency try { Method locationMethod = PrivilegedAccessHelper.getDeclaredMethod(XmlSchema.class, "location", new Class[] {}); String location = (String) PrivilegedAccessHelper.invokeMethod(locationMethod, xmlSchema, new Object[] {}); if (location != null) { if (location.equals("##generate")) { location = null; } else if (location.equals(EMPTY_STRING)) { location = null; } } info.setLocation(location); } catch (Exception ignored) { // ignored } } else { info.setNamespace(defaultTargetNamespace); } if (!info.isElementFormQualified() ){ isDefaultNamespaceAllowed = false; } return info; } public Map<String, TypeInfo> getTypeInfos() { return typeInfos; } public List<JavaClass> getTypeInfoClasses() { return typeInfoClasses; } public Map<String, QName> getUserDefinedSchemaTypes() { return userDefinedSchemaTypes; } public QName getQNameForProperty(Property property, String defaultName, JavaHasAnnotations element, NamespaceInfo namespaceInfo, TypeInfo info) { String uri = info.getClassNamespace(); String name = XMLProcessor.DEFAULT; String namespace = XMLProcessor.DEFAULT; QName qName = null; if(property.isMap()){ isDefaultNamespaceAllowed = false; } if (helper.isAnnotationPresent(element, XmlAttribute.class)) { XmlAttribute xmlAttribute = (XmlAttribute) helper.getAnnotation(element, XmlAttribute.class); name = xmlAttribute.name(); namespace = xmlAttribute.namespace(); if (name.equals(XMLProcessor.DEFAULT)) { name = defaultName; try { name = info.getXmlNameTransformer().transformAttributeName(name); } catch (Exception ex) { throw org.eclipse.persistence.exceptions.JAXBException.exceptionDuringNameTransformation(name, info.getXmlNameTransformer().getClass().getName(), ex); } } if (!namespace.equals(XMLProcessor.DEFAULT)) { qName = new QName(namespace, name); isDefaultNamespaceAllowed = false; } else { if (namespaceInfo.isAttributeFormQualified()) { qName = new QName(uri, name); isDefaultNamespaceAllowed = false; } else { qName = new QName(name); } } } else { if (helper.isAnnotationPresent(element, XmlElement.class)) { XmlElement xmlElement = (XmlElement) helper.getAnnotation(element, XmlElement.class); name = xmlElement.name(); namespace = xmlElement.namespace(); } if (property.isMap() && helper.isAnnotationPresent(element, XmlElementWrapper.class)) { XmlElementWrapper xmlElementWrapper = (XmlElementWrapper) helper.getAnnotation(element, XmlElementWrapper.class); name = xmlElementWrapper.name(); namespace = xmlElementWrapper.namespace(); } if (name.equals(XMLProcessor.DEFAULT)) { name = defaultName; try { name = info.getXmlNameTransformer().transformElementName(name); } catch (Exception ex) { throw org.eclipse.persistence.exceptions.JAXBException.exceptionDuringNameTransformation(name, info.getXmlNameTransformer().getClass().getName(), ex); } } if (!namespace.equals(XMLProcessor.DEFAULT)) { qName = new QName(namespace, name); if (namespace.equals(Constants.EMPTY_STRING)) { isDefaultNamespaceAllowed = false; } } else { if (namespaceInfo.isElementFormQualified()) { qName = new QName(uri, name); } else { qName = new QName(name); } } } return qName; } public Map<String, PackageInfo> getPackageToPackageInfoMappings() { return packageToPackageInfoMappings; } /** * Add a package name/NamespaceInfo entry to the map. This method will * lazy-load the map if necessary. */ public void addPackageToNamespaceMapping(String packageName, NamespaceInfo nsInfo) { PackageInfo info = getPackageInfoWithLazyInit(packageName); info.setNamespaceInfo(nsInfo); } /** * Add a package name/XmlElementNillable entry to the map. This method will * lazy-load the map if necessary. */ public void addPackageToXmlElementNillable(String packageName, org.eclipse.persistence.jaxb.xmlmodel.XmlElementNillable xmlElementNillable) { XmlNillableInfo info = getXmlNillableInfoWithLazyInit(packageName); info.setXmlElementNillable(xmlElementNillable); } /** * Add a package name/XmlNullPolicy entry to the map. This method will * lazy-load the map if necessary. */ public void addPackageToXmlNullPolicy(String packageName, org.eclipse.persistence.jaxb.xmlmodel.XmlNullPolicy xmlNullPolicy) { XmlNillableInfo info = getXmlNillableInfoWithLazyInit(packageName); info.setXmlNullPolicy(xmlNullPolicy); } private XmlNillableInfo getXmlNillableInfoWithLazyInit(String packageName) { if (packageToXmlNillableInfoMappings == null) { packageToXmlNillableInfoMappings = new HashMap<String, XmlNillableInfo>(); } XmlNillableInfo info = packageToXmlNillableInfoMappings.get(packageName); if (info == null) { info = new XmlNillableInfo(); packageToXmlNillableInfoMappings.put(packageName, info); } return info; } private PackageInfo getPackageInfoWithLazyInit(String packageName) { if (packageToPackageInfoMappings == null) { packageToPackageInfoMappings = new HashMap<String, PackageInfo>(); } PackageInfo info = packageToPackageInfoMappings.get(packageName); if (info == null) { info = new PackageInfo(); packageToPackageInfoMappings.put(packageName, info); } return info; } public void addPackageToPackageInfoMapping(String packageName, PackageInfo packageInfo) { if(packageToPackageInfoMappings == null) { packageToPackageInfoMappings = new HashMap<String, PackageInfo>(); } packageToPackageInfoMappings.put(packageName, packageInfo); } public PackageInfo getPackageInfoForPackage(JavaClass javaClass) { String packageName = javaClass.getPackageName(); PackageInfo packageInfo = packageToPackageInfoMappings.get(packageName); if (packageInfo == null) { packageInfo = getPackageInfoForPackage(javaClass.getPackage(), packageName); } return packageInfo; } public PackageInfo getPackageInfoForPackage(JavaPackage pack, String packageName) { PackageInfo packageInfo = packageToPackageInfoMappings.get(packageName); if (packageInfo == null) { XmlSchema xmlSchema = (XmlSchema) helper.getAnnotation(pack, XmlSchema.class); packageInfo = new PackageInfo(); NamespaceInfo namespaceInfo = processNamespaceInformation(xmlSchema); packageInfo.setNamespaceInfo(namespaceInfo); // if it's still null, generate based on package name if (namespaceInfo.getNamespace() == null) { namespaceInfo.setNamespace(EMPTY_STRING); } if (helper.isAnnotationPresent(pack, XmlAccessorType.class)) { XmlAccessorType xmlAccessorType = (XmlAccessorType) helper.getAnnotation(pack, XmlAccessorType.class); packageInfo.setAccessType(XmlAccessType.fromValue(xmlAccessorType.value().name())); } if (helper.isAnnotationPresent(pack, XmlAccessorOrder.class)) { XmlAccessorOrder xmlAccessorOrder = (XmlAccessorOrder) helper.getAnnotation(pack, XmlAccessorOrder.class); packageInfo.setAccessOrder(XmlAccessOrder.fromValue(xmlAccessorOrder.value().name())); } if (CompilerHelper.ACCESSOR_FACTORY_ANNOTATION_CLASS != null && helper.isAnnotationPresent(pack, CompilerHelper.ACCESSOR_FACTORY_ANNOTATION_CLASS)) { Annotation xmlAccessorFactory = helper.getAnnotation(pack, CompilerHelper.ACCESSOR_FACTORY_ANNOTATION_CLASS); Class xmlAccessorFactoryClass = null; try { xmlAccessorFactoryClass = (Class)PrivilegedAccessHelper.invokeMethod(CompilerHelper.ACCESSOR_FACTORY_VALUE_METHOD, xmlAccessorFactory, new Object[]{}); packageInfo.setAccessorFactory(new AccessorFactoryWrapper(PrivilegedAccessHelper.newInstanceFromClass(xmlAccessorFactoryClass))); } catch (Exception ex) { throw JAXBException.errorInstantiatingAccessorFactory(xmlAccessorFactoryClass, ex); } }else if (CompilerHelper.INTERNAL_ACCESSOR_FACTORY_ANNOTATION_CLASS != null && helper.isAnnotationPresent(pack, CompilerHelper.INTERNAL_ACCESSOR_FACTORY_ANNOTATION_CLASS)) { Annotation xmlAccessorFactory = helper.getAnnotation(pack, CompilerHelper.INTERNAL_ACCESSOR_FACTORY_ANNOTATION_CLASS); Class xmlAccessorFactoryClass = null; try { xmlAccessorFactoryClass = (Class)PrivilegedAccessHelper.invokeMethod(CompilerHelper.INTERNAL_ACCESSOR_FACTORY_VALUE_METHOD, xmlAccessorFactory, new Object[]{}); packageInfo.setAccessorFactory(new AccessorFactoryWrapper(PrivilegedAccessHelper.newInstanceFromClass(xmlAccessorFactoryClass))); } catch (Exception ex) { throw JAXBException.errorInstantiatingAccessorFactory(xmlAccessorFactoryClass, ex); } } packageToPackageInfoMappings.put(packageName, packageInfo); } return packageInfo; } public NamespaceInfo findInfoForNamespace(String namespace) { for(PackageInfo next:this.packageToPackageInfoMappings.values()) { String nextUri = next.getNamespace(); if(nextUri == null) { nextUri = Constants.EMPTY_STRING; } if(namespace == null) { namespace = Constants.EMPTY_STRING; } if(nextUri.equals(namespace)) { return next.getNamespaceInfo(); } } return null; } void checkForCallbackMethods() { JavaClass unmarshallerCls = helper.getJavaClass(Unmarshaller.class); JavaClass marshallerCls = helper.getJavaClass(Marshaller.class); JavaClass objectCls = helper.getJavaClass(Object.class); JavaClass[] unmarshalParams = new JavaClass[] { unmarshallerCls, objectCls }; JavaClass[] marshalParams = new JavaClass[] { marshallerCls }; for (JavaClass next : typeInfoClasses) { if (next == null) { continue; } UnmarshalCallback unmarshalCallback = null; MarshalCallback marshalCallback = null; // look for before unmarshal callback if (next.getMethod("beforeUnmarshal", unmarshalParams) != null) { unmarshalCallback = new UnmarshalCallback(); unmarshalCallback.setDomainClassName(next.getQualifiedName()); unmarshalCallback.setHasBeforeUnmarshalCallback(); } // look for after unmarshal callback if (next.getMethod("afterUnmarshal", unmarshalParams) != null) { if (unmarshalCallback == null) { unmarshalCallback = new UnmarshalCallback(); unmarshalCallback.setDomainClassName(next.getQualifiedName()); } unmarshalCallback.setHasAfterUnmarshalCallback(); } // if before/after unmarshal callback was found, add the callback to // the list if (unmarshalCallback != null) { if (this.unmarshalCallbacks == null) { this.unmarshalCallbacks = new HashMap<String, UnmarshalCallback>(); } unmarshalCallbacks.put(next.getQualifiedName(), unmarshalCallback); } // look for before marshal callback if (next.getMethod("beforeMarshal", marshalParams) != null) { marshalCallback = new MarshalCallback(); marshalCallback.setDomainClassName(next.getQualifiedName()); marshalCallback.setHasBeforeMarshalCallback(); } // look for after marshal callback if (next.getMethod("afterMarshal", marshalParams) != null) { if (marshalCallback == null) { marshalCallback = new MarshalCallback(); marshalCallback.setDomainClassName(next.getQualifiedName()); } marshalCallback.setHasAfterMarshalCallback(); } // if before/after marshal callback was found, add the callback to // the list if (marshalCallback != null) { if (this.marshalCallbacks == null) { this.marshalCallbacks = new HashMap<String, MarshalCallback>(); } marshalCallbacks.put(next.getQualifiedName(), marshalCallback); } } } public Map<String, MarshalCallback> getMarshalCallbacks() { return this.marshalCallbacks; } public Map<String, UnmarshalCallback> getUnmarshalCallbacks() { return this.unmarshalCallbacks; } private void findAndProcessObjectFactory(JavaClass cls){ //need to make sure objectfactory gets processed. try { String className =cls.getPackageName() + ".ObjectFactory"; findAndProcessObjectFactory(className); } catch (JAXBException ignored) { /* ignored */} } void findAndProcessObjectFactory(String objectFactoryClassName){ //need to make sure objectfactory gets processed. try { if(objectFactoryClassNames.contains(objectFactoryClassName)){ return; } JavaClass javaClass = helper.getJavaClass(objectFactoryClassName); if (isXmlRegistry(javaClass)) { JavaClass[] processed = this.processObjectFactory(javaClass, new ArrayList<JavaClass>()); preBuildTypeInfo(processed); buildTypeInfo(processed); updateGlobalElements(processed); } } catch (JAXBException ignored){ /* ignored */ } } public JavaClass[] processObjectFactory(JavaClass objectFactoryClass, List<JavaClass> classes) { String className = objectFactoryClass.getName(); if(objectFactoryClassNames.contains(className)){ return new JavaClass[0]; } objectFactoryClassNames.add(className); // if there is an xml-registry from XML for this JavaClass, create a map // of method names to XmlElementDecl objects to simplify processing // later on in this method Map<String, org.eclipse.persistence.jaxb.xmlmodel.XmlRegistry.XmlElementDecl> elemDecls = new HashMap<String, org.eclipse.persistence.jaxb.xmlmodel.XmlRegistry.XmlElementDecl>(); org.eclipse.persistence.jaxb.xmlmodel.XmlRegistry xmlReg = xmlRegistries.get(objectFactoryClass.getQualifiedName()); if (xmlReg != null) { // process xml-element-decl entries for (org.eclipse.persistence.jaxb.xmlmodel.XmlRegistry.XmlElementDecl xmlElementDecl : xmlReg.getXmlElementDecl()) { // key each element-decl on method name elemDecls.put(xmlElementDecl.getJavaMethod(), xmlElementDecl); } } Collection methods = objectFactoryClass.getDeclaredMethods(); Iterator methodsIter = methods.iterator(); PackageInfo packageInfo = getPackageInfoForPackage(objectFactoryClass); while (methodsIter.hasNext()) { JavaMethod next = (JavaMethod) methodsIter.next(); if (next.getName().startsWith(CREATE)) { JavaClass type = next.getReturnType(); if (JAVAX_XML_BIND_JAXBELEMENT.equals(type.getName())) { Object[] actualTypeArguments = type.getActualTypeArguments().toArray(); if (actualTypeArguments.length == 0) { type = helper.OBJECT_CLASS; } else { type = (JavaClass) actualTypeArguments[0]; } type = processXmlElementDecl(type, next, packageInfo, elemDecls); }else if (helper.JAXBELEMENT_CLASS.isAssignableFrom(type)) { this.factoryMethods.put(type.getRawName(), next); type = processXmlElementDecl(type, next, packageInfo, elemDecls); } else { this.factoryMethods.put(type.getRawName(), next); } if (!helper.isBuiltInJavaType(type) && !helper.classExistsInArray(type, classes)) { classes.add(type); } } } if (classes.size() > 0) { classesToProcessPropertyTypes.addAll(classes); return classes.toArray(new JavaClass[classes.size()]); } else { return new JavaClass[0]; } } private JavaClass processXmlElementDecl(JavaClass type, JavaMethod next, PackageInfo packageInfo, Map<String, org.eclipse.persistence.jaxb.xmlmodel.XmlRegistry.XmlElementDecl> elemDecls){ JavaClass returnType = type; // if there's an XmlElementDecl for this method from XML, use it // - otherwise look for an annotation org.eclipse.persistence.jaxb.xmlmodel.XmlRegistry.XmlElementDecl xmlEltDecl = elemDecls.get(next.getName()); if (( xmlEltDecl != null) || helper.isAnnotationPresent(next, XmlElementDecl.class)) { QName qname; QName substitutionHead = null; String url; String localName; String defaultValue = null; Class scopeClass = javax.xml.bind.annotation.XmlElementDecl.GLOBAL.class; if (xmlEltDecl != null) { url = xmlEltDecl.getNamespace(); localName = xmlEltDecl.getName(); String scopeClassName = xmlEltDecl.getScope(); if (!scopeClassName.equals(ELEMENT_DECL_GLOBAL)) { JavaClass jScopeClass = helper.getJavaClass(scopeClassName); if (jScopeClass != null) { scopeClass = helper.getClassForJavaClass(jScopeClass); if (scopeClass == null) { scopeClass = javax.xml.bind.annotation.XmlElementDecl.GLOBAL.class; } } } if (!xmlEltDecl.getSubstitutionHeadName().equals(EMPTY_STRING)) { String subHeadLocal = xmlEltDecl.getSubstitutionHeadName(); String subHeadNamespace = xmlEltDecl.getSubstitutionHeadNamespace(); if (subHeadNamespace.equals(XMLProcessor.DEFAULT)) { subHeadNamespace = packageInfo.getNamespace(); } substitutionHead = new QName(subHeadNamespace, subHeadLocal); } if (!(xmlEltDecl.getDefaultValue().length() == 1 && xmlEltDecl.getDefaultValue().startsWith(ELEMENT_DECL_DEFAULT))) { defaultValue = xmlEltDecl.getDefaultValue(); } } else { // there was no xml-element-decl for this method in XML, // so use the annotation XmlElementDecl elementDecl = (XmlElementDecl) helper.getAnnotation(next, XmlElementDecl.class); url = elementDecl.namespace(); localName = elementDecl.name(); scopeClass = elementDecl.scope(); if (!elementDecl.substitutionHeadName().equals(EMPTY_STRING)) { String subHeadLocal = elementDecl.substitutionHeadName(); String subHeadNamespace = elementDecl.substitutionHeadNamespace(); if (subHeadNamespace.equals(XMLProcessor.DEFAULT)) { subHeadNamespace = packageInfo.getNamespace(); } substitutionHead = new QName(subHeadNamespace, subHeadLocal); } if (!(elementDecl.defaultValue().length() == 1 && elementDecl.defaultValue().startsWith(ELEMENT_DECL_DEFAULT))) { defaultValue = elementDecl.defaultValue(); } } if (XMLProcessor.DEFAULT.equals(url)) { url = packageInfo.getNamespace(); } if(Constants.EMPTY_STRING.equals(url)) { isDefaultNamespaceAllowed = false; qname = new QName(localName); }else{ qname = new QName(url, localName); } boolean isList = false; if (JAVA_UTIL_LIST.equals(type.getName())) { isList = true; Collection args = type.getActualTypeArguments(); if (args.size() > 0) { type = (JavaClass) args.iterator().next(); } } ElementDeclaration declaration = new ElementDeclaration(qname, type, type.getQualifiedName(), isList, scopeClass); if (substitutionHead != null) { declaration.setSubstitutionHead(substitutionHead); } if (defaultValue != null) { declaration.setDefaultValue(defaultValue); } if (helper.isAnnotationPresent(next, XmlJavaTypeAdapter.class)) { XmlJavaTypeAdapter typeAdapter = (XmlJavaTypeAdapter) helper.getAnnotation(next, XmlJavaTypeAdapter.class); Class typeAdapterClass = typeAdapter.value(); declaration.setJavaTypeAdapterClass(typeAdapterClass); Class declJavaType = CompilerHelper.getTypeFromAdapterClass(typeAdapterClass); JavaClass adaptedType = helper.getJavaClass(declJavaType); declaration.setJavaType(adaptedType); declaration.setAdaptedJavaType(type); returnType = adaptedType; } if (helper.isAnnotationPresent(next, XmlMimeType.class)) { XmlMimeType mimeType = (XmlMimeType)helper.getAnnotation(next, XmlMimeType.class); declaration.setXmlMimeType(mimeType.value()); } if (helper.isAnnotationPresent(next, XmlAttachmentRef.class)) { declaration.setXmlAttachmentRef(true); } Map<QName, ElementDeclaration> elements = getElementDeclarationsForScope(scopeClass.getName()); if (elements == null) { elements = new HashMap<QName, ElementDeclaration>(); this.elementDeclarations.put(scopeClass.getName(), elements); } if(elements.containsKey(qname)){ throw JAXBException.duplicateElementName(qname); } elements.put(qname, declaration); } return returnType; } /** * Lazy load and return the map of global elements. * * @return */ public Map<QName, ElementDeclaration> getGlobalElements() { return this.elementDeclarations.get(XmlElementDecl.GLOBAL.class.getName()); } public void updateGlobalElements(JavaClass[] classesToProcess) { // Once all the global element declarations have been created, make sure // that any ones that have // a substitution head set are added to the list of substitutable // elements on the declaration for that // head. // Look for XmlRootElement declarations for (JavaClass javaClass : classesToProcess) { TypeInfo info = typeInfos.get(javaClass.getQualifiedName()); if (info == null) { continue; } if (!info.isTransient() && info.isSetXmlRootElement()) { org.eclipse.persistence.jaxb.xmlmodel.XmlRootElement xmlRE = info.getXmlRootElement(); NamespaceInfo namespaceInfo; namespaceInfo = getPackageInfoForPackage(javaClass).getNamespaceInfo(); String elementName = xmlRE.getName(); if (elementName.equals(XMLProcessor.DEFAULT) || elementName.equals(EMPTY_STRING)) { XMLNameTransformer transformer = info.getXmlNameTransformer(); try { elementName = transformer.transformRootElementName(javaClass.getName()); } catch (Exception ex) { throw org.eclipse.persistence.exceptions.JAXBException.exceptionDuringNameTransformation(javaClass.getName(), info.getXmlNameTransformer().getClass().getName(), ex); } } String rootNamespace = xmlRE.getNamespace(); QName rootElemName = null; if (rootNamespace.equals(XMLProcessor.DEFAULT)) { if (namespaceInfo == null) { rootElemName = new QName(elementName); } else { String rootNS = namespaceInfo.getNamespace(); rootElemName = new QName(rootNS, elementName); if (rootNS.equals(Constants.EMPTY_STRING)) { isDefaultNamespaceAllowed = false; } } } else { rootElemName = new QName(rootNamespace, elementName); if (rootNamespace.equals(Constants.EMPTY_STRING)) { isDefaultNamespaceAllowed = false; } } ElementDeclaration declaration = new ElementDeclaration(rootElemName, javaClass, javaClass.getQualifiedName(), false); declaration.setIsXmlRootElement(true); addGlobalElement(rootElemName, declaration); this.xmlRootElements.put(javaClass.getQualifiedName(), declaration); } } for (QName next : this.getGlobalElements().keySet()) { ElementDeclaration nextDeclaration = this.getGlobalElements().get(next); QName substitutionHead = nextDeclaration.getSubstitutionHead(); while (substitutionHead != null) { ElementDeclaration rootDeclaration = this.getGlobalElements().get(substitutionHead); rootDeclaration.addSubstitutableElement(nextDeclaration); if (rootDeclaration.getSubstitutionHead() != null && rootDeclaration.getSubstitutionHead().equals(substitutionHead)) { // Break the loop if substitutionHead equals rootDeclaration's substitutionHead // (XmlElementDecl's substitutionHeadName == name) substitutionHead = null; } else { substitutionHead = rootDeclaration.getSubstitutionHead(); } } } } private void addReferencedElement(Property property, ElementDeclaration referencedElement) { property.addReferencedElement(referencedElement); if (referencedElement.getSubstitutableElements() != null && referencedElement.getSubstitutableElements().size() > 0) { for (ElementDeclaration substitutable : referencedElement.getSubstitutableElements()) { if (substitutable != referencedElement) { addReferencedElement(property, substitutable); } } } } /** * Returns true if the field or method passed in is annotated with JAXB * annotations. */ private boolean hasJAXBAnnotations(JavaHasAnnotations elem) { if(elem == null){ return false; } List annotations = (List<JavaAnnotation>) elem.getAnnotations(); if (annotations == null || annotations.isEmpty()) { return false; } for (Object annotation : annotations) { String nextName = ((JavaAnnotation) annotation).getName(); if (nextName.startsWith(JAVAX_XML_BIND_ANNOTATION) || nextName.startsWith(OXM_ANNOTATIONS) || nextName.equals(CompilerHelper.XML_LOCATION_ANNOTATION_NAME) || nextName.equals(CompilerHelper.INTERNAL_XML_LOCATION_ANNOTATION_NAME)) { return true; } } return false; } private void validatePropOrderForInfo(TypeInfo info) { if (info.isTransient()) { return; } if(info.getXmlVirtualAccessMethods() != null) { return; } // Ensure that all properties in the propOrder list actually exist String[] propOrder = info.getPropOrder(); int propOrderLength = propOrder.length; if (propOrderLength > 0) { for (int i = 1; i < propOrderLength; i++) { String nextPropName = propOrder[i]; if (!nextPropName.equals(EMPTY_STRING) && !info.getPropertyNames().contains(nextPropName)) { throw JAXBException.nonExistentPropertyInPropOrder(nextPropName); } } } } private void validateXmlValueFieldOrProperty(JavaClass cls, Property property) { JavaClass ptype = property.getActualType(); String propName = property.getPropertyName(); JavaClass parent = cls.getSuperclass(); while (parent != null && !(parent.getQualifiedName().equals(JAVA_LANG_OBJECT))) { if (!useXmlValueExtension(property)) { throw JAXBException.propertyOrFieldCannotBeXmlValue(propName); } else { TypeInfo parentTypeInfo = typeInfos.get(parent.getQualifiedName()); if(hasElementMappedProperties(parentTypeInfo)) { throw JAXBException.propertyOrFieldCannotBeXmlValue(propName); } parent = parent.getSuperclass(); } } QName schemaQName = getSchemaTypeOrNullFor(ptype); if (schemaQName == null) { TypeInfo refInfo = processReferencedClass(ptype); if (refInfo != null && !refInfo.isEnumerationType() && refInfo.getXmlValueProperty() == null) { throw JAXBException.invalidTypeForXmlValueField(propName); } } } private boolean useXmlValueExtension(Property property) { return MOXySystemProperties.xmlValueExtension || helper.isAnnotationPresent(property.getElement(), XmlValueExtension.class) || property.isXmlValueExtension(); } private boolean hasElementMappedProperties(TypeInfo typeInfo) { for(Property property : typeInfo.getPropertyList()) { if(!(property.isTransient()|| property.isAttribute() || property.isAnyAttribute())) { return true; } } return false; } private void validateXmlAttributeFieldOrProperty(TypeInfo tInfo, Property property) { // Check that @XmlAttribute references a Java type that maps to text in XML JavaClass ptype = property.getActualType(); TypeInfo refInfo = typeInfos.get(ptype.getQualifiedName()); if (refInfo != null) { if (!refInfo.isPostBuilt()) { postBuildTypeInfo(new JavaClass[] { ptype }); } if (!refInfo.isEnumerationType()) { JavaClass parent = ptype.getSuperclass(); boolean hasMapped = false; while (parent != null) { hasMapped = hasTextMapping(refInfo); if (hasMapped || parent.getQualifiedName().equals(JAVA_LANG_OBJECT)) { break; } refInfo = typeInfos.get(parent.getQualifiedName()); parent = parent.getSuperclass(); } if (!hasMapped) { String propName = property.getPropertyName(); String typeName = tInfo.getJavaClassName(); String refTypeName = refInfo.getJavaClassName(); throw org.eclipse.persistence.exceptions.JAXBException.mustMapToText(propName, typeName, refTypeName); } } } } private boolean hasTextMapping(TypeInfo tInfo) { Collection<Property> props = tInfo.getProperties().values(); for (Property property : props) { if (property.isAttribute()) { JavaClass ptype = property.getActualType(); TypeInfo refInfo = typeInfos.get(ptype.getQualifiedName()); if (refInfo != null && refInfo != tInfo) { return hasTextMapping(refInfo); } } } boolean hasXmlId = (tInfo.getIDProperty() != null && !tInfo.getIDProperty().isTransient()); boolean hasXmlValue = (tInfo.getXmlValueProperty() != null && !tInfo.getXmlValueProperty().isTransient()); if (hasXmlValue) { // Ensure there is an @XmlValue property and nothing else hasXmlValue = CompilerHelper.isSimpleType(tInfo); } return (hasXmlValue || hasXmlId); } private Class generateWrapperForMapClass(JavaClass mapClass, JavaClass keyClass, JavaClass valueClass, TypeMappingInfo typeMappingInfo) { String packageName = JAXB_DEV; NamespaceResolver combinedNamespaceResolver = new NamespaceResolver(); if (!helper.isBuiltInJavaType(keyClass)) { String keyPackageName = keyClass.getPackageName(); packageName = packageName + DOT_CHR + keyPackageName; NamespaceInfo keyNamespaceInfo = getPackageInfoForPackage(keyClass).getNamespaceInfo(); if (keyNamespaceInfo != null) { java.util.Vector<Namespace> namespaces = keyNamespaceInfo.getNamespaceResolver().getNamespaces(); for (Namespace n : namespaces) { combinedNamespaceResolver.put(n.getPrefix(), n.getNamespaceURI()); } } } if (!helper.isBuiltInJavaType(valueClass)) { String valuePackageName = valueClass.getPackageName(); packageName = packageName + DOT_CHR + valuePackageName; NamespaceInfo valueNamespaceInfo = getPackageInfoForPackage(valueClass).getNamespaceInfo(); if (valueNamespaceInfo != null) { java.util.Vector<Namespace> namespaces = valueNamespaceInfo.getNamespaceResolver().getNamespaces(); for (Namespace n : namespaces) { combinedNamespaceResolver.put(n.getPrefix(), n.getNamespaceURI()); } } } String namespace = this.defaultTargetNamespace; if (namespace == null) { namespace = EMPTY_STRING; } PackageInfo packageInfo = packageToPackageInfoMappings.get(mapClass.getPackageName()); if (packageInfo == null) { packageInfo = getPackageToPackageInfoMappings().get(packageName); } else { if (packageInfo.getNamespace() != null) { namespace = packageInfo.getNamespace(); } getPackageToPackageInfoMappings().put(packageName, packageInfo); } if (packageInfo == null) { packageInfo = new PackageInfo(); packageInfo.setNamespaceInfo(new NamespaceInfo()); packageInfo.setNamespace(namespace); packageInfo.setNamespaceResolver(combinedNamespaceResolver); getPackageToPackageInfoMappings().put(packageName, packageInfo); } int beginIndex = keyClass.getName().lastIndexOf(DOT_CHR) + 1; String keyName = keyClass.getName().substring(beginIndex); int dollarIndex = keyName.indexOf(DOLLAR_SIGN_CHR); if (dollarIndex > -1) { keyName = keyName.substring(dollarIndex + 1); } beginIndex = valueClass.getName().lastIndexOf(DOT_CHR) + 1; String valueName = valueClass.getName().substring(beginIndex); dollarIndex = valueName.indexOf(DOLLAR_SIGN_CHR); if (dollarIndex > -1) { valueName = valueName.substring(dollarIndex + 1); } String collectionClassShortName = mapClass.getRawName().substring(mapClass.getRawName().lastIndexOf(DOT_CHR) + 1); String suggestedClassName = keyName + valueName + collectionClassShortName; String qualifiedClassName = packageName + DOT_CHR + suggestedClassName; qualifiedClassName = getNextAvailableClassName(qualifiedClassName); String qualifiedInternalClassName = qualifiedClassName.replace(DOT_CHR, SLASH_CHR); String internalKeyName = keyClass.getQualifiedName().replace(DOT_CHR, SLASH_CHR); String internalValueName = valueClass.getQualifiedName().replace(DOT_CHR, SLASH_CHR); Type mapType = Type.getType(L + mapClass.getRawName().replace(DOT_CHR, SLASH_CHR) + SEMI_COLON); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); String sig = "Lorg/eclipse/persistence/internal/jaxb/many/MapValue<L" + mapType.getInternalName() + "<L" + internalKeyName + ";L" + internalValueName + ";>;>;"; cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, qualifiedInternalClassName, sig, "org/eclipse/persistence/internal/jaxb/many/MapValue", null); // Write Field: @... public Map entry String fieldSig = L + mapType.getInternalName() + "<L" + internalKeyName + ";L" + internalValueName + ";>;"; FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC, "entry", L + mapType.getInternalName() + SEMI_COLON, fieldSig, null); AnnotationVisitor av = fv.visitAnnotation(Type.getDescriptor(XmlElement.class), true); if (typeMappingInfo != null) { Annotation[] annotations = typeMappingInfo.getAnnotations(); if (annotations != null) { for (Annotation nextAnnotation : annotations) { if (nextAnnotation != null && !(nextAnnotation instanceof XmlElement) && !(nextAnnotation instanceof XmlJavaTypeAdapter)) { String annotationClassName = nextAnnotation.annotationType().getName(); av = fv.visitAnnotation(L + annotationClassName.replace(DOT_CHR, SLASH_CHR) + SEMI_COLON, true); for (Method next : nextAnnotation.annotationType().getDeclaredMethods()) { try { Object nextValue = next.invoke(nextAnnotation, new Object[] { }); if (nextValue instanceof Class) { Type nextType = Type.getType(L + ((Class) nextValue).getName().replace(DOT_CHR, SLASH_CHR) + SEMI_COLON); nextValue = nextType; } av.visit(next.getName(), nextValue); } catch (InvocationTargetException ignored) { // ignore the invocation target exception here. } catch (IllegalAccessException ignored) { // ignore the illegal access exception here. } } av.visitEnd(); } } } } fv.visitEnd(); MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "org/eclipse/persistence/internal/jaxb/many/MapValue", "<init>", "()V"); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); // Write: @XmlTransient public void setItem(???) String methodSig = "(L" + mapType.getInternalName() + "<L" + internalKeyName + ";L" + internalValueName + ";>;)V"; mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "setItem", "(L" + mapType.getInternalName() + ";)V", methodSig, null); // TODO: Verify that we really want to put @XmlTranient on setItem // method mv.visitAnnotation("Ljavax/xml/bind/annotation/XmlTransient;", true); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitFieldInsn(Opcodes.PUTFIELD, qualifiedInternalClassName, "entry", L + mapType.getInternalName() + SEMI_COLON); mv.visitInsn(Opcodes.RETURN); Label l1 = new Label(); mv.visitLabel(l1); // Replacement?:LocalVariableTypeTableAttribute cvAttr = new // LocalVariableTypeTableAttribute(); // mv.visitAttribute(cvAttr); mv.visitMaxs(2, 2); mv.visitEnd(); // Write @XmlTransient public ??? getItem() methodSig = "()L" + mapType.getInternalName() + "<L" + internalKeyName + ";L" + internalValueName + ";>;"; mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "getItem", "()L" + mapType.getInternalName() + SEMI_COLON, methodSig, null); mv.visitAnnotation("Ljavax/xml/bind/annotation/XmlTransient;", true); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, qualifiedInternalClassName, "entry", L + mapType.getInternalName() + SEMI_COLON); mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_BRIDGE + Opcodes.ACC_SYNTHETIC, "getItem", "()Ljava/lang/Object;", null, null); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, qualifiedInternalClassName, "getItem", "()L" + mapType.getInternalName() + SEMI_COLON, false); mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_BRIDGE + Opcodes.ACC_SYNTHETIC, "setItem", "(Ljava/lang/Object;)V", null, null); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitTypeInsn(Opcodes.CHECKCAST, mapType.getInternalName()); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, qualifiedInternalClassName, "setItem", "(L" + mapType.getInternalName() + ";)V", false); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(2, 2); mv.visitEnd(); // Write @XmlType(namespace) av = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlType;", true); av.visit("namespace", namespace); cw.visitEnd(); byte[] classBytes = cw.toByteArray(); return generateClassFromBytes(qualifiedClassName, classBytes); } private Class generateWrapperForArrayClass(JavaClass arrayClass, TypeMappingInfo typeMappingInfo, Class xmlElementType, List<JavaClass> classesToProcess) { JavaClass componentClass = null; if (typeMappingInfo != null && xmlElementType != null) { componentClass = helper.getJavaClass(xmlElementType); } else { componentClass = arrayClass.getComponentType(); } if (componentClass.isArray()) { Class nestedArrayClass = arrayClassesToGeneratedClasses.get(componentClass.getName()); if (nestedArrayClass == null) { nestedArrayClass = generateWrapperForArrayClass(componentClass, typeMappingInfo, xmlElementType, classesToProcess); arrayClassesToGeneratedClasses.put(componentClass.getName(), nestedArrayClass); classesToProcess.add(helper.getJavaClass(nestedArrayClass)); } return generateArrayValue(arrayClass, helper.getJavaClass(nestedArrayClass), helper.getJavaClass(nestedArrayClass), typeMappingInfo); } else { return generateArrayValue(arrayClass, componentClass, componentClass, typeMappingInfo); } } private Class generateArrayValue(JavaClass arrayClass, JavaClass componentClass, JavaClass nestedClass, TypeMappingInfo typeMappingInfo) { String packageName; String qualifiedClassName; QName qName = null; if (helper.getJavaClass(ManyValue.class).isAssignableFrom(componentClass)) { packageName = componentClass.getPackageName(); qualifiedClassName = nestedClass.getQualifiedName() + ARRAY_CLASS_NAME_SUFFIX; } else { if (componentClass.isPrimitive()) { packageName = ARRAY_PACKAGE_NAME; qualifiedClassName = packageName + DOT_CHR + componentClass.getName() + ARRAY_CLASS_NAME_SUFFIX; } else { packageName = ARRAY_PACKAGE_NAME + DOT_CHR + componentClass.getPackageName(); if (componentClass.isMemberClass()) { qualifiedClassName = componentClass.getName(); qualifiedClassName = qualifiedClassName.substring(qualifiedClassName.indexOf(DOLLAR_SIGN_CHR) + 1); qualifiedClassName = ARRAY_PACKAGE_NAME + DOT_CHR + componentClass.getPackageName() + DOT_CHR + qualifiedClassName + ARRAY_CLASS_NAME_SUFFIX; } else { qualifiedClassName = ARRAY_PACKAGE_NAME + DOT_CHR + componentClass.getQualifiedName() + ARRAY_CLASS_NAME_SUFFIX; } } if (componentClass.isPrimitive() || helper.isBuiltInJavaType(componentClass)) { qName = (QName) helper.getXMLToJavaTypeMap().get(componentClass.getQualifiedName()); if(null != qName) { packageName = ARRAY_PACKAGE_NAME; qualifiedClassName = ARRAY_PACKAGE_NAME + DOT_CHR + qName.getLocalPart() + ARRAY_CLASS_NAME_SUFFIX; } PackageInfo namespaceInfo = getPackageToPackageInfoMappings().get(packageName); if (namespaceInfo == null) { namespaceInfo = new PackageInfo(); namespaceInfo.setNamespaceInfo(new NamespaceInfo()); namespaceInfo.setNamespace(ARRAY_NAMESPACE); namespaceInfo.setNamespaceResolver(new NamespaceResolver()); getPackageToPackageInfoMappings().put(packageName, namespaceInfo); } } else { PackageInfo namespaceInfo = getPackageInfoForPackage(componentClass.getPackage(), componentClass.getPackageName()); getPackageToPackageInfoMappings().put(packageName, namespaceInfo); } } try { String qualifiedInternalClassName = qualifiedClassName.replace(DOT_CHR, SLASH_CHR); if (helper.getJavaClass(ManyValue.class).isAssignableFrom(componentClass)) { return generateClassFromBytes(qualifiedClassName, generateMultiDimensionalManyValueClass(typeMappingInfo, null, MultiDimensionalArrayValue.class, qualifiedInternalClassName, componentClass, arrayClass.getComponentType())); } else { return generateClassFromBytes(qualifiedClassName, generateManyValue(typeMappingInfo, null, ArrayValue.class, qualifiedInternalClassName, componentClass, componentClass)); } } catch(LinkageError e) { if(null != qName) { throw JAXBException.nameCollision(qName.getNamespaceURI(), qName.getLocalPart()); } throw e; } } private JavaClass getObjectType(JavaClass javaClass) { if (javaClass.isPrimitive()) { String primitiveClassName = javaClass.getRawName(); Class primitiveClass = getPrimitiveClass(primitiveClassName); return helper.getJavaClass(getObjectClass(primitiveClass)); } return javaClass; } private Class generateCollectionValue(JavaClass collectionClass, TypeMappingInfo typeMappingInfo, Class xmlElementType, List<JavaClass> classesToProcess) { JavaClass componentClass; if (typeMappingInfo != null && xmlElementType != null) { componentClass = helper.getJavaClass(xmlElementType); } else{ Collection args = collectionClass.getActualTypeArguments(); if(args.size() >0 ){ componentClass = ((JavaClass) args.toArray()[0]); }else{ componentClass = helper.getJavaClass(Object.class); } } boolean multiDimensional = false; if (componentClass.isPrimitive()) { Class primitiveClass = getPrimitiveClass(componentClass.getRawName()); componentClass = helper.getJavaClass(getObjectClass(primitiveClass)); } else if(helper.getJavaClass(Collection.class).isAssignableFrom(componentClass)) { multiDimensional = true; java.lang.reflect.Type nestedCollectionType = getNestedCollectionType(typeMappingInfo); Class nestedCollectionClass = collectionClassesToGeneratedClasses.get(nestedCollectionType); if (nestedCollectionClass == null) { nestedCollectionClass = generateCollectionValue(componentClass, typeMappingInfo, xmlElementType, classesToProcess); collectionClassesToGeneratedClasses.put(nestedCollectionType, nestedCollectionClass); classesToProcess.add(helper.getJavaClass(nestedCollectionClass)); } componentClass = helper.getJavaClass(nestedCollectionClass); } else if(componentClass.isArray()) { if(componentClass.getName().equals("[B")) { multiDimensional = false; } else { multiDimensional = true; Class nestedArrayClass = arrayClassesToGeneratedClasses.get(componentClass.getName()); if (nestedArrayClass == null) { nestedArrayClass = generateWrapperForArrayClass(componentClass, typeMappingInfo, xmlElementType, classesToProcess); arrayClassesToGeneratedClasses.put(componentClass.getName(), nestedArrayClass); } componentClass = helper.getJavaClass(nestedArrayClass); } } PackageInfo packageInfo = packageToPackageInfoMappings.get(collectionClass.getPackageName()); String namespace = EMPTY_STRING; if (this.defaultTargetNamespace != null) { namespace = this.defaultTargetNamespace; } PackageInfo componentNamespaceInfo = getPackageInfoForPackage(componentClass); String packageName = componentClass.getPackageName(); packageName = "jaxb.dev.java.net." + packageName; if (packageInfo == null) { packageInfo = getPackageToPackageInfoMappings().get(packageName); } else { getPackageToPackageInfoMappings().put(packageName, packageInfo); if (packageInfo.getNamespace() != null) { namespace = packageInfo.getNamespace(); } } if (packageInfo == null) { if (componentNamespaceInfo != null) { packageInfo = componentNamespaceInfo; } else { packageInfo = new PackageInfo(); packageInfo.setNamespaceInfo(new NamespaceInfo()); packageInfo.setNamespaceResolver(new NamespaceResolver()); } getPackageToPackageInfoMappings().put(packageName, packageInfo); } String name = componentClass.getName(); if("[B".equals(name)) { name = "byteArray"; } int beginIndex = name.lastIndexOf(DOT_CHR) + 1; name = name.substring(beginIndex); int dollarIndex = name.indexOf(DOLLAR_SIGN_CHR); if (dollarIndex > -1) { name = name.substring(dollarIndex + 1); } String collectionClassRawName = collectionClass.getRawName(); String collectionClassShortName = collectionClassRawName.substring(collectionClassRawName.lastIndexOf(DOT_CHR) + 1); String suggestedClassName = collectionClassShortName + "Of" + name; String qualifiedClassName = packageName + DOT_CHR + suggestedClassName; qualifiedClassName = getNextAvailableClassName(qualifiedClassName); String qualifiedInternalClassName = qualifiedClassName.replace(DOT_CHR, SLASH_CHR); byte[] classBytes; if(multiDimensional) { classBytes = generateMultiDimensionalManyValueClass(typeMappingInfo, namespace, MultiDimensionalCollectionValue.class, qualifiedInternalClassName, componentClass, collectionClass); } else { classBytes = generateManyValue(typeMappingInfo, namespace, CollectionValue.class, qualifiedInternalClassName, componentClass, collectionClass); } return generateClassFromBytes(qualifiedClassName, classBytes); } private java.lang.reflect.Type getNestedCollectionType(TypeMappingInfo mappingInfo) { java.lang.reflect.Type result = null; if (mappingInfo != null && mappingInfo.getType() != null) { // called for a collection, type must be parametrized ... ParameterizedType pType = (ParameterizedType) mappingInfo.getType(); java.lang.reflect.Type[] actualTypeArguments = pType.getActualTypeArguments(); result = actualTypeArguments != null && actualTypeArguments.length > 0 ? actualTypeArguments[0] : null; } if (result == null) { getLogger().logWarning("cant_get_nested_collection_type", new Object[]{}); } return result; } private byte[] generateManyValue(TypeMappingInfo typeMappingInfo, String namespace, Class superType, String classNameSeparatedBySlash, JavaClass componentType, JavaClass containerType) { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); generateManyValueClass(cw, typeMappingInfo, namespace, superType, classNameSeparatedBySlash, componentType, containerType); cw.visitEnd(); return cw.toByteArray(); } private void generateManyValueClass(ClassWriter cw, TypeMappingInfo typeMappingInfo, String namespace, Class superType, String classNameSeparatedBySlash, JavaClass componentType, JavaClass containerType) { String componentClassNameSeparatedBySlash = getObjectType(componentType).getQualifiedName().replace(DOT_CHR, SLASH_CHR); String containerClassNameSeperatedBySlash = containerType.getQualifiedName().replace(DOT_CHR, SLASH_CHR); if("[B".equals(componentClassNameSeparatedBySlash)) { cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, classNameSeparatedBySlash, "L" + Type.getInternalName(superType) + "<" + componentClassNameSeparatedBySlash + ">;", Type.getInternalName(superType), null); } else { cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, classNameSeparatedBySlash, "L" + Type.getInternalName(superType) + "<L" + componentClassNameSeparatedBySlash + ";>;", Type.getInternalName(superType), null); } // Write @XmlType(namespace) AnnotationVisitor av = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlType;", true); if(null != namespace) { av.visit("namespace", namespace); } if(classNameSeparatedBySlash.startsWith(ARRAY_PACKAGE_NAME.replace('.', '/')) && classNameSeparatedBySlash.contains("QName") ) { av.visit("name", classNameSeparatedBySlash.substring(classNameSeparatedBySlash.lastIndexOf('/') + 1)); } av.visitEnd(); // Public No-Arg Constructor MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(superType), "<init>", "()V", false); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); if(!componentType.isPrimitive() && ArrayValue.class.isAssignableFrom(superType)){ //@Override //public Object getItem() { // if(null == adaptedValue) { // return null; // } // int len = adaptedValue.size(); // Float[] array = new Float[len]; // adaptedValue.toArray(array); // return array; // } mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "getItem", "()Ljava/lang/Object;", null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, classNameSeparatedBySlash, "adaptedValue", "Ljava/util/Collection;"); Label l0 = new Label(); mv.visitJumpInsn(Opcodes.IFNONNULL, l0); mv.visitInsn(Opcodes.ACONST_NULL); mv.visitInsn(Opcodes.ARETURN); mv.visitLabel(l0); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, classNameSeparatedBySlash, "adaptedValue", "Ljava/util/Collection;"); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Collection", "size", "()I", true); mv.visitVarInsn(Opcodes.ISTORE, 1); mv.visitVarInsn(Opcodes.ILOAD, 1); mv.visitTypeInsn(Opcodes.ANEWARRAY, componentClassNameSeparatedBySlash); mv.visitVarInsn(Opcodes.ASTORE, 2); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, classNameSeparatedBySlash, "adaptedValue", "Ljava/util/Collection;"); mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Collection", "toArray", "([Ljava/lang/Object;)[Ljava/lang/Object;"); mv.visitInsn(Opcodes.POP); mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(2, 3); mv.visitEnd(); //@Override //public void setItem(Object array) { // Float[] floatArray = (Float[])array; // adaptedValue = (Collection<T>) Arrays.asList(floatArray); //} mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "setItem", "(Ljava/lang/Object;)V", null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitTypeInsn(Opcodes.CHECKCAST, "[L"+componentClassNameSeparatedBySlash+";"); mv.visitVarInsn(Opcodes.ASTORE, 2); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "asList", "([Ljava/lang/Object;)Ljava/util/List;", false); mv.visitFieldInsn(Opcodes.PUTFIELD, classNameSeparatedBySlash, "adaptedValue", "Ljava/util/Collection;"); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(2, 3); mv.visitEnd(); } // @XmlElement(name="item", nillable=true) // public Collection<COMPONENT_TYPE> getAdaptedValue() { // return super.getAdaptedValue(); // } // OR // @XmlValue // public Collection<COMPONENT_TYPE> getAdaptedValue() { // return super.getAdaptedValue(); // } if("[B".equals(componentClassNameSeparatedBySlash)) { mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "getAdaptedValue", "()Ljava/util/Collection;", "()Ljava/util/Collection<" + componentClassNameSeparatedBySlash + ">;", null); } else { mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "getAdaptedValue", "()Ljava/util/Collection;", "()Ljava/util/Collection<L" + componentClassNameSeparatedBySlash + ";>;", null); } // Copy annotations boolean hasXmlList = false; Annotation[] annotations; if (typeMappingInfo != null && ((annotations = getAnnotations(typeMappingInfo)) != null)) { for (Annotation annotation : annotations) { if(!(annotation instanceof XmlElement || annotation instanceof XmlJavaTypeAdapter)) { Class<? extends Annotation> annotationType = annotation.annotationType(); //if(annotationType.equals(XmlList.class)) { if(annotation instanceof XmlList) { hasXmlList = true; } av = mv.visitAnnotation(L + annotationType.getName().replace(DOT_CHR, SLASH_CHR) + SEMI_COLON, true); for (Method next : annotation.annotationType().getDeclaredMethods()) { try { Object nextValue = next.invoke(annotation, new Object[] {}); if (nextValue instanceof Class) { nextValue = Type.getType(L + ((Class) nextValue).getName().replace(DOT_CHR, SLASH_CHR) + SEMI_COLON); } av.visit(next.getName(), nextValue); } catch (InvocationTargetException ex) { } catch (IllegalAccessException ex) { } } av.visitEnd(); } } } if(hasXmlList) { // @XmlValue av = mv.visitAnnotation("Ljavax/xml/bind/annotation/XmlValue;", true); av = mv.visitAnnotation("Lorg/eclipse/persistence/oxm/annotations/XmlValueExtension;", true); av.visitEnd(); } else { // @XmlElement(name="item", nillable=true) av = mv.visitAnnotation("Ljavax/xml/bind/annotation/XmlElement;", true); av.visit("name", ITEM); av.visit("nillable", true); av.visitEnd(); } mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(superType), "getAdaptedValue", "()Ljava/util/Collection;", false); mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); // public void setAdaptedValue(Collection<COMPONENT_TYPE> adaptedValue) { // super.setAdaptedValue(adaptedValue); // } mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "setAdaptedValue", "(Ljava/util/Collection;)V", "(Ljava/util/Collection<L" + componentClassNameSeparatedBySlash + ";>;)V", null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(superType), "setAdaptedValue", "(Ljava/util/Collection;)V", false); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(2, 2); mv.visitEnd(); // public Class<?> containerClass() { // return CONTAINER_TYPE.class; // } mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "containerClass", "()Ljava/lang/Class;", "()Ljava/lang/Class<*>;", null); mv.visitCode(); if(componentType.isPrimitive()) { mv.visitFieldInsn(Opcodes.GETSTATIC, getObjectType(componentType).getQualifiedName().replace(DOT_CHR, SLASH_CHR), "TYPE", "Ljava/lang/Class;"); } else { if(containerClassNameSeperatedBySlash.contains(";")) { mv.visitLdcInsn(Type.getType(containerClassNameSeperatedBySlash)); } else { mv.visitLdcInsn(Type.getType("L" + containerClassNameSeperatedBySlash + ";")); } } mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } private byte[] generateMultiDimensionalManyValueClass(TypeMappingInfo typeMappingInfo, String namespace, Class superType, String classNameSeparatedBySlash, JavaClass componentType, JavaClass containerType) { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); generateManyValueClass(cw, typeMappingInfo, namespace, superType, classNameSeparatedBySlash, componentType, containerType); generateMultiDimensionalManyValueClass(cw, componentType); cw.visitEnd(); return cw.toByteArray(); } private void generateMultiDimensionalManyValueClass(ClassWriter cw, JavaClass componentType) { // public Class<?> componentClass() { // return COMPONENT_TYPE.class; // } String componentClassNameSeparatedBySlash = componentType.getQualifiedName().replace(DOT_CHR, SLASH_CHR); MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "componentClass", "()Ljava/lang/Class;", "()Ljava/lang/Class<L" + componentClassNameSeparatedBySlash + ";>;", null); mv.visitCode(); mv.visitLdcInsn(Type.getType("L" + componentClassNameSeparatedBySlash + ";")); mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } private Class generateClassFromBytes(String className, byte[] classBytes) { JaxbClassLoader loader = (JaxbClassLoader) helper.getClassLoader(); Class generatedClass = loader.generateClass(className, classBytes); return generatedClass; } /** * Inner class used for ordering a list of Properties alphabetically by * property name. * */ private static final class PropertyComparitor implements Comparator<Property> { @Override public int compare(Property p1, Property p2) { return p1.getPropertyName().compareTo(p2.getPropertyName()); } } private String getNextAvailableClassName(String suggestedName) { int counter = 1; return getNextAvailableClassName(suggestedName, suggestedName, counter); } private String getNextAvailableClassName(String suggestedBaseName, String suggestedName, int counter) { Iterator<Class> iter = typeMappingInfosToGeneratedClasses.values().iterator(); while (iter.hasNext()) { Class nextClass = iter.next(); if (nextClass.getName().equals(suggestedName)) { counter = counter + 1; return getNextAvailableClassName(suggestedBaseName, suggestedBaseName + counter, counter); } } return suggestedName; } private Class getPrimitiveClass(String primitiveClassName) { return ConversionManager.getDefaultManager().convertClassNameToClass(primitiveClassName); } private Class getObjectClass(Class primitiveClass) { return ConversionManager.getDefaultManager().getObjectClass(primitiveClass); } public Map<java.lang.reflect.Type, Class> getCollectionClassesToGeneratedClasses() { return collectionClassesToGeneratedClasses; } public Map<String, Class> getArrayClassesToGeneratedClasses() { return arrayClassesToGeneratedClasses; } public Map<Class, java.lang.reflect.Type> getGeneratedClassesToCollectionClasses() { return generatedClassesToCollectionClasses; } public Map<Class, JavaClass> getGeneratedClassesToArrayClasses() { return generatedClassesToArrayClasses; } /** * Convenience method for returning all of the TypeInfo objects for a given * package name. * * This method is inefficient as we need to iterate over the entire typeinfo * map for each call. We should eventually store the TypeInfos in a Map * based on package name, i.e.: * * Map {@literal <String, Map<String, TypeInfo>>} * * @param packageName * @return List of TypeInfo objects for a given package name */ public Map<String, TypeInfo> getTypeInfosForPackage(String packageName) { Map<String, TypeInfo> typeInfos = new HashMap<String, TypeInfo>(); List<JavaClass> jClasses = getTypeInfoClasses(); for (JavaClass jClass : jClasses) { if (jClass.getPackageName().equals(packageName)) { String key = jClass.getQualifiedName(); typeInfos.put(key, this.typeInfos.get(key)); } } return typeInfos; } /** * Set namespace override info from XML bindings file. This will typically * be called from the XMLProcessor. * * @param packageToNamespaceMappings */ public void setPackageToNamespaceMappings(HashMap<String, NamespaceInfo> packageToNamespaceMappings) { //this.packageToNamespaceMappings = packageToNamespaceMappings; } public void setPackageToPackageInfoMappings(HashMap<String, PackageInfo> packageToPackageInfoMappings) { this.packageToPackageInfoMappings = packageToPackageInfoMappings; } public SchemaTypeInfo addClass(JavaClass javaClass) { if (javaClass == null) { return null; } else if (helper.isAnnotationPresent(javaClass, XmlTransient.class)) { return null; } if (typeInfos == null) { // this is the first class. Initialize all the properties this.typeInfoClasses = new ArrayList<JavaClass>(); this.typeInfos = new HashMap<String, TypeInfo>(); this.typeQNames = new ArrayList<QName>(); this.userDefinedSchemaTypes = new HashMap<String, QName>(); this.packageToPackageInfoMappings = new HashMap<String, PackageInfo>(); } JavaClass[] jClasses = new JavaClass[] { javaClass }; buildNewTypeInfo(jClasses); TypeInfo info = typeInfos.get(javaClass.getQualifiedName()); PackageInfo packageInfo; String packageName = javaClass.getPackageName(); packageInfo = this.packageToPackageInfoMappings.get(packageName); SchemaTypeInfo schemaInfo = new SchemaTypeInfo(); schemaInfo.setSchemaTypeName(new QName(info.getClassNamespace(), info.getSchemaTypeName())); if (info.isSetXmlRootElement()) { org.eclipse.persistence.jaxb.xmlmodel.XmlRootElement xmlRE = info.getXmlRootElement(); String elementName = xmlRE.getName(); if (elementName.equals(XMLProcessor.DEFAULT) || elementName.equals(EMPTY_STRING)) { try { elementName = info.getXmlNameTransformer().transformRootElementName(javaClass.getName()); } catch (Exception ex) { throw org.eclipse.persistence.exceptions.JAXBException.exceptionDuringNameTransformation(javaClass.getName(), info.getXmlNameTransformer().getClass().getName(), ex); } } String rootNamespace = xmlRE.getNamespace(); QName rootElemName = null; if (rootNamespace.equals(XMLProcessor.DEFAULT)) { rootElemName = new QName(packageInfo.getNamespace(), elementName); } else { rootElemName = new QName(rootNamespace, elementName); } schemaInfo.getGlobalElementDeclarations().add(rootElemName); ElementDeclaration declaration = new ElementDeclaration(rootElemName, javaClass, javaClass.getRawName(), false); addGlobalElement(rootElemName, declaration); } return schemaInfo; } /** * Convenience method which class pre and postBuildTypeInfo for a given set * of JavaClasses. * * @param javaClasses */ public void buildNewTypeInfo(JavaClass[] javaClasses) { preBuildTypeInfo(javaClasses); javaClasses = postBuildTypeInfo(javaClasses); for(JavaClass next:javaClasses) { processPropertyTypes(next); } } /** * Pre-process a descriptor customizer. Here, the given JavaClass is checked * for the existence of an @XmlCustomizer annotation. * * Note that the post processing of the descriptor customizers will take * place in MappingsGenerator's generateProject method, after the * descriptors and mappings have been generated. * * @param jClass * @param tInfo * @see XmlCustomizer * @see MappingsGenerator */ private void preProcessCustomizer(JavaClass jClass, TypeInfo tInfo) { XmlCustomizer xmlCustomizer = (XmlCustomizer) helper.getAnnotation(jClass, XmlCustomizer.class); if (xmlCustomizer != null) { tInfo.setXmlCustomizer(xmlCustomizer.value().getName()); } } /** * Lazy load the metadata logger. * * @return */ private JAXBMetadataLogger getLogger() { if (logger == null) { logger = new JAXBMetadataLogger(); } return logger; } /** * Return the Helper object set on this processor. * * @return */ Helper getHelper() { return this.helper; } public boolean isDefaultNamespaceAllowed() { return isDefaultNamespaceAllowed; } public List<ElementDeclaration> getLocalElements() { return this.localElements; } public Map<TypeMappingInfo, Class> getTypeMappingInfosToGeneratedClasses() { return this.typeMappingInfosToGeneratedClasses; } public Map<TypeMappingInfo, Class> getTypeMappingInfoToAdapterClasses() { return this.typeMappingInfoToAdapterClasses; } /** * Add an XmlRegistry to ObjectFactory class name pair to the map. * * @param factoryClassName * ObjectFactory class name * @param xmlReg * org.eclipse.persistence.jaxb.xmlmodel.XmlRegistry instance */ public void addXmlRegistry(String factoryClassName, org.eclipse.persistence.jaxb.xmlmodel.XmlRegistry xmlReg) { this.xmlRegistries.put(factoryClassName, xmlReg); } /** * Convenience method for determining if a given JavaClass should be * processed as an ObjectFactory class. * * @param javaClass * @return true if the JavaClass is annotated with @XmlRegistry or the map * of XmlRegistries contains a key equal to the JavaClass' qualified * name */ private boolean isXmlRegistry(JavaClass javaClass) { if (javaClass == null) { return false; } return (helper.isAnnotationPresent(javaClass, XmlRegistry.class) || xmlRegistries.get(javaClass.getQualifiedName()) != null); } public Map<TypeMappingInfo, QName> getTypeMappingInfosToSchemaTypes() { return this.typeMappingInfosToSchemaTypes; } String getDefaultTargetNamespace() { return this.defaultTargetNamespace; } void setDefaultTargetNamespace(String defaultTargetNamespace) { this.defaultTargetNamespace = defaultTargetNamespace; } public void setDefaultNamespaceAllowed(boolean isDefaultNamespaceAllowed) { this.isDefaultNamespaceAllowed = isDefaultNamespaceAllowed; } Map<QName, ElementDeclaration> getElementDeclarationsForScope(String scopeClassName) { return this.elementDeclarations.get(scopeClassName); } private void addGlobalElement(QName key, ElementDeclaration declaration){ getGlobalElements().put(key, declaration); classesToProcessPropertyTypes.add(declaration.getJavaType()); } private Map<Object, Object> createUserPropertiesMap(XmlProperty[] properties) { Map<Object, Object> propMap = new HashMap<Object, Object>(); for (XmlProperty prop : properties) { Object pvalue = prop.value(); if (!(prop.valueType() == String.class)) { pvalue = XMLConversionManager.getDefaultXMLManager().convertObject(prop.value(), prop.valueType()); } propMap.put(prop.name(), pvalue); } return propMap; } /** * Indicates if a given Property represents an MTOM attachment. Will return * true if the given Property's actual type is one of: * * - DataHandler - byte[] - Byte[] - Image - Source - MimeMultipart * * @param property * @return */ public boolean isMtomAttachment(Property property) { JavaClass ptype = property.getActualType(); return (areEquals(ptype, JAVAX_ACTIVATION_DATAHANDLER) || areEquals(ptype, byte[].class) || areEquals(ptype, Image.class) || areEquals(ptype, Source.class) || areEquals(ptype, JAVAX_MAIL_INTERNET_MIMEMULTIPART)); } public boolean hasSwaRef() { return this.hasSwaRef; } public void setHasSwaRef(boolean swaRef) { this.hasSwaRef = swaRef; } public List getReferencedByTransformer(){ return referencedByTransformer; } /** * Indicates whether this AnnotationsProcessor has been configured to enable * processing of XmlAccessorFactory annotations. * * @see com.sun.xml.internal.bind.XmlAccessorFactory */ public boolean isXmlAccessorFactorySupport() { return xmlAccessorFactorySupport; } /** * Sets whether this AnnotationsProcessor should process XmlAccessorFactory annotations. * * @see com.sun.xml.internal.bind.XmlAccessorFactory */ public void setXmlAccessorFactorySupport(boolean value) { this.xmlAccessorFactorySupport = value; } public void setHasXmlBindings(boolean b) { this.hasXmlBindings = true; } public boolean hasXmlBindings() { return this.hasXmlBindings; } }