blob: a892b6406d57e47833d170aab938b9a22aca80de [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1998, 2010 Oracle. 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.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
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.Set;
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.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.XmlValue;
import javax.xml.bind.annotation.XmlType.DEFAULT;
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.JAXBException;
import org.eclipse.persistence.internal.descriptors.Namespace;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.internal.jaxb.JaxbClassLoader;
import org.eclipse.persistence.internal.libraries.asm.ClassWriter;
import org.eclipse.persistence.internal.libraries.asm.CodeVisitor;
import org.eclipse.persistence.internal.libraries.asm.Constants;
import org.eclipse.persistence.internal.libraries.asm.Label;
import org.eclipse.persistence.internal.libraries.asm.Type;
import org.eclipse.persistence.internal.libraries.asm.attrs.Annotation;
import org.eclipse.persistence.internal.libraries.asm.attrs.LocalVariableTypeTableAttribute;
import org.eclipse.persistence.internal.libraries.asm.attrs.RuntimeVisibleAnnotations;
import org.eclipse.persistence.internal.libraries.asm.attrs.SignatureAttribute;
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.jaxb.TypeMappingInfo;
import org.eclipse.persistence.jaxb.javamodel.AnnotationProxy;
import org.eclipse.persistence.jaxb.javamodel.Helper;
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.oxm.NamespaceResolver;
import org.eclipse.persistence.oxm.XMLConstants;
import org.eclipse.persistence.oxm.annotations.XmlContainerProperty;
import org.eclipse.persistence.oxm.annotations.XmlCustomizer;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
/**
* 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>
* <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 class AnnotationsProcessor {
private static final String JAVAX_ACTIVATION_DATAHANDLER = "javax.activation.DataHandler";
private static final String JAVAX_MAIL_INTERNET_MIMEMULTIPART = "javax.mail.internet.MimeMultipart";
private static final String TYPE_METHOD_NAME = "type";
private static final String VALUE_METHOD_NAME = "value";
private ArrayList<JavaClass> typeInfoClasses;
private HashMap<String, NamespaceInfo> packageToNamespaceMappings;
private HashMap<String, MarshalCallback> marshalCallbacks;
private HashMap<String, QName> userDefinedSchemaTypes;
private HashMap<String, TypeInfo> typeInfo;
private ArrayList<QName> typeQNames;
private HashMap<String, UnmarshalCallback> unmarshalCallbacks;
private HashMap<String, HashMap<QName, ElementDeclaration>> elementDeclarations;
private HashMap<String, ElementDeclaration> xmlRootElements;
private List<ElementDeclaration> localElements;
private HashMap<String, JavaMethod> factoryMethods;
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, TypeMappingInfo> javaClassToTypeMappingInfos;
private Map<TypeMappingInfo, Class> typeMappingInfoToGeneratedClasses;
private Map<TypeMappingInfo, Class> typeMappingInfoToAdapterClasses;
private Map<TypeMappingInfo, QName> typeMappingInfoToSchemaType;
private NamespaceResolver namespaceResolver;
private Helper helper;
private String defaultTargetNamespace;
private JAXBMetadataLogger logger;
private boolean isDefaultNamespaceAllowed;
public AnnotationsProcessor(Helper helper) {
this.helper = helper;
isDefaultNamespaceAllowed = true;
}
/**
* Generate TypeInfo instances for a given array of JavaClasses.
*
* @param classes
*/
void processClassesAndProperties(JavaClass[] classes, TypeMappingInfo[] typeMappingInfos) {
init(classes, typeMappingInfos);
preBuildTypeInfo(classes);
classes = postBuildTypeInfo(classes);
processJavaClasses(classes);
finalizeProperties();
createElementsForTypeMappingInfo();
}
public void createElementsForTypeMappingInfo() {
if(this.javaClassToTypeMappingInfos != null && !this.javaClassToTypeMappingInfos.isEmpty()) {
Set<JavaClass> classes = this.javaClassToTypeMappingInfos.keySet();
for(JavaClass nextClass:classes) {
TypeMappingInfo nextInfo = this.javaClassToTypeMappingInfos.get(nextClass);
if(nextInfo != null) {
boolean xmlAttachmentRef = false;
String xmlMimeType = null;
java.lang.annotation.Annotation[] annotations = getAnnotations(nextInfo);
Class adapterClass = this.typeMappingInfoToAdapterClasses.get(nextInfo);
Class declJavaType = null;
if(adapterClass != null){
declJavaType = CompilerHelper.getTypeFromAdapterClass(adapterClass);
}
if (annotations != null) {
for (int j = 0; j < annotations.length; j++) {
java.lang.annotation.Annotation nextAnnotation = annotations[j];
if(nextAnnotation != null){
if (nextAnnotation instanceof XmlMimeType){
XmlMimeType javaAnnotation = (XmlMimeType)nextAnnotation;
xmlMimeType = javaAnnotation.value();
}else if (nextAnnotation instanceof XmlAttachmentRef){
xmlAttachmentRef = true;
}
}
}
}
QName qname = null;
String nextClassName = nextClass.getQualifiedName();
if(declJavaType != null){
nextClassName = declJavaType.getCanonicalName();
}
if(typeMappingInfoToGeneratedClasses != null){
Class generatedClass = typeMappingInfoToGeneratedClasses.get(nextInfo);
if(generatedClass != null){
nextClassName = generatedClass.getCanonicalName();
}
}
TypeInfo nextTypeInfo = typeInfo.get(nextClassName);
if(nextTypeInfo != null){
qname = new QName(nextTypeInfo.getClassNamespace(), nextTypeInfo.getSchemaTypeName());
} else {
qname = getUserDefinedSchemaTypes().get(nextClassName);
if(qname == null){
if (nextClassName.equals(ClassConstants.ABYTE.getName()) || nextClassName.equals(ClassConstants.APBYTE.getName()) || nextClassName.equals(Image.class.getName()) || nextClassName.equals(Source.class.getName()) || nextClassName.equals("javax.activation.DataHandler") ) {
if(xmlAttachmentRef){
qname = XMLConstants.SWA_REF_QNAME;
}else{
qname = XMLConstants.BASE_64_BINARY_QNAME;
}
} else if(nextClassName.equals(ClassConstants.OBJECT.getName())){
qname = XMLConstants.ANY_TYPE_QNAME;
} else {
Class theClass = helper.getClassForJavaClass(nextClass);
qname = (QName)XMLConversionManager.getDefaultJavaTypes().get(theClass);
}
}
}
if(qname != null){
typeMappingInfoToSchemaType.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);
if(declJavaType != null){
element.setJavaType(helper.getJavaClass(declJavaType));
}
Class generatedClass = typeMappingInfoToGeneratedClasses.get(nextInfo);
if(generatedClass != null) {
element.setJavaType(helper.getJavaClass(generatedClass));
}
if(nextInfo.getElementScope() == TypeMappingInfo.ElementScope.Global) {
this.getGlobalElements().put(element.getElementName(), element);
} else {
this.localElements.add(element);
}
}
}
}
}
}
/**
* 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 = (org.eclipse.persistence.jaxb.xmlmodel.XmlElement) 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();
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();
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();
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>();
typeInfo = new HashMap<String, TypeInfo>();
typeQNames = new ArrayList<QName>();
userDefinedSchemaTypes = new HashMap<String, QName>();
if (packageToNamespaceMappings == null) {
packageToNamespaceMappings = new HashMap<String, NamespaceInfo>();
}
this.factoryMethods = new HashMap<String, JavaMethod>();
this.namespaceResolver = new NamespaceResolver();
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>();
typeMappingInfoToGeneratedClasses = new HashMap<TypeMappingInfo, Class>();
typeMappingInfoToSchemaType = new HashMap<TypeMappingInfo, QName>();
elementDeclarations = new HashMap<String, HashMap<QName, ElementDeclaration>>();
HashMap globalElements = new HashMap<QName, ElementDeclaration>();
elementDeclarations.put(XmlElementDecl.GLOBAL.class.getName(), globalElements);
localElements = new ArrayList<ElementDeclaration>();
javaClassToTypeMappingInfos = new HashMap<JavaClass, TypeMappingInfo>();
if (typeMappingInfos != null) {
for (int i = 0; i < typeMappingInfos.length; i++) {
javaClassToTypeMappingInfos.put(classes[i], typeMappingInfos[i]);
}
}
typeMappingInfoToAdapterClasses = new HashMap<TypeMappingInfo, Class>();
if (typeMappingInfos != null) {
for(TypeMappingInfo next:typeMappingInfos) {
java.lang.annotation.Annotation[] annotations = getAnnotations(next);
if(annotations != null) {
for(java.lang.annotation.Annotation nextAnnotation:annotations) {
if(nextAnnotation instanceof XmlJavaTypeAdapter) {
typeMappingInfoToAdapterClasses.put(next, ((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) {
if (javaClass == null || !shouldGenerateTypeInfo(javaClass) || helper.isAnnotationPresent(javaClass, XmlRegistry.class)) {
continue;
}
TypeInfo info = typeInfo.get(javaClass.getQualifiedName());
if (info != null) {
if (info.isPreBuilt()) {
continue;
}
}
if (javaClass.isEnum()) {
info = new EnumTypeInfo(helper);
} else {
info = new TypeInfo(helper);
}
info.setPreBuilt(true);
// handle @XmlTransient
if (helper.isAnnotationPresent(javaClass, XmlTransient.class)) {
info.setXmlTransient(true);
}
// handle @XmlRootElement
processXmlRootElement(javaClass, info);
// handle @XmlSeeAlso
processXmlSeeAlso(javaClass, info);
NamespaceInfo packageNamespace = getNamespaceInfoForPackage(javaClass);
// handle @XmlType
preProcessXmlType(javaClass, info, packageNamespace);
// handle @XmlAccessorType
preProcessXmlAccessorType(javaClass, info, packageNamespace);
// handle @XmlAccessorOrder
preProcessXmlAccessorOrder(javaClass, info, packageNamespace);
// handle package level @XmlJavaTypeAdapters
processPackageLevelAdapters(javaClass, info);
// handle class level @XmlJavaTypeAdapters
processClassLevelAdapters(javaClass, info);
// handle descriptor customizer
preProcessCustomizer(javaClass, info);
typeInfoClasses.add(javaClass);
typeInfo.put(javaClass.getQualifiedName(), info);
}
return typeInfo;
}
/**
* Process any additional classes (i.e. inner classes, @XmlSeeAlso, @XmlRegisrty, 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;
}
// create type info instances for any additional classes
javaClasses = processAdditionalClasses(javaClasses);
preBuildTypeInfo(javaClasses);
updateGlobalElements(javaClasses);
buildTypeInfo(javaClasses);
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 = typeInfo.get(javaClass.getQualifiedName());
if (info == null || info.isPostBuilt()) {
continue;
}
info.setPostBuilt(true);
// handle factory methods
processFactoryMethods(javaClass, info);
// handle @XmlSchemaType(s)
processSchemaTypes(javaClass, info);
NamespaceInfo packageNamespace = getNamespaceInfoForPackage(javaClass);
// handle @XmlAccessorType
postProcessXmlAccessorType(info, packageNamespace);
// handle @XmlType
postProcessXmlType(javaClass, info, packageNamespace);
// handle @XmlEnum
if (info.isEnumerationType()) {
addEnumTypeInfo(javaClass, ((EnumTypeInfo) info));
continue;
}
// process schema type name
processTypeQName(javaClass, info, packageNamespace);
// handle superclass if necessary
JavaClass superClass = (JavaClass) javaClass.getSuperclass();
if (shouldGenerateTypeInfo(superClass)) {
JavaClass[] jClassArray = new JavaClass[] { superClass };
buildNewTypeInfo(jClassArray);
}
// add properties
info.setProperties(getPropertiesForClass(javaClass, info));
// process properties
processTypeInfoProperties(javaClass, info);
// handle @XmlAccessorOrder
postProcessXmlAccessorOrder(info, packageNamespace);
// Make sure this class has a factory method or a zero arg constructor
if (info.getFactoryMethodName() == null && info.getObjectFactoryClassName() == null) {
JavaConstructor zeroArgConstructor = javaClass.getDeclaredConstructor(new JavaClass[] {});
if (zeroArgConstructor == null) {
throw org.eclipse.persistence.exceptions.JAXBException.factoryMethodOrConstructorRequired(javaClass.getName());
}
}
validatePropOrderForInfo(info);
}
return typeInfo;
}
/**
* Perform any final generation and/or validation operations on TypeInfo
* properties.
*
*/
public void finalizeProperties() {
ArrayList<JavaClass> jClasses = getTypeInfoClasses();
for (JavaClass jClass : jClasses) {
TypeInfo tInfo = getTypeInfo().get(jClass.getQualifiedName());
// validate XmlValue
if (tInfo.getXmlValueProperty() != null) {
validateXmlValueFieldOrProperty(jClass, tInfo.getXmlValueProperty());
}
for (Property property : tInfo.getPropertyList()) {
// 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()) {
throw JAXBException.propertyOrFieldShouldBeAnAttribute(property.getPropertyName());
}
}
// if the property is an XmlIDREF, the target must have an XmlID set
if (property.isXmlIdRef()) {
JavaClass typeClass = property.getActualType();
TypeInfo targetInfo = typeInfo.get(typeClass.getQualifiedName());
if (targetInfo != null && targetInfo.getIDProperty() == null) {
throw JAXBException.invalidIdRef(property.getPropertyName(), typeClass.getQualifiedName());
}
}
// there can only be one XmlID per type info
if (property.isXmlId() && tInfo.getIDProperty() != null && !(tInfo.getIDProperty().getPropertyName().equals(property.getPropertyName()))) {
throw JAXBException.idAlreadySet(property.getPropertyName(), tInfo.getIDProperty().getPropertyName(), jClass.getName());
}
// 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() && tInfo.isSetAnyElementPropertyName() && !(tInfo.getAnyElementPropertyName().equals(property.getPropertyName()))) {
throw JAXBException.xmlAnyElementAlreadySet(property.getPropertyName(), tInfo.getAnyElementPropertyName(), jClass.getName());
}
// 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 (!isCollectionType(property) && !property.getType().isArray()) {
throw JAXBException.invalidElementWrapper(property.getPropertyName());
}
}
}
}
}
/**
* Process a given TypeInfo instance's properties.
*
* @param info
*/
private void processTypeInfoProperties(JavaClass javaClass, TypeInfo info) {
ArrayList<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);
JavaClass propertyType = property.getActualType();
if (shouldGenerateTypeInfo(propertyType)) {
JavaClass[] jClassArray = new JavaClass[] { propertyType };
buildNewTypeInfo(jClassArray);
}
}
}
/**
* Process a given set of JavaClass instances. @XmlIDREFs will be validated, and call back methods
* will be handled as required. This method is typically called after init, preBuildTypeInfo, and
* postBuildTypeInfo have been called.
*
* @param classes
*/
public void processJavaClasses(JavaClass[] classes) {
ArrayList<JavaClass> classesToProcess = new ArrayList<JavaClass>();
for (JavaClass javaClass : classes) {
classesToProcess.add(javaClass);
}
checkForCallbackMethods();
}
/**
* 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) {
Class xmlElementType = null;
JavaClass javaClass = jClass;
TypeMappingInfo tmi = javaClassToTypeMappingInfos.get(javaClass);
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 = (JavaClass) returnType;
break;
}
}
}
javaClass = newType;
}
java.lang.annotation.Annotation[] annotations = getAnnotations(tmi);
if (annotations != null) {
for (int j = 0; j < annotations.length; j++) {
java.lang.annotation.Annotation nextAnnotation = annotations[j];
if (nextAnnotation != null) {
if (nextAnnotation instanceof XmlElement) {
XmlElement javaAnnotation = (XmlElement) nextAnnotation;
if (javaAnnotation.type() != XmlElement.DEFAULT.class) {
xmlElementType = javaAnnotation.type();
}
}
}
}
}
}
if (areEquals(javaClass, byte[].class) || 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);
getGlobalElements().put(null, declaration);
}
} else if (javaClass.isArray()) {
if (!helper.isBuiltInJavaType(javaClass.getComponentType())) {
extraClasses.add(javaClass.getComponentType());
}
Class generatedClass = CompilerHelper.getExisitingGeneratedClass(tmi, typeMappingInfoToGeneratedClasses, typeMappingInfoToAdapterClasses, helper.getClassLoader());
if(generatedClass == null){
generatedClass = generateWrapperForArrayClass(javaClass, tmi, xmlElementType);
extraClasses.add(helper.getJavaClass(generatedClass));
arrayClassesToGeneratedClasses.put(javaClass.getRawName(), generatedClass);
}
generatedClassesToArrayClasses.put(generatedClass, javaClass);
typeMappingInfoToGeneratedClasses.put(tmi, generatedClass);
} else if (isCollectionType(javaClass)) {
JavaClass componentClass;
if (javaClass.hasActualTypeArguments()) {
componentClass = (JavaClass) javaClass.getActualTypeArguments().toArray()[0];
if (!componentClass.isPrimitive()) {
extraClasses.add(componentClass);
}
} else {
componentClass = helper.getJavaClass(Object.class);
}
Class generatedClass = CompilerHelper.getExisitingGeneratedClass(tmi, typeMappingInfoToGeneratedClasses, typeMappingInfoToAdapterClasses, helper.getClassLoader());
if(generatedClass == null){
generatedClass = generateCollectionValue(javaClass, tmi, xmlElementType);
extraClasses.add(helper.getJavaClass(generatedClass));
}
typeMappingInfoToGeneratedClasses.put(tmi, generatedClass);
} else if (isMapType(javaClass)) {
JavaClass keyClass;
JavaClass valueClass;
if (javaClass.hasActualTypeArguments()) {
keyClass = (JavaClass) javaClass.getActualTypeArguments().toArray()[0];
if (!helper.isBuiltInJavaType(keyClass)) {
extraClasses.add(keyClass);
}
valueClass = (JavaClass) javaClass.getActualTypeArguments().toArray()[1];
if (!helper.isBuiltInJavaType(valueClass)) {
extraClasses.add(valueClass);
}
} else {
keyClass = helper.getJavaClass(Object.class);
valueClass = helper.getJavaClass(Object.class);
}
Class generatedClass = CompilerHelper.getExisitingGeneratedClass(tmi, typeMappingInfoToGeneratedClasses, typeMappingInfoToAdapterClasses, helper.getClassLoader());
if(generatedClass == null){
generatedClass = generateWrapperForMapClass(javaClass, keyClass, valueClass, tmi);
extraClasses.add(helper.getJavaClass(generatedClass));
}
typeMappingInfoToGeneratedClasses.put(tmi, generatedClass);
} else {
// process @XmlRegistry, @XmlSeeAlso and inner classes
processClass(javaClass, classesToProcess);
}
}
// process @XmlRegistry, @XmlSeeAlso and inner classes
for (JavaClass javaClass : extraClasses) {
processClass(javaClass, classesToProcess);
}
return classesToProcess.toArray(new JavaClass[classesToProcess.size()]);
}
/**
* Adds additional classes to the given List, from inner classes,
* @XmlRegistry or @XmlSeeAlso.
*
* @param javaClass
* @param classesToProcess
*/
private void processClass(JavaClass javaClass, ArrayList<JavaClass> classesToProcess) {
if (shouldGenerateTypeInfo(javaClass)) {
if (helper.isAnnotationPresent(javaClass, XmlRegistry.class)) {
this.processObjectFactory(javaClass, classesToProcess);
} else {
classesToProcess.add(javaClass);
// handle @XmlSeeAlso
TypeInfo info = typeInfo.get(javaClass.getQualifiedName());
if (info != null && info.isSetXmlSeeAlso()) {
for (String jClassName : info.getXmlSeeAlso()) {
classesToProcess.add(helper.getJavaClass(jClassName));
}
}
// handle inner classes
for (Iterator<JavaClass> jClassIt = javaClass.getDeclaredClasses().iterator(); jClassIt.hasNext();) {
JavaClass innerClass = jClassIt.next();
if (shouldGenerateTypeInfo(innerClass)) {
TypeInfo tInfo = typeInfo.get(innerClass.getQualifiedName());
if ((tInfo != null && !tInfo.isTransient()) || !helper.isAnnotationPresent(innerClass, XmlTransient.class)) {
classesToProcess.add(innerClass);
}
}
}
}
}
}
/**
* 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");
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) {
}
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().getRawName());
JavaClass[] paramTypes = factoryMethod.getParameterTypes();
if (paramTypes != null && paramTypes.length > 0) {
String[] paramTypeNames = new String[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
paramTypeNames[i] = paramTypes[i].getRawName();
}
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[] { boundType, 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 @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();
if (helper.isAnnotationPresent(javaClass, XmlType.class)) {
XmlType typeAnnotation = (XmlType) helper.getAnnotation(javaClass, XmlType.class);
// set name
xmlType.setName(typeAnnotation.name());
// set namespace
xmlType.setNamespace(typeAnnotation.namespace());
// set propOrder
String[] propOrder = typeAnnotation.propOrder();
// handle case where propOrder is an empty array
if (propOrder != null) {
xmlType.getPropOrder();
}
for (String prop : propOrder) {
xmlType.getPropOrder().add(prop);
}
// set factoryClass
Class factoryClass = typeAnnotation.factoryClass();
if (factoryClass == DEFAULT.class) {
xmlType.setFactoryClass("javax.xml.bind.annotation.XmlType.DEFAULT");
} else {
xmlType.setFactoryClass(factoryClass.getCanonicalName());
}
// set factoryMethodName
xmlType.setFactoryMethod(typeAnnotation.factoryMethod());
} else {
// set defaults
xmlType.setName(getSchemaTypeNameForClassName(javaClass.getName()));
xmlType.setNamespace(packageNamespace.getNamespace());
}
info.setXmlType(xmlType);
}
/**
* 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, NamespaceInfo 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("")) {
// 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.setObjectFactoryClassName(javaClass.getRawName());
info.setFactoryMethodName(factoryMethodName);
}
} else {
if (factoryMethodName == null || factoryMethodName.equals("")) {
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("##default")) {
typeName = getSchemaTypeNameForClassName(javaClass.getName());
}
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("")) {
info.setPropOrder(new String[] { "" });
} else {
info.setPropOrder(xmlType.getPropOrder().toArray(new String[xmlType.getPropOrder().size()]));
}
}
// figure out namespace
if (xmlType.getNamespace().equals("##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 (helper.isAnnotationPresent(javaClass, XmlAccessorType.class)) {
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, NamespaceInfo packageNamespace) {
if (!info.isSetXmlAccessType()) {
// use value in package-info.java as last resort - will default if not set
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 javaClass
* @param info
*/
private void postProcessXmlAccessorOrder(TypeInfo info, NamespaceInfo 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(), 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.setOriginalType(property.getType());
property.setType(helper.getJavaClass(element.type()));
property.setHasXmlElementType(true);
}
// handle default value
if (!element.defaultValue().equals("\u0000")) {
property.setDefaultValue(element.defaultValue());
}
validateElementIsInPropOrder(info, property.getPropertyName());
}
}
/**
* 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 propertyType
* @return if @XmlJavaTypeAdapter exists return property's value type; otherwise propertyType
*/
private void processXmlJavaTypeAdapter(Property property, TypeInfo info) {
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 = typeInfo.get(ptype.getQualifiedName());
if (ptypeInfo == null && shouldGenerateTypeInfo(ptype)) {
JavaClass[] jClassArray = new JavaClass[] { ptype };
buildNewTypeInfo(jClassArray);
}
if (ptypeInfo!= null && ptypeInfo.getXmlJavaTypeAdapter() != null){
property.setXmlJavaTypeAdapter(ptypeInfo.getXmlJavaTypeAdapter() );
} else if (info.getPackageLevelAdaptersByClass().get(ptype.getQualifiedName()) != null) {
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) {
String typeName = info.getSchemaTypeName();
if (typeName != null && !("".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() || javaClass.isInterface() || javaClass.isArray()) {
return false;
}
if (javaClass.getRawName().equals("org.eclipse.persistence.internal.jaxb.ArrayWrappedValue")) {
return false;
}
if (userDefinedSchemaTypes.get(javaClass.getQualifiedName()) != null) {
return false;
}
if (helper.isBuiltInJavaType(javaClass)) {
return false;
}
if (isCollectionType(javaClass) || 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();
TypeInfo superClassInfo = typeInfo.get(superClass.getQualifiedName());
while (superClassInfo != null && superClassInfo.isTransient()) {
List<Property> superProps = getPublicMemberPropertiesForClass(superClass, superClassInfo);
returnList.addAll(0, superProps);
superClass = superClass.getSuperclass();
superClassInfo = typeInfo.get(superClass.getQualifiedName());
}
}
if (info.isTransient()) {
returnList.addAll(getNoAccessTypePropertiesForClass(cls, info));
} else if (info.getXmlAccessType() == XmlAccessType.FIELD) {
returnList.addAll(getFieldPropertiesForClass(cls, info, false));
} else if (info.getXmlAccessType() == XmlAccessType.PROPERTY) {
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) {
ArrayList<Property> properties = new ArrayList<Property>();
if (cls == null) {
return properties;
}
for (Iterator<JavaField> fieldIt = cls.getDeclaredFields().iterator(); fieldIt.hasNext();) {
JavaField nextField = fieldIt.next();
if (!helper.isAnnotationPresent(nextField, XmlTransient.class)) {
int modifiers = nextField.getModifiers();
if (!Modifier.isTransient(modifiers) && ((Modifier.isPublic(nextField.getModifiers()) && onlyPublic) || !onlyPublic)) {
if(!Modifier.isStatic(modifiers)){
Property property = buildNewProperty(info, cls, nextField, nextField.getName(), nextField.getResolvedType());
properties.add(property);
}else if(Modifier.isFinal(modifiers) && helper.isAnnotationPresent(nextField, XmlAttribute.class)){
try{
Property property = buildNewProperty(info, cls, nextField, nextField.getName(), nextField.getResolvedType());
Object value = ((JavaFieldImpl)nextField).get(null);
String stringValue = (String)XMLConversionManager.getDefaultXMLManager().convertObject(value, String.class, property.getSchemaType());
property.setFixedValue(stringValue);
properties.add(property);
}catch(ClassCastException e){
//do Nothing
}catch(IllegalAccessException e){
//do Nothing
}
}
}
} else {
// If a property is marked transient ensure it doesn't exist in the propOrder
List<String> propOrderList = Arrays.asList(info.getPropOrder());
if (propOrderList.contains(nextField.getName())) {
throw JAXBException.transientInProporder(nextField.getName());
}
}
}
return properties;
}
/*
* Create a new Property Object and process the annotations that are common to fields and methods
*/
private Property buildNewProperty(TypeInfo info, JavaClass cls, JavaHasAnnotations javaHasAnnotations, String propertyName, JavaClass ptype){
Property property = null;
if (helper.isAnnotationPresent(javaHasAnnotations, XmlElements.class)) {
property = buildChoiceProperty(info, cls, javaHasAnnotations, propertyName, ptype);
} else if (helper.isAnnotationPresent(javaHasAnnotations, XmlAnyElement.class)) {
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, XmlElementRef.class) || helper.isAnnotationPresent(javaHasAnnotations, XmlElementRefs.class)) {
property = buildReferenceProperty(info, cls, javaHasAnnotations, propertyName, ptype);
} 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
TypeInfo pTypeInfo = typeInfo.get(ptype.getQualifiedName());
if ((pTypeInfo != null && !pTypeInfo.isTransient()) || !helper.isAnnotationPresent(ptype, XmlTransient.class)) {
property.setType(ptype);
} else {
JavaClass parent = ptype.getSuperclass();
while (parent != null) {
if (parent.getName().equals("java.lang.Object")) {
property.setType(parent);
break;
}
// if there is a TypeInfo for parent check it for transient, otherwise check the class
TypeInfo parentTypeInfo = typeInfo.get(parent.getQualifiedName());
if ((parentTypeInfo != null && !parentTypeInfo.isTransient()) || !helper.isAnnotationPresent(parent, XmlTransient.class)) {
property.setType(parent);
break;
}
parent = parent.getSuperclass();
}
}
property.setSchemaName(getQNameForProperty(propertyName, javaHasAnnotations, getNamespaceInfoForPackage(cls), info.getClassNamespace()));
processPropertyAnnotations(info, cls, javaHasAnnotations, property);
ptype = property.getActualType();
if (ptype.isPrimitive() || ptype.isArray() && ptype.getComponentType().isPrimitive()){
property.setIsRequired(true);
}
// apply class level adapters - don't override property level adapter
if (!property.isSetXmlJavaTypeAdapter()) {
TypeInfo refClassInfo = getTypeInfo().get(ptype.getQualifiedName());
if (refClassInfo != null && refClassInfo.isSetXmlJavaTypeAdapter()) {
property.setXmlJavaTypeAdapter(refClassInfo.getXmlJavaTypeAdapter());
}
}
return property;
}
private Property buildChoiceProperty(TypeInfo info, JavaClass cls, JavaHasAnnotations javaHasAnnotations, String propertyName, JavaClass propertyType){
ChoiceProperty property = new ChoiceProperty(helper);
XmlElements xmlElements = (XmlElements) helper.getAnnotation(javaHasAnnotations, XmlElements.class);
XmlElement[] elements = xmlElements.value();
ArrayList<Property> choiceProperties = new ArrayList<Property>(elements.length);
boolean isIdRef = helper.isAnnotationPresent(javaHasAnnotations, XmlIDREF.class);
property.setIsXmlIdRef(isIdRef);
validateElementIsInPropOrder(info, propertyName);
for (int i = 0; i < elements.length; i++) {
XmlElement next = elements[i];
Property choiceProp = new Property(helper);
String name = next.name();
String namespace = next.namespace();
QName qName = null;
if (name.equals("##default")) {
name = propertyName;
}
if (!namespace.equals("##default")) {
qName = new QName(namespace, name);
} else {
NamespaceInfo namespaceInfo = getNamespaceInfoForPackage(cls);
if (namespaceInfo.isElementFormQualified()) {
qName = new QName(namespaceInfo.getNamespace(), name);
} else {
qName = new QName(name);
}
}
choiceProp.setPropertyName(property.getPropertyName());
Class typeClass = next.type();
if (typeClass.equals(XmlElement.DEFAULT.class)) {
choiceProp.setType(propertyType);
} else {
choiceProp.setType(helper.getJavaClass(typeClass));
}
choiceProp.setSchemaName(qName);
choiceProp.setSchemaType(getSchemaTypeFor(choiceProp.getType()));
choiceProp.setElement(javaHasAnnotations);
choiceProp.setIsXmlIdRef(isIdRef);
choiceProperties.add(choiceProp);
}
property.setChoiceProperties(choiceProperties);
return property;
}
private Property buildReferenceProperty(TypeInfo info, JavaClass cls, JavaHasAnnotations javaHasAnnotations, String propertyName, JavaClass type){
ReferenceProperty property = new ReferenceProperty(helper);
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.setHasElementRefs(true);
}
validateElementIsInPropOrder(info, propertyName);
for (XmlElementRef nextRef : elementRefs) {
String typeName = type.getQualifiedName();
property.setType(type);
if (isCollectionType(property)) {
if (type.hasActualTypeArguments()) {
type = (JavaClass) type.getActualTypeArguments().toArray()[0];
typeName = type.getQualifiedName();
}
}
if (nextRef.type() != XmlElementRef.DEFAULT.class) {
typeName = helper.getJavaClass(nextRef.type()).getQualifiedName();
}
ElementDeclaration referencedElement = this.xmlRootElements.get(typeName);
if (referencedElement != null) {
addReferencedElement((ReferenceProperty) property, referencedElement);
} else {
String name = nextRef.name();
String namespace = nextRef.namespace();
if (namespace.equals("##default")) {
namespace = "";
}
QName qname = new QName(namespace, name);
JavaClass scopeClass = cls;
while(!(scopeClass.getName().equals("java.lang.Object"))) {
HashMap<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 processPropertyAnnotations(TypeInfo info, JavaClass cls, JavaHasAnnotations javaHasAnnotations, Property property){
//Check for mixed context
if (helper.isAnnotationPresent(javaHasAnnotations, XmlMixed.class)) {
info.setMixed(true);
property.setMixedContent(true);
}
if(helper.isAnnotationPresent(javaHasAnnotations, XmlContainerProperty.class)) {
XmlContainerProperty container = (XmlContainerProperty)helper.getAnnotation(javaHasAnnotations, XmlContainerProperty.class);
property.setInverseReferencePropertyName(container.value());
property.setInverseReferencePropertyGetMethodName(container.getMethodName());
property.setInverseReferencePropertySetMethodName(container.setMethodName());
} else if(helper.isAnnotationPresent(javaHasAnnotations, XmlInverseReference.class)) {
XmlInverseReference inverseReference = (XmlInverseReference) helper.getAnnotation(javaHasAnnotations, XmlInverseReference.class);
property.setInverseReferencePropertyName(inverseReference.mappedBy());
TypeInfo targetInfo = this.getTypeInfo().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" + propName);
property.setInverseReferencePropertySetMethodName("set" + propName);
}
property.setInverseReference(true);
}
processXmlJavaTypeAdapter(property, info);
processXmlElement(property, info);
JavaClass ptype = property.getActualType();
if (helper.isAnnotationPresent(property.getElement(), XmlAttachmentRef.class) && areEquals(ptype, JAVAX_ACTIVATION_DATAHANDLER)) {
property.setIsSwaAttachmentRef(true);
property.setSchemaType(XMLConstants.SWA_REF_QNAME);
} else if (areEquals(ptype, JAVAX_ACTIVATION_DATAHANDLER) || areEquals(ptype, byte[].class) || areEquals(ptype, Byte[].class) || areEquals(ptype, Image.class) || areEquals(ptype, Source.class)
|| areEquals(ptype, JAVAX_MAIL_INTERNET_MIMEMULTIPART)) {
property.setIsMtomAttachment(true);
property.setSchemaType(XMLConstants.BASE_64_BINARY_QNAME);
}
if (helper.isAnnotationPresent(property.getElement(), XmlMimeType.class)) {
property.setMimeType(((XmlMimeType) helper.getAnnotation(property.getElement(), XmlMimeType.class)).value());
}
// Get schema-type info if specified and set it on the property for later use:
if (helper.isAnnotationPresent(property.getElement(), XmlSchemaType.class)) {
XmlSchemaType schemaType = (XmlSchemaType) helper.getAnnotation(property.getElement(), XmlSchemaType.class);
QName schemaTypeQname = new QName(schemaType.namespace(), schemaType.name());
property.setSchemaType(schemaTypeQname);
}
if (helper.isAnnotationPresent(property.getElement(), XmlAttribute.class)) {
property.setIsAttribute(true);
property.setIsRequired(((XmlAttribute) helper.getAnnotation(property.getElement(), XmlAttribute.class)).required());
}
if (helper.isAnnotationPresent(property.getElement(), XmlAnyAttribute.class)) {
if (info.isSetAnyAttributePropertyName()) {
throw org.eclipse.persistence.exceptions.JAXBException.multipleAnyAttributeMapping(cls.getName());
}
if (!property.getType().getName().equals("java.util.Map")) {
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(property.getElement(), XmlElementWrapper.class)) {
XmlElementWrapper wrapper = (XmlElementWrapper) helper.getAnnotation(property.getElement(), XmlElementWrapper.class);
org.eclipse.persistence.jaxb.xmlmodel.XmlElementWrapper xmlEltWrapper = new org.eclipse.persistence.jaxb.xmlmodel.XmlElementWrapper();
xmlEltWrapper.setName(wrapper.name());
xmlEltWrapper.setNamespace(wrapper.namespace());
xmlEltWrapper.setNillable(wrapper.nillable());
xmlEltWrapper.setRequired(wrapper.required());
property.setXmlElementWrapper(xmlEltWrapper);
}
if (helper.isAnnotationPresent(property.getElement(), XmlList.class)) {
// Make sure XmlList annotation is on a collection or array
if (!isCollectionType(property) && !property.getType().isArray()) {
throw JAXBException.invalidList(property.getPropertyName());
}
property.setIsXmlList(true);
}
if (helper.isAnnotationPresent(property.getElement(), XmlValue.class)) {
property.setIsXmlValue(true);
info.setXmlValueProperty(property);
}
}
/**
* 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());
}
/**
* 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, String tgtCanonicalName) {
if (src == null || tgtCanonicalName == null) {
return false;
}
return src.getRawName().equals(tgtCanonicalName);
}
public ArrayList<Property> getPropertyPropertiesForClass(JavaClass cls, TypeInfo info, boolean onlyPublic) {
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.getName().startsWith("get") && next.getName().length() > 3) || (next.getName().startsWith("is") && next.getName().length() > 2)) {
int modifiers = next.getModifiers();
if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers) && ((onlyPublic && Modifier.isPublic(next.getModifiers())) || !onlyPublic)) {
propertyMethods.add(next);
}
} else if (next.getName().startsWith("set") && 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)) {
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)
ArrayList<String> propertyNames = new ArrayList<String>();
for (int i = 0; i < propertyMethods.size(); i++) {
boolean isPropertyTransient = false;
JavaMethod nextMethod = propertyMethods.get(i);
String propertyName = "";
JavaMethod getMethod;
JavaMethod setMethod;
JavaMethod propertyMethod = null;
if (!nextMethod.getName().startsWith("set")) {
if (nextMethod.getName().startsWith("get")) {
propertyName = nextMethod.getName().substring(3);
} else if (nextMethod.getName().startsWith("is")) {
propertyName = nextMethod.getName().substring(2);
}
getMethod = nextMethod;
String setMethodName = "set" + propertyName;
// use the JavaBean API to correctly decapitalize the first character, if necessary
propertyName = Introspector.decapitalize(propertyName);
JavaClass[] paramTypes = { (JavaClass) getMethod.getReturnType() };
setMethod = cls.getDeclaredMethod(setMethodName, paramTypes);
if (setMethod != null && !setMethod.getAnnotations().isEmpty()) {
// use the set method if it exists and is annotated
if (!helper.isAnnotationPresent(setMethod, XmlTransient.class)) {
propertyMethod = setMethod;
} else {
isPropertyTransient = true;
}
} else {
if (!helper.isAnnotationPresent(getMethod, XmlTransient.class)) {
propertyMethod = getMethod;
} else {
isPropertyTransient = true;
}
}
} else {
propertyName = nextMethod.getName().substring(3);
setMethod = nextMethod;
String getMethodName = "get" + propertyName;
getMethod = cls.getDeclaredMethod(getMethodName, new JavaClass[] {});
if (getMethod == null) {
// try is instead of get
getMethodName = "is" + propertyName;
getMethod = cls.getDeclaredMethod(getMethodName, new JavaClass[] {});
}
if (getMethod != null && !getMethod.getAnnotations().isEmpty()) {
// use the set method if it exists and is annotated
if (!helper.isAnnotationPresent(getMethod, XmlTransient.class)) {
propertyMethod = getMethod;
} else {
isPropertyTransient = true;
}
} else {
if (!helper.isAnnotationPresent(setMethod, XmlTransient.class)) {
propertyMethod = setMethod;
} else {
isPropertyTransient = true;
}
}
// use the JavaBean API to correctly decapitalize the first character, if necessary
propertyName = Introspector.decapitalize(propertyName);
}
JavaClass ptype = null;
if (getMethod != null) {
ptype = (JavaClass) getMethod.getReturnType();
} else {
ptype = setMethod.getParameterTypes()[0];
}
if (!propertyNames.contains(propertyName)) {
propertyNames.add(propertyName);
Property property = buildNewProperty(info, cls, propertyMethod, propertyName, ptype);
property.setTransient(isPropertyTransient);
if (getMethod != null) {
property.setGetMethodName(getMethod.getName());
}
if (setMethod != null) {
property.setSetMethodName(setMethod.getName());
}
property.setMethodProperty(true);
if (!helper.isAnnotationPresent(property.getElement(), XmlTransient.class)) {
properties.add(property);
} else {
// If a property is marked transient ensure it doesn't exist in the propOrder
List<String> propOrderList = Arrays.asList(info.getPropOrder());
if (propOrderList.contains(propertyName)) {
throw JAXBException.transientInProporder(propertyName);
}
property.setTransient(true);
}
}
}
// default to alphabetical ordering
// RI compliancy
Collections.sort(properties, new PropertyComparitor());
return properties;
}
public ArrayList getPublicMemberPropertiesForClass(JavaClass cls, TypeInfo info) {
ArrayList<Property> fieldProperties = getFieldPropertiesForClass(cls, info, false);
ArrayList<Property> methodProperties = getPropertyPropertiesForClass(cls, info, false);
// 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 fieldPropertyMap = getPropertyMapFromArrayList(publicFieldProperties);
for (int i = 0; i < publicMethodProperties.size(); i++) {
Property next = (Property) publicMethodProperties.get(i);
if (fieldPropertyMap.get(next.getPropertyName()) == null) {
publicFieldProperties.add(next);
}
}
return publicFieldProperties;
}
}
public HashMap getPropertyMapFromArrayList(ArrayList<Property> props) {
HashMap propMap = new HashMap(props.size());
Iterator propIter = props.iterator();
while (propIter.hasNext()) {
Property next = (Property) propIter.next();
propMap.put(next.getPropertyName(), next);
}
return propMap;
}
public ArrayList getNoAccessTypePropertiesForClass(JavaClass cls, TypeInfo info) {
ArrayList list = new ArrayList();
if (cls == null) {
return list;
}
ArrayList fieldProperties = getFieldPropertiesForClass(cls, info, false);
ArrayList methodProperties = getPropertyPropertiesForClass(cls, info, false);
// 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
for (int i = 0; i < fieldProperties.size(); i++) {
Property next = (Property) fieldProperties.get(i);
JavaHasAnnotations elem = next.getElement();
if (hasJAXBAnnotations(elem)) {
list.add(next);
}
}
for (int i = 0; i < methodProperties.size(); i++) {
Property next = (Property) methodProperties.get(i);
JavaHasAnnotations elem = next.getElement();
if (hasJAXBAnnotations(elem)) {
list.add(next);
}
}
return list;
}
public void processSchemaType(XmlSchemaType type) {
String schemaTypeName = type.name();
Class javaType = type.type();
if (javaType == null) {
return;
}
JavaClass jClass = helper.getJavaClass(javaType);
if (jClass == null) {
return;
}
QName typeQName = new QName(type.namespace(), schemaTypeName);
this.userDefinedSchemaTypes.put(jClass.getQualifiedName(), typeQName);
}
public void addEnumTypeInfo(JavaClass javaClass, EnumTypeInfo info) {
if (javaClass == null) {
return;
}
info.setClassName(javaClass.getQualifiedName());
Class restrictionClass = String.class;
if (helper.isAnnotationPresent(javaClass, XmlEnum.class)) {
XmlEnum xmlEnum = (XmlEnum) helper.getAnnotation(javaClass, XmlEnum.class);
restrictionClass = xmlEnum.value();
}
QName restrictionBase = getSchemaTypeFor(helper.getJavaClass(restrictionClass));
info.setRestrictionBase(restrictionBase);
for (Iterator<JavaField> fieldIt = javaClass.getDeclaredFields().iterator(); fieldIt.hasNext();) {
JavaField field = fieldIt.next();
if (field.isEnumConstant()) {
String fieldValue = field.getName();
if (helper.isAnnotationPresent(field, XmlEnumValue.class)) {
XmlEnumValue xmlEnumValue = (XmlEnumValue) helper.getAnnotation(field, XmlEnumValue.class);
fieldValue = xmlEnumValue.value();
}
info.addObjectToFieldValuePair(field.getName(), fieldValue);
}
}
}
private String decapitalize(String javaName) {
// return Introspector.decapitalize(name); Spec Compliant
// RI compliancy
char[] name = javaName.toCharArray();
int i = 0;
while (i < name.length && Character.isUpperCase(name[i])) {
i++;
}
if (i > 0) {
name[0] = Character.toLowerCase(name[0]);
for (int j = 1; j < i - 1; j++) {
name[j] = Character.toLowerCase(name[j]);
}
return new String(name);
} else {
return javaName;
}
}
public String getSchemaTypeNameForClassName(String className) {
String typeName = "";
if (className.indexOf('$') != -1) {
typeName = decapitalize(className.substring(className.lastIndexOf('$') + 1));
} else {
typeName = decapitalize(className.substring(className.lastIndexOf('.') + 1));
}
// now capitalize any characters that occur after a "break"
boolean inBreak = false;
StringBuffer toReturn = new StringBuffer(typeName.length());
for (int i = 0; i < typeName.length(); i++) {
char next = typeName.charAt(i);
if (Character.isDigit(next)) {
if (!inBreak) {
inBreak = true;
}
toReturn.append(next);
} else {
if (inBreak) {
toReturn.append(Character.toUpperCase(next));
} else {
toReturn.append(next);
}
}
}
return toReturn.toString();
}
public QName getSchemaTypeOrNullFor(JavaClass javaClass) {
if (javaClass == null) {
return null;
}
// check user defined types first
QName schemaType = (QName) 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 XMLConstants.ANY_SIMPLE_TYPE_QNAME;
}
return schemaType;
}
public boolean isCollectionType(Property field) {
return isCollectionType(field.getType());
}
public boolean isCollectionType(JavaClass type) {
if (helper.getJavaClass(java.util.Collection.class).isAssignableFrom(type)
|| helper.getJavaClass(java.util.List.class).isAssignableFrom(type)
|| helper.getJavaClass(java.util.Set.class).isAssignableFrom(type)) {
return true;
}
return false;
}
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("") || namespaceMapping.equals("##default"))) {
packageNamespace = namespaceMapping;
} else if(namespaceMapping.equals("##default")) {
packageNamespace = this.defaultTargetNamespace;
}
info.setNamespace(packageNamespace);
XmlNs[] xmlns = xmlSchema.xmlns();
for (int i = 0; i < xmlns.length; i++) {
XmlNs next = xmlns[i];
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("")) {
location = null;
}
}
info.setLocation(location);
} catch (Exception ex) {
}
} else {
info.setNamespace(defaultTargetNamespace);
}
if(!info.isElementFormQualified() || info.isAttributeFormQualified()){
isDefaultNamespaceAllowed = false;
}
return info;
}
public HashMap<String, TypeInfo> getTypeInfo() {
return typeInfo;
}
public ArrayList<JavaClass> getTypeInfoClasses() {
return typeInfoClasses;
}
public HashMap<String, QName> getUserDefinedSchemaTypes() {
return userDefinedSchemaTypes;
}
public NamespaceResolver getNamespaceResolver() {
return namespaceResolver;
}
public String getSchemaTypeNameFor(JavaClass javaClass, XmlType xmlType) {
String typeName = "";
if (javaClass == null) {
return typeName;
}
if (helper.isAnnotationPresent(javaClass, XmlType.class)) {
// Figure out what kind of type we have
// figure out type name
XmlType typeAnnotation = (XmlType) helper.getAnnotation(javaClass, XmlType.class);
typeName = typeAnnotation.name();
if (typeName.equals("#default")) {
typeName = getSchemaTypeNameForClassName(javaClass.getName());
}
} else {
typeName = getSchemaTypeNameForClassName(javaClass.getName());
}
return typeName;
}
public QName getQNameForProperty(String defaultName, JavaHasAnnotations element, NamespaceInfo namespaceInfo, String uri) {
String name = "##default";
String namespace = "##default";
QName qName = null;
if (helper.isAnnotationPresent(element, XmlAttribute.class)) {
XmlAttribute xmlAttribute = (XmlAttribute) helper.getAnnotation(element, XmlAttribute.class);
name = xmlAttribute.name();
namespace = xmlAttribute.namespace();
if (name.equals("##default")) {
name = defaultName;
}
if (!namespace.equals("##default")) {
qName = new QName(namespace, name);
isDefaultNamespaceAllowed = false;
} else {
if (namespaceInfo.isAttributeFormQualified()) {
qName = new QName(uri, name);
} 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 (name.equals("##default")) {
name = defaultName;
}
if (!namespace.equals("##default")) {
qName = new QName(namespace, name);
if(namespace.equals(XMLConstants.EMPTY_STRING)){
isDefaultNamespaceAllowed = false;
}
} else {
if (namespaceInfo.isElementFormQualified()) {
qName = new QName(uri, name);
} else {
qName = new QName(name);
}
}
}
return qName;
}
public HashMap<String, NamespaceInfo> getPackageToNamespaceMappings() {
return packageToNamespaceMappings;
}
/**
* Add a package name/NamespaceInfo entry to the map. This method will lazy-load
* the map if necessary.
*
* @return
*/
public void addPackageToNamespaceMapping(String packageName, NamespaceInfo nsInfo) {
if (packageToNamespaceMappings == null) {
packageToNamespaceMappings = new HashMap<String, NamespaceInfo>();
}
packageToNamespaceMappings.put(packageName, nsInfo);
}
public NamespaceInfo getNamespaceInfoForPackage(JavaClass javaClass) {
NamespaceInfo packageNamespace = packageToNamespaceMappings.get(javaClass.getPackageName());
if (packageNamespace == null) {
packageNamespace = getNamespaceInfoForPackage(javaClass.getPackage());
}
return packageNamespace;
}
public NamespaceInfo getNamespaceInfoForPackage(JavaPackage pack) {
NamespaceInfo packageNamespace = packageToNamespaceMappings.get(pack.getQualifiedName());
if (packageNamespace == null) {
XmlSchema xmlSchema = (XmlSchema) helper.getAnnotation(pack, XmlSchema.class);
packageNamespace = processNamespaceInformation(xmlSchema);
// if it's still null, generate based on package name
if (packageNamespace.getNamespace() == null) {
packageNamespace.setNamespace("");
}
if (helper.isAnnotationPresent(pack, XmlAccessorType.class)) {
XmlAccessorType xmlAccessorType = (XmlAccessorType) helper.getAnnotation(pack, XmlAccessorType.class);
packageNamespace.setAccessType(XmlAccessType.fromValue(xmlAccessorType.value().name()));
}
if (helper.isAnnotationPresent(pack, XmlAccessorOrder.class)) {
XmlAccessorOrder xmlAccessorOrder = (XmlAccessorOrder) helper.getAnnotation(pack, XmlAccessorOrder.class);
packageNamespace.setAccessOrder(XmlAccessOrder.fromValue(xmlAccessorOrder.value().name()));
}
packageToNamespaceMappings.put(pack.getQualifiedName(), packageNamespace);
}
return packageNamespace;
}
public NamespaceInfo getNamespaceInfoForPackage(String packageName) {
NamespaceInfo packageNamespace = packageToNamespaceMappings.get(packageName);
if (packageName == null) {
packageNamespace = new NamespaceInfo();
packageNamespace.setNamespaceResolver(new NamespaceResolver());
packageToNamespaceMappings.put(packageName, packageNamespace);
}
return packageNamespace;
}
private void checkForCallbackMethods() {
for (JavaClass next : typeInfoClasses) {
if (next == null) {
continue;
}
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 };
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 HashMap<String, MarshalCallback> getMarshalCallbacks() {
return this.marshalCallbacks;
}
public HashMap<String, UnmarshalCallback> getUnmarshalCallbacks() {
return this.unmarshalCallbacks;
}
public JavaClass[] processObjectFactory(JavaClass objectFactoryClass, ArrayList<JavaClass> classes) {
Collection methods = objectFactoryClass.getDeclaredMethods();
Iterator methodsIter = methods.iterator();
NamespaceInfo namespaceInfo = getNamespaceInfoForPackage(objectFactoryClass);
while (methodsIter.hasNext()) {
JavaMethod next = (JavaMethod) methodsIter.next();
if (next.getName().startsWith("create")) {
JavaClass type = next.getReturnType();
if (type.getName().equals("javax.xml.bind.JAXBElement")) {
type = (JavaClass) next.getReturnType().getActualTypeArguments().toArray()[0];
} else {
this.factoryMethods.put(next.getReturnType().getRawName(), next);
}
if (helper.isAnnotationPresent(next, XmlElementDecl.class)) {
XmlElementDecl elementDecl = (XmlElementDecl) helper.getAnnotation(next, XmlElementDecl.class);
String url = elementDecl.namespace();
Class scopeClass = elementDecl.scope();
if(XMLConstants.EMPTY_STRING.equals(url)) {
isDefaultNamespaceAllowed = false;
}
if ("##default".equals(url)) {
url = namespaceInfo.getNamespace();
}
String localName = elementDecl.name();
QName qname = new QName(url, localName);
boolean isList = false;
if ("java.util.List".equals(type.getName())) {
isList = true;
if (type.hasActualTypeArguments()) {
type = (JavaClass) type.getActualTypeArguments().toArray()[0];
}
}
ElementDeclaration declaration = new ElementDeclaration(qname, type, type.getQualifiedName(), isList, elementDecl.scope());
if (!elementDecl.substitutionHeadName().equals("")) {
String subHeadLocal = elementDecl.substitutionHeadName();
String subHeadNamespace = elementDecl.substitutionHeadNamespace();
if (subHeadNamespace.equals("##default")) {
subHeadNamespace = namespaceInfo.getNamespace();
}
declaration.setSubstitutionHead(new QName(subHeadNamespace, subHeadLocal));
}
if (!(elementDecl.defaultValue().length() == 1 && elementDecl.defaultValue().startsWith("\u0000"))) {
declaration.setDefaultValue(elementDecl.defaultValue());
}
if (helper.isAnnotationPresent(next, XmlJavaTypeAdapter.class)) {
XmlJavaTypeAdapter typeAdapter = (XmlJavaTypeAdapter) helper.getAnnotation(next, XmlJavaTypeAdapter.class);
Class typeAdapterClass = typeAdapter.value();
declaration.setJavaTypeAdapterClass(typeAdapterClass);
Method[] tacMethods = typeAdapterClass.getMethods();
Class declJavaType = null;
for (int i = 0; i < tacMethods.length; i++) {
Method method = tacMethods[i];
if (method.getName().equals("marshal")) {
declJavaType = method.getReturnType();
break;
}
}
declaration.setJavaType(helper.getJavaClass(declJavaType));
declaration.setAdaptedJavaType(type);
}
HashMap<QName, ElementDeclaration> elements = getElementDeclarationsForScope(scopeClass.getName());
if(elements == null) {
elements = new HashMap<QName, ElementDeclaration>();
this.elementDeclarations.put(scopeClass.getName(), elements);
}
elements.put(qname, declaration);
}
if (!helper.isBuiltInJavaType(type) && !classes.contains(type)) {
classes.add(type);
}
}
}
if (classes.size() > 0) {
return classes.toArray(new JavaClass[classes.size()]);
} else {
return new JavaClass[0];
}
}
/**
* Lazy load and return the map of global elements.
*
* @return
*/
public HashMap<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 = typeInfo.get(javaClass.getQualifiedName());
if (info == null) {
continue;
}
if (!info.isTransient() && info.isSetXmlRootElement()) {
org.eclipse.persistence.jaxb.xmlmodel.XmlRootElement xmlRE = info.getXmlRootElement();
NamespaceInfo namespaceInfo;
namespaceInfo = getNamespaceInfoForPackage(javaClass);
String elementName = xmlRE.getName();
if (elementName.equals("##default") || elementName.equals("")) {
if (javaClass.getName().indexOf("$") != -1) {
elementName = Introspector.decapitalize(javaClass.getName().substring(javaClass.getName().lastIndexOf('$') + 1));
} else {
elementName = Introspector.decapitalize(javaClass.getName().substring(javaClass.getName().lastIndexOf('.') + 1));
}
// TCK Compliancy
if (elementName.length() >= 3) {
int idx = elementName.length() - 1;
char ch = elementName.charAt(idx - 1);
if (Character.isDigit(ch)) {
char lastCh = Character.toUpperCase(elementName.charAt(idx));
elementName = elementName.substring(0, idx) + lastCh;
}
}
}
String rootNamespace = xmlRE.getNamespace();
QName rootElemName = null;
if (rootNamespace.equals("##default")) {
if (namespaceInfo == null) {
rootElemName = new QName(elementName);
} else {
String rootNS = namespaceInfo.getNamespace();
rootElemName = new QName(rootNS, elementName);
if(rootNS.equals(XMLConstants.EMPTY_STRING)){
isDefaultNamespaceAllowed = false;
}
}
} else {
rootElemName = new QName(rootNamespace, elementName);
if(rootNamespace.equals(XMLConstants.EMPTY_STRING)){
isDefaultNamespaceAllowed = false;
}
}
ElementDeclaration declaration = new ElementDeclaration(rootElemName, javaClass, javaClass.getQualifiedName(), false);
declaration.setIsXmlRootElement(true);
this.getGlobalElements().put(rootElemName, declaration);
this.xmlRootElements.put(javaClass.getQualifiedName(), declaration);
}
}
Iterator<QName> elementQnames = this.getGlobalElements().keySet().iterator();
while (elementQnames.hasNext()) {
QName next = elementQnames.next();
ElementDeclaration nextDeclaration = this.getGlobalElements().get(next);
QName substitutionHead = nextDeclaration.getSubstitutionHead();
while(substitutionHead != null) {
ElementDeclaration rootDeclaration = this.getGlobalElements().get(substitutionHead);
rootDeclaration.addSubstitutableElement(nextDeclaration);
substitutionHead = rootDeclaration.getSubstitutionHead();
}
}
}
private void addReferencedElement(ReferenceProperty property, ElementDeclaration referencedElement) {
property.addReferencedElement(referencedElement);
if (referencedElement.getSubstitutableElements() != null && referencedElement.getSubstitutableElements().size() > 0) {
for (ElementDeclaration substitutable : referencedElement.getSubstitutableElements()) {
addReferencedElement(property, substitutable);
}
}
}
/**
* Returns true if the field or method passed in is annotated with JAXB annotations.
*/
private boolean hasJAXBAnnotations(JavaHasAnnotations elem) {
if (helper.isAnnotationPresent(elem, XmlElement.class)
|| helper.isAnnotationPresent(elem, XmlAttribute.class)
|| helper.isAnnotationPresent(elem, XmlAnyElement.class)
|| helper.isAnnotationPresent(elem, XmlAnyAttribute.class)
|| helper.isAnnotationPresent(elem, XmlValue.class)
|| helper.isAnnotationPresent(elem, XmlElements.class)
|| helper.isAnnotationPresent(elem, XmlElementRef.class)
|| helper.isAnnotationPresent(elem, XmlElementRefs.class)
|| helper.isAnnotationPresent(elem, XmlID.class)
|| helper.isAnnotationPresent(elem, XmlSchemaType.class)
|| helper.isAnnotationPresent(elem, XmlElementWrapper.class)
|| helper.isAnnotationPresent(elem, XmlList.class)
|| helper.isAnnotationPresent(elem, XmlMimeType.class)
|| helper.isAnnotationPresent(elem, XmlIDREF.class)) {
return true;
}
return false;
}
private void validateElementIsInPropOrder(TypeInfo info, String name) {
if (info.isTransient()) {
return;
}
// If a property is marked with XMLElement, XMLElements, XMLElementRef or XMLElementRefs
// and propOrder is not empty then it must be in the proporder list
String[] propOrder = info.getPropOrder();
if (propOrder.length > 0) {
if (propOrder.length == 1 && propOrder[0].equals("")) {
return;
}
List<String> propOrderList = Arrays.asList(info.getPropOrder());
if (!propOrderList.contains(name)) {
throw JAXBException.missingPropertyInPropOrder(name);
}
}
}
private void validatePropOrderForInfo(TypeInfo info) {
if (info.isTransient()) {
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("") && !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"))) {
TypeInfo parentTypeInfo = typeInfo.get(parent.getQualifiedName());
if (parentTypeInfo != null || (parentTypeInfo == null && shouldGenerateTypeInfo(parent))) {
throw JAXBException.propertyOrFieldCannotBeXmlValue(propName);
}
parent = parent.getSuperclass();
}
QName schemaQName = getSchemaTypeOrNullFor(ptype);
if (schemaQName == null) {
TypeInfo refInfo = typeInfo.get(ptype.getQualifiedName());
if (refInfo != null) {
if (!refInfo.isPostBuilt()) {
postBuildTypeInfo(new JavaClass[] { ptype });
}
} else if (shouldGenerateTypeInfo(ptype)) {
JavaClass[] jClasses = new JavaClass[] { ptype };
buildNewTypeInfo(jClasses);
refInfo = typeInfo.get(ptype.getQualifiedName());
}
if (refInfo != null && !refInfo.isEnumerationType() && refInfo.getXmlValueProperty() == null) {
throw JAXBException.invalidTypeForXmlValueField(propName);
}
}
}
public boolean isMapType(JavaClass type) {
return helper.getJavaClass(java.util.Map.class).isAssignableFrom(type);
}
private Class generateWrapperForMapClass(JavaClass mapClass, JavaClass keyClass, JavaClass valueClass, TypeMappingInfo typeMappingInfo) {
String packageName = "jaxb.dev.java.net";
NamespaceResolver combinedNamespaceResolver = new NamespaceResolver();
if (!helper.isBuiltInJavaType(keyClass)) {
String keyPackageName = keyClass.getPackageName();
packageName = packageName + "." + keyPackageName;
NamespaceInfo keyNamespaceInfo = getNamespaceInfoForPackage(keyClass);
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 + "." + valuePackageName;
NamespaceInfo valueNamespaceInfo = getNamespaceInfoForPackage(valueClass);
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 = "";
}
NamespaceInfo namespaceInfo = packageToNamespaceMappings.get(mapClass.getPackageName());
if(namespaceInfo == null) {
namespaceInfo = getPackageToNamespaceMappings().get(packageName);
} else {
if(namespaceInfo.getNamespace() != null) {
namespace = namespaceInfo.getNamespace();
}
getPackageToNamespaceMappings().put(packageName, namespaceInfo);
}
if(namespaceInfo == null) {
namespaceInfo = new NamespaceInfo();
namespaceInfo.setNamespace(namespace);
namespaceInfo.setNamespaceResolver(combinedNamespaceResolver);
getPackageToNamespaceMappings().put(packageName, namespaceInfo);
}
int beginIndex = keyClass.getName().lastIndexOf(".")+1;
String keyName = keyClass.getName().substring(beginIndex);
int dollarIndex = keyName.indexOf('$');
if(dollarIndex > -1){
keyName = keyName.substring(dollarIndex + 1);
}
beginIndex = valueClass.getName().lastIndexOf(".")+1;
String valueName = valueClass.getName().substring(beginIndex);
dollarIndex = valueName.indexOf('$');
if(dollarIndex > -1){
valueName = valueName.substring(dollarIndex + 1);
}
String collectionClassShortName = mapClass.getRawName().substring(mapClass.getRawName().lastIndexOf('.') + 1);
String suggestedClassName = keyName + valueName + collectionClassShortName;
String qualifiedClassName = packageName + "." + suggestedClassName;
qualifiedClassName = getNextAvailableClassName(qualifiedClassName);
String qualifiedInternalClassName = qualifiedClassName.replace('.', '/');
String internalKeyName = keyClass.getQualifiedName().replace('.', '/');
String internalValueName = valueClass.getQualifiedName().replace('.', '/');
Type mapType = Type.getType("L" + mapClass.getRawName().replace('.', '/') + ";");
ClassWriter cw = new ClassWriter(false);
CodeVisitor cv;
cw.visit(Constants.V1_5, Constants.ACC_PUBLIC + Constants.ACC_SUPER, qualifiedInternalClassName, "org/eclipse/persistence/internal/jaxb/many/MapValue", null, "StringEmployeeMap.java");
// FIELD ATTRIBUTES
RuntimeVisibleAnnotations fieldAttrs1 = new RuntimeVisibleAnnotations();
if (typeMappingInfo != null) {
java.lang.annotation.Annotation[] annotations = typeMappingInfo.getAnnotations();
if (annotations != null) {
for (int i = 0; i < annotations.length; i++) {
java.lang.annotation.Annotation nextAnnotation = annotations[i];
if (nextAnnotation != null && !(nextAnnotation instanceof XmlElement) && !(nextAnnotation instanceof XmlJavaTypeAdapter)) {
String annotationClassName = nextAnnotation.annotationType().getName();
Annotation fieldAttrs1ann0 = new Annotation("L" + annotationClassName.replace('.', '/') + ";");
fieldAttrs1.annotations.add(fieldAttrs1ann0);
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('.', '/') + ";");
nextValue = nextType;
}
fieldAttrs1ann0.add(next.getName(), nextValue);
} catch(InvocationTargetException ex) {
//ignore the invocation target exception here.
} catch(IllegalAccessException ex) {
}
}
}
}
}
}
// FIELD ATTRIBUTES
SignatureAttribute fieldAttrs2 = new SignatureAttribute("L" + mapType.getInternalName() + "<L" + internalKeyName + ";L" + internalValueName + ";>;");
fieldAttrs1.next = fieldAttrs2;
cw.visitField(Constants.ACC_PUBLIC, "entry", "L"+ mapType.getInternalName()+ ";", null, fieldAttrs1);
cv = cw.visitMethod(Constants.ACC_PUBLIC, "<init>", "()V", null, null);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitMethodInsn(Constants.INVOKESPECIAL, "org/eclipse/persistence/internal/jaxb/many/MapValue", "<init>", "()V");
cv.visitInsn(Constants.RETURN);
cv.visitMaxs(1, 1);
// METHOD ATTRIBUTES
RuntimeVisibleAnnotations methodAttrs1 = new RuntimeVisibleAnnotations();
Annotation methodAttrs1ann0 = new Annotation("Ljavax/xml/bind/annotation/XmlTransient;");
methodAttrs1.annotations.add(methodAttrs1ann0);
SignatureAttribute methodAttrs2 = new SignatureAttribute("(L"+ mapType.getInternalName()+ "<L" + internalKeyName + ";L" + internalValueName + ";>;)V");
methodAttrs1.next = methodAttrs2;
cv = cw.visitMethod(Constants.ACC_PUBLIC, "setItem", "(L"+ mapType.getInternalName()+ ";)V", null, methodAttrs1);
Label l0 = new Label();
cv.visitLabel(l0);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitVarInsn(Constants.ALOAD, 1);
cv.visitFieldInsn(Constants.PUTFIELD, qualifiedInternalClassName, "entry", "L"+ mapType.getInternalName()+ ";");
cv.visitInsn(Constants.RETURN);
Label l1 = new Label();
cv.visitLabel(l1);
// CODE ATTRIBUTE
LocalVariableTypeTableAttribute cvAttr = new LocalVariableTypeTableAttribute();
cv.visitAttribute(cvAttr);
cv.visitMaxs(2, 2);
// METHOD ATTRIBUTES
methodAttrs1 = new RuntimeVisibleAnnotations();
methodAttrs1ann0 = new Annotation("Ljavax/xml/bind/annotation/XmlTransient;");
methodAttrs1.annotations.add(methodAttrs1ann0);
methodAttrs2 = new SignatureAttribute("()L"+ mapType.getInternalName()+ "<L" + internalKeyName + ";L" + internalValueName + ";>;");
methodAttrs1.next = methodAttrs2;
cv = cw.visitMethod(Constants.ACC_PUBLIC, "getItem", "()L"+ mapType.getInternalName()+ ";", null, methodAttrs1);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitFieldInsn(Constants.GETFIELD, qualifiedInternalClassName, "entry", "L"+ mapType.getInternalName()+ ";");
cv.visitInsn(Constants.ARETURN);
cv.visitMaxs(1, 1);
cv = cw.visitMethod(Constants.ACC_PUBLIC + Constants.ACC_BRIDGE + Constants.ACC_SYNTHETIC, "getItem", "()Ljava/lang/Object;", null, null);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitMethodInsn(Constants.INVOKEVIRTUAL, qualifiedInternalClassName, "getItem", "()L"+ mapType.getInternalName()+ ";");
cv.visitInsn(Constants.ARETURN);
cv.visitMaxs(1, 1);
cv = cw.visitMethod(Constants.ACC_PUBLIC + Constants.ACC_BRIDGE + Constants.ACC_SYNTHETIC, "setItem", "(Ljava/lang/Object;)V", null, null);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitVarInsn(Constants.ALOAD, 1);
cv.visitTypeInsn(Constants.CHECKCAST, mapType.getInternalName());
cv.visitMethodInsn(Constants.INVOKEVIRTUAL, qualifiedInternalClassName, "setItem", "(L"+ mapType.getInternalName()+ ";)V");
cv.visitInsn(Constants.RETURN);
cv.visitMaxs(2, 2);
// CLASS ATTRIBUTE
RuntimeVisibleAnnotations annotationsAttr = new RuntimeVisibleAnnotations();
Annotation attrann0 = new Annotation("Ljavax/xml/bind/annotation/XmlType;");
attrann0.add( "namespace", namespace);
annotationsAttr.annotations.add( attrann0);
cw.visitAttribute(annotationsAttr);
SignatureAttribute attr = new SignatureAttribute("Lorg/eclipse/persistence/internal/jaxb/many/MapValue<L"+ mapType.getInternalName()+ "<L" + internalKeyName + ";L" + internalValueName + ";>;>;");
cw.visitAttribute(attr);
cw.visitEnd();
byte[] classBytes = cw.toByteArray();
return generateClassFromBytes(qualifiedClassName, classBytes);
}
private Class generateWrapperForArrayClass(JavaClass arrayClass, TypeMappingInfo typeMappingInfo, Class xmlElementType) {
JavaClass componentClass = null;
if(typeMappingInfo != null && xmlElementType != null){
componentClass = helper.getJavaClass(xmlElementType);
}else{
componentClass = arrayClass.getComponentType();
}
if (componentClass.isPrimitive()) {
return generatePrimitiveArrayValue(arrayClass, componentClass, typeMappingInfo);
} else {
return generateObjectArrayValue(arrayClass, componentClass, typeMappingInfo);
}
}
private Class generatePrimitiveArrayValue(JavaClass arrayClass, JavaClass componentClass, TypeMappingInfo typeMappingInfo) {
String packageName = "jaxb.dev.java.net.array";
NamespaceInfo namespaceInfo = getNamespaceInfoForPackage(packageName);
if (namespaceInfo == null) {
namespaceInfo = new NamespaceInfo();
namespaceInfo.setNamespace("http://jaxb.dev.java.net/array");
namespaceInfo.setNamespaceResolver(new NamespaceResolver());
getPackageToNamespaceMappings().put(packageName, namespaceInfo);
}
int beginIndex = componentClass.getName().lastIndexOf(".") + 1;
String name = componentClass.getName().substring(beginIndex);
String suggestedClassName = name + "Array";
String qualifiedClassName = packageName + "." + suggestedClassName;
qualifiedClassName = getNextAvailableClassName(qualifiedClassName);
String className = qualifiedClassName.substring(qualifiedClassName.lastIndexOf('.') + 1);
String primitiveClassName = componentClass.getRawName();
Class primitiveClass = getPrimitiveClass(primitiveClassName);
componentClass = helper.getJavaClass(getObjectClass(primitiveClass));
String qualifiedInternalClassName = qualifiedClassName.replace('.', '/');
Type componentType = Type.getType("L" + componentClass.getRawName().replace('.', '/') + ";");
ClassWriter cw = new ClassWriter(false);
CodeVisitor cv;
cw.visit(Constants.V1_5, Constants.ACC_PUBLIC + Constants.ACC_SUPER, qualifiedInternalClassName, "org/eclipse/persistence/internal/jaxb/many/PrimitiveArrayValue", null, className.replace(".", "/") + ".java");
// FIELD ATTRIBUTES
RuntimeVisibleAnnotations fieldAttrs1 = new RuntimeVisibleAnnotations();
if(typeMappingInfo != null){
java.lang.annotation.Annotation[] annotations = getAnnotations(typeMappingInfo);
if(annotations != null){
for(int i=0; i<annotations.length; i++){
java.lang.annotation.Annotation nextAnnotation = annotations[i];
if (nextAnnotation != null && !(nextAnnotation instanceof XmlElement) && !(nextAnnotation instanceof XmlJavaTypeAdapter)) {
String annotationClassName = nextAnnotation.getClass().getName();
Annotation fieldAttrs1ann0 = new Annotation("L"+annotationClassName+";");
fieldAttrs1.annotations.add(fieldAttrs1ann0);
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('.', '/') + ";");
nextValue = nextType;
}
fieldAttrs1ann0.add(next.getName(), nextValue);
} catch(InvocationTargetException ex) {
//ignore the invocation target exception here.
} catch(IllegalAccessException ex) {
}
}
}
}
}
}
SignatureAttribute fieldAttrs2 = new SignatureAttribute("Ljava/util/Collection<L" + componentType.getInternalName() + ";>;");
fieldAttrs1.next = fieldAttrs2;
cw.visitField(Constants.ACC_PUBLIC, "item", "Ljava/util/Collection;", null, fieldAttrs1);
cv = cw.visitMethod(Constants.ACC_PUBLIC, "<init>", "()V", null, null);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitMethodInsn(Constants.INVOKESPECIAL, "org/eclipse/persistence/internal/jaxb/many/PrimitiveArrayValue", "<init>", "()V");
cv.visitInsn(Constants.RETURN);
cv.visitMaxs(1, 1);
// METHOD ATTRIBUTES
RuntimeVisibleAnnotations methodAttrs1 = new RuntimeVisibleAnnotations();
Annotation methodAttrs1ann0 = new Annotation("Ljavax/xml/bind/annotation/XmlTransient;");
methodAttrs1.annotations.add(methodAttrs1ann0);
cv = cw.visitMethod(Constants.ACC_PUBLIC, "getItem", "()Ljava/lang/Object;", null, methodAttrs1);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitFieldInsn(Constants.GETFIELD, qualifiedInternalClassName, "item", "Ljava/util/Collection;");
cv.visitMethodInsn(Constants.INVOKEINTERFACE, "java/util/Collection", "iterator", "()Ljava/util/Iterator;");
cv.visitVarInsn(Constants.ASTORE, 1);
Label l0 = new Label();
cv.visitLabel(l0);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitFieldInsn(Constants.GETFIELD, qualifiedInternalClassName, "item", "Ljava/util/Collection;");
cv.visitMethodInsn(Constants.INVOKEINTERFACE, "java/util/Collection", "size", "()I");
cv.visitIntInsn(Constants.NEWARRAY, getNewArrayConstantForPrimitive(primitiveClassName));
cv.visitVarInsn(Constants.ASTORE, 2);
cv.visitInsn(Constants.ICONST_0);
cv.visitVarInsn(Constants.ISTORE, 3);
Label l1 = new Label();
cv.visitJumpInsn(Constants.GOTO, l1);
Label l2 = new Label();
cv.visitLabel(l2);
cv.visitVarInsn(Constants.ALOAD, 2);
cv.visitVarInsn(Constants.ILOAD, 3);
cv.visitVarInsn(Constants.ALOAD, 1);
cv.visitMethodInsn(Constants.INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
cv.visitTypeInsn(Constants.CHECKCAST, componentType.getInternalName());
cv.visitMethodInsn(Constants.INVOKEVIRTUAL, componentType.getInternalName(), getToPrimitiveStringForObjectClass(primitiveClassName), getReturnTypeFor(primitiveClass));
int iaStoreOpcode = Type.getType(primitiveClass).getOpcode(Constants.IASTORE);
cv.visitInsn(iaStoreOpcode);
cv.visitIincInsn(3, 1);
cv.visitLabel(l1);
cv.visitVarInsn(Constants.ILOAD, 3);
cv.visitVarInsn(Constants.ALOAD, 2);
cv.visitInsn(Constants.ARRAYLENGTH);
cv.visitJumpInsn(Constants.IF_ICMPLT, l2);
cv.visitVarInsn(Constants.ALOAD, 2);
cv.visitInsn(Constants.ARETURN);
Label l3 = new Label();
cv.visitLabel(l3);
// CODE ATTRIBUTE
LocalVariableTypeTableAttribute cvAttr = new LocalVariableTypeTableAttribute();
cv.visitAttribute(cvAttr);
cv.visitMaxs(3, 4);
// METHOD ATTRIBUTES
methodAttrs1 = new RuntimeVisibleAnnotations();
methodAttrs1ann0 = new Annotation("Ljavax/xml/bind/annotation/XmlTransient;");
methodAttrs1.annotations.add(methodAttrs1ann0);
cv = cw.visitMethod(Constants.ACC_PUBLIC, "setItem", "(Ljava/lang/Object;)V", null, methodAttrs1);
cv.visitTypeInsn(Constants.NEW, "java/util/ArrayList");
cv.visitInsn(Constants.DUP);
cv.visitMethodInsn(Constants.INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V");
cv.visitVarInsn(Constants.ASTORE, 2);
l0 = new Label();
cv.visitLabel(l0);
cv.visitVarInsn(Constants.ALOAD, 1);
cv.visitTypeInsn(Constants.CHECKCAST, getCastTypeFor(primitiveClass));
cv.visitVarInsn(Constants.ASTORE, 3);
cv.visitInsn(Constants.ICONST_0);
cv.visitVarInsn(Constants.ISTORE, 4);
l1 = new Label();
cv.visitJumpInsn(Constants.GOTO, l1);
l2 = new Label();
cv.visitLabel(l2);
cv.visitVarInsn(Constants.ALOAD, 3);
cv.visitVarInsn(Constants.ILOAD, 4);
int iaLoadOpcode = Type.getType(primitiveClass).getOpcode(Constants.IALOAD);
cv.visitInsn(iaLoadOpcode);
cv.visitVarInsn(Constants.ISTORE, 5);
cv.visitVarInsn(Constants.ALOAD, 2);
cv.visitTypeInsn(Constants.NEW, componentType.getInternalName());
cv.visitInsn(Constants.DUP);
cv.visitVarInsn(Constants.ILOAD, 5);
cv.visitMethodInsn(Constants.INVOKESPECIAL, componentType.getInternalName(), "<init>", "(" + getShortNameForPrimitive(primitiveClass) + ")V");
cv.visitMethodInsn(Constants.INVOKEVIRTUAL, "java/util/ArrayList", "add", "(Ljava/lang/Object;)Z");
cv.visitInsn(Constants.POP);
cv.visitIincInsn(4, 1);
cv.visitLabel(l1);
cv.visitVarInsn(Constants.ILOAD, 4);
cv.visitVarInsn(Constants.ALOAD, 3);
cv.visitInsn(Constants.ARRAYLENGTH);
cv.visitJumpInsn(Constants.IF_ICMPLT, l2);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitVarInsn(Constants.ALOAD, 2);
cv.visitFieldInsn(Constants.PUTFIELD, qualifiedInternalClassName, "item", "Ljava/util/Collection;");
cv.visitInsn(Constants.RETURN);
l3 = new Label();
cv.visitLabel(l3);
// CODE ATTRIBUTE
cv.visitAttribute(cvAttr);
cv.visitMaxs(4, 6);
cw.visitEnd();
byte[] classBytes = cw.toByteArray();
return generateClassFromBytes(qualifiedClassName, classBytes);
}
private Class generateObjectArrayValue(JavaClass arrayClass, JavaClass componentClass, TypeMappingInfo typeMappingInfo) {
String packageName = componentClass.getPackageName();
packageName = "jaxb.dev.java.net.array." + packageName;
if (helper.isBuiltInJavaType(componentClass)) {
NamespaceInfo namespaceInfo = getPackageToNamespaceMappings().get(packageName);
if (namespaceInfo == null) {
namespaceInfo = new NamespaceInfo();
namespaceInfo.setNamespace("http://jaxb.dev.java.net/array");
namespaceInfo.setNamespaceResolver(new NamespaceResolver());
getPackageToNamespaceMappings().put(packageName, namespaceInfo);
}
} else {
NamespaceInfo namespaceInfo = getNamespaceInfoForPackage(componentClass.getPackage());
getPackageToNamespaceMappings().put(packageName, namespaceInfo);
}
String name = componentClass.getName();
int dollarIndex = name.indexOf('$');
if(dollarIndex > -1){
name = name.substring(dollarIndex + 1);
}
int beginIndex = name.lastIndexOf(".") + 1;
String suggestedClassName = name.substring(beginIndex) + "Array";
String qualifiedClassName = packageName + "." + suggestedClassName;
qualifiedClassName = getNextAvailableClassName(qualifiedClassName);
String className = qualifiedClassName.substring(qualifiedClassName.lastIndexOf('.') + 1);
String qualifiedInternalClassName = qualifiedClassName.replace('.', '/');
Type componentType = Type.getType("L" + componentClass.getQualifiedName().replace('.', '/') + ";");
ClassWriter cw = new ClassWriter(false);
CodeVisitor cv;
cw.visit(Constants.V1_5, Constants.ACC_PUBLIC + Constants.ACC_SUPER, qualifiedInternalClassName, "org/eclipse/persistence/internal/jaxb/many/ObjectArrayValue", null, className.replace('.', '/') + ".java");
// FIELD ATTRIBUTES
RuntimeVisibleAnnotations fieldAttrs1 = new RuntimeVisibleAnnotations();
if(typeMappingInfo != null){
java.lang.annotation.Annotation[] annotations = getAnnotations(typeMappingInfo);
if(annotations != null){
for(int i=0; i<annotations.length; i++){
java.lang.annotation.Annotation nextAnnotation = annotations[i];
if (nextAnnotation != null && !(nextAnnotation instanceof XmlElement) && !(nextAnnotation instanceof XmlJavaTypeAdapter)) {
String annotationClassName = nextAnnotation.getClass().getName();
Annotation fieldAttrs1ann0 = new Annotation("L"+annotationClassName+";");
fieldAttrs1.annotations.add(fieldAttrs1ann0);
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('.', '/') + ";");
nextValue = nextType;
}
fieldAttrs1ann0.add(next.getName(), nextValue);
} catch(InvocationTargetException ex) {
//ignore the invocation target exception here.
} catch(IllegalAccessException ex) {
}
}
}
}
}
}
SignatureAttribute fieldAttrs2 = new SignatureAttribute("Ljava/util/Collection<L" + componentType.getInternalName() + ";>;");
fieldAttrs1.next = fieldAttrs2;
cw.visitField(Constants.ACC_PUBLIC, "item", "Ljava/util/Collection;", null, fieldAttrs1);
cv = cw.visitMethod(Constants.ACC_PUBLIC, "<init>", "()V", null, null);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitMethodInsn(Constants.INVOKESPECIAL, "org/eclipse/persistence/internal/jaxb/many/ObjectArrayValue", "<init>", "()V");
cv.visitInsn(Constants.RETURN);
cv.visitMaxs(1, 1);
// METHOD ATTRIBUTES
RuntimeVisibleAnnotations methodAttrs1 = new RuntimeVisibleAnnotations();
Annotation methodAttrs1ann0 = new Annotation("Ljavax/xml/bind/annotation/XmlTransient;");
methodAttrs1.annotations.add(methodAttrs1ann0);
cv = cw.visitMethod(Constants.ACC_PUBLIC, "getItem", "()[Ljava/lang/Object;", null, methodAttrs1);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitFieldInsn(Constants.GETFIELD, qualifiedInternalClassName, "item", "Ljava/util/Collection;");
cv.visitMethodInsn(Constants.INVOKEVIRTUAL, qualifiedInternalClassName, "convertCollectionToArray", "(Ljava/util/Collection;)[Ljava/lang/Object;");
cv.visitInsn(Constants.ARETURN);
cv.visitMaxs(2, 1);
// METHOD ATTRIBUTES
methodAttrs1 = new RuntimeVisibleAnnotations();
methodAttrs1ann0 = new Annotation("Ljavax/xml/bind/annotation/XmlTransient;");
methodAttrs1.annotations.add(methodAttrs1ann0);
cv = cw.visitMethod(Constants.ACC_PUBLIC, "setItem", "([Ljava/lang/Object;)V", null, methodAttrs1);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitVarInsn(Constants.ALOAD, 1);
cv.visitMethodInsn(Constants.INVOKEVIRTUAL, qualifiedInternalClassName, "convertArrayToList", "([Ljava/lang/Object;)Ljava/util/List;");
cv.visitFieldInsn(Constants.PUTFIELD, qualifiedInternalClassName, "item", "Ljava/util/Collection;");
cv.visitInsn(Constants.RETURN);
cv.visitMaxs(3, 2);
// METHOD ATTRIBUTES
methodAttrs1 = new RuntimeVisibleAnnotations();
methodAttrs1ann0 = new Annotation("Ljavax/xml/bind/annotation/XmlTransient;");
methodAttrs1.annotations.add(methodAttrs1ann0);
cv = cw.visitMethod(Constants.ACC_PUBLIC, "getComponentClass", "()Ljava/lang/Class;", null, methodAttrs1);
cv.visitLdcInsn(componentType);
cv.visitInsn(Constants.ARETURN);
cv.visitMaxs(1, 1);
cv = cw.visitMethod(Constants.ACC_PUBLIC + Constants.ACC_BRIDGE + Constants.ACC_SYNTHETIC, "getItem", "()Ljava/lang/Object;", null, null);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitMethodInsn(Constants.INVOKEVIRTUAL, qualifiedInternalClassName, "getItem", "()[Ljava/lang/Object;");
cv.visitInsn(Constants.ARETURN);
cv.visitMaxs(1, 1);
cv = cw.visitMethod(Constants.ACC_PUBLIC + Constants.ACC_BRIDGE + Constants.ACC_SYNTHETIC, "setItem", "(Ljava/lang/Object;)V", null, null);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitVarInsn(Constants.ALOAD, 1);
cv.visitTypeInsn(Constants.CHECKCAST, "[Ljava/lang/Object;");
cv.visitMethodInsn(Constants.INVOKEVIRTUAL, qualifiedInternalClassName, "setItem", "([Ljava/lang/Object;)V");
cv.visitInsn(Constants.RETURN);
cv.visitMaxs(2, 2);
// CLASS ATRIBUTE
SignatureAttribute attr = new SignatureAttribute("Lorg/eclipse/persistence/internal/jaxb/many/ObjectArrayValue<[Ljava/lang/Object;>;");
cw.visitAttribute(attr);
cw.visitEnd();
byte[] classBytes = cw.toByteArray();
return generateClassFromBytes(qualifiedClassName, classBytes);
}
private Class generateCollectionValue(JavaClass collectionClass, TypeMappingInfo typeMappingInfo, Class xmlElementType) {
JavaClass componentClass;
if(typeMappingInfo != null && xmlElementType != null){
componentClass = helper.getJavaClass(xmlElementType);
}else if (collectionClass.hasActualTypeArguments()) {
componentClass = ((JavaClass) collectionClass.getActualTypeArguments().toArray()[0]);
} else {
componentClass = helper.getJavaClass(Object.class);
}
if(componentClass.isPrimitive()){
Class primitiveClass = getPrimitiveClass(componentClass.getRawName());
componentClass = helper.getJavaClass(getObjectClass(primitiveClass));
}
NamespaceInfo namespaceInfo = packageToNamespaceMappings.get(collectionClass.getPackageName());
String namespace = "";
if(this.defaultTargetNamespace != null) {
namespace = this.defaultTargetNamespace;
}
NamespaceInfo componentNamespaceInfo = getNamespaceInfoForPackage(componentClass);
String packageName = componentClass.getPackageName();
packageName = "jaxb.dev.java.net." + packageName;
if(namespaceInfo == null) {
namespaceInfo = getPackageToNamespaceMappings().get(packageName);
} else {
getPackageToNamespaceMappings().put(packageName, namespaceInfo);
if(namespaceInfo.getNamespace() != null) {
namespace = namespaceInfo.getNamespace();
}
}
if (namespaceInfo == null) {
if(componentNamespaceInfo != null) {
namespaceInfo = componentNamespaceInfo;
} else {
namespaceInfo = new NamespaceInfo();
namespaceInfo.setNamespaceResolver(new NamespaceResolver());
}
getPackageToNamespaceMappings().put(packageName, namespaceInfo);
}
String name = componentClass.getName();
Type componentType = Type.getType("L" + componentClass.getName().replace('.', '/') + ";");
String componentTypeInternalName = null;
if(name.equals("[B")){
name = "byteArray";
componentTypeInternalName = componentType.getInternalName();
}else if(name.equals("[Ljava.lang.Byte;")){
name = "ByteArray";
componentTypeInternalName = componentType.getInternalName() + ";";
}else{
componentTypeInternalName = "L" + componentType.getInternalName() + ";";
}
int beginIndex = name.lastIndexOf('.') + 1;
name = name.substring(beginIndex);
int dollarIndex = name.indexOf('$');
if(dollarIndex > -1){
name = name.substring(dollarIndex + 1);
}
String collectionClassRawName = collectionClass.getRawName();
String collectionClassShortName = collectionClassRawName.substring(collectionClassRawName.lastIndexOf('.') + 1);
String suggestedClassName = collectionClassShortName + "Of" + name;
String qualifiedClassName = packageName + "." + suggestedClassName;
qualifiedClassName = getNextAvailableClassName(qualifiedClassName);
String className = qualifiedClassName.substring(qualifiedClassName.lastIndexOf('.') + 1);
Type collectionType = Type.getType("L" + collectionClassRawName.replace('.', '/') + ";");
String qualifiedInternalClassName = qualifiedClassName.replace('.', '/');
ClassWriter cw = new ClassWriter(false);
CodeVisitor cv;
cw.visit(Constants.V1_5, Constants.ACC_PUBLIC + Constants.ACC_SUPER, qualifiedInternalClassName, "org/eclipse/persistence/internal/jaxb/many/CollectionValue", null, className.replace('.', '/') + ".java");
// FIELD ATTRIBUTES
RuntimeVisibleAnnotations fieldAttrs1 = new RuntimeVisibleAnnotations();
if(typeMappingInfo != null){
java.lang.annotation.Annotation[] annotations = getAnnotations(typeMappingInfo);
if(annotations != null){
for(int i=0; i<annotations.length; i++){
java.lang.annotation.Annotation nextAnnotation = annotations[i];
if (nextAnnotation != null && !(nextAnnotation instanceof XmlElement) && !(nextAnnotation instanceof XmlJavaTypeAdapter)) {
String annotationClassName = nextAnnotation.annotationType().getName();
Annotation fieldAttrs1ann0 = new Annotation("L" + annotationClassName.replace('.', '/') + ";");
fieldAttrs1.annotations.add(fieldAttrs1ann0);
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('.', '/') + ";");
nextValue = nextType;
}
fieldAttrs1ann0.add(next.getName(), nextValue);
} catch(InvocationTargetException ex) {
//ignore the invocation target exception here.
} catch(IllegalAccessException ex) {
}
}
}
}
}
}
SignatureAttribute fieldAttrs2 = new SignatureAttribute("L" + collectionType.getInternalName() + "<" + componentTypeInternalName + ">;");
fieldAttrs1.next = fieldAttrs2;
cw.visitField(Constants.ACC_PUBLIC, "item", "L"+collectionType.getInternalName()+";", null, fieldAttrs1);
cv = cw.visitMethod(Constants.ACC_PUBLIC, "<init>", "()V", null, null);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitMethodInsn(Constants.INVOKESPECIAL, "org/eclipse/persistence/internal/jaxb/many/CollectionValue", "<init>", "()V");
cv.visitInsn(Constants.RETURN);
cv.visitMaxs(1, 1);
// METHOD ATTRIBUTES
RuntimeVisibleAnnotations methodAttrs1 = new RuntimeVisibleAnnotations();
Annotation methodAttrs1ann0 = new Annotation("Ljavax/xml/bind/annotation/XmlTransient;");
methodAttrs1.annotations.add(methodAttrs1ann0);
SignatureAttribute methodAttrs2 = new SignatureAttribute("(L" + collectionType.getInternalName() + "<" + componentTypeInternalName + ">;)V");
methodAttrs1.next = methodAttrs2;
cv = cw.visitMethod(Constants.ACC_PUBLIC, "setItem", "(L"+collectionType.getInternalName()+";)V", null, methodAttrs1);
Label l0 = new Label();
cv.visitLabel(l0);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitVarInsn(Constants.ALOAD, 1);
cv.visitFieldInsn(Constants.PUTFIELD, qualifiedInternalClassName, "item", "L"+collectionType.getInternalName()+";");
cv.visitInsn(Constants.RETURN);
Label l1 = new Label();
cv.visitLabel(l1);
// CODE ATTRIBUTE
LocalVariableTypeTableAttribute cvAttr = new LocalVariableTypeTableAttribute();
cv.visitAttribute(cvAttr);
cv.visitMaxs(2, 2);
// METHOD ATTRIBUTES
methodAttrs1 = new RuntimeVisibleAnnotations();
methodAttrs1ann0 = new Annotation("Ljavax/xml/bind/annotation/XmlTransient;");
methodAttrs1.annotations.add(methodAttrs1ann0);
methodAttrs2 = new SignatureAttribute("()L" + collectionType.getInternalName() + "<" + componentTypeInternalName + ">;");
methodAttrs1.next = methodAttrs2;
cv = cw.visitMethod(Constants.ACC_PUBLIC, "getItem", "()L"+collectionType.getInternalName()+";", null, methodAttrs1);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitFieldInsn(Constants.GETFIELD, qualifiedInternalClassName, "item", "L"+collectionType.getInternalName()+";");
cv.visitInsn(Constants.ARETURN);
cv.visitMaxs(1, 1);
cv = cw.visitMethod(Constants.ACC_PUBLIC + Constants.ACC_BRIDGE + Constants.ACC_SYNTHETIC, "getItem", "()Ljava/lang/Object;", null, null);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitMethodInsn(Constants.INVOKEVIRTUAL, qualifiedInternalClassName, "getItem", "()L"+collectionType.getInternalName()+";");
cv.visitInsn(Constants.ARETURN);
cv.visitMaxs(1, 1);
cv = cw.visitMethod(Constants.ACC_PUBLIC + Constants.ACC_BRIDGE + Constants.ACC_SYNTHETIC, "setItem", "(Ljava/lang/Object;)V", null, null);
cv.visitVarInsn(Constants.ALOAD, 0);
cv.visitVarInsn(Constants.ALOAD, 1);
cv.visitTypeInsn(Constants.CHECKCAST, ""+collectionType.getInternalName()+"");
cv.visitMethodInsn(Constants.INVOKEVIRTUAL, qualifiedInternalClassName, "setItem", "(L"+collectionType.getInternalName()+";)V");
cv.visitInsn(Constants.RETURN);
cv.visitMaxs(2, 2);
// CLASS ATRIBUTE
// CLASS ATTRIBUTE
RuntimeVisibleAnnotations annotationsAttr = new RuntimeVisibleAnnotations();
Annotation attrann0 = new Annotation("Ljavax/xml/bind/annotation/XmlType;");
attrann0.add( "namespace", namespace);
annotationsAttr.annotations.add( attrann0);
cw.visitAttribute(annotationsAttr);
SignatureAttribute attr = new SignatureAttribute("Lorg/eclipse/persistence/internal/jaxb/many/CollectionValue<L"+collectionType.getInternalName()+"<" + componentTypeInternalName + ">;>;");
cw.visitAttribute(attr);
cw.visitEnd();
byte[] classBytes = cw.toByteArray();
return generateClassFromBytes(qualifiedClassName, classBytes);
}
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.
*
*/
class PropertyComparitor implements Comparator<Property> {
public int compare(Property p1, Property p2) {
return p1.getPropertyName().compareTo(p2.getPropertyName());
}
}
private String getCastTypeFor(Class primitiveClass) {
return "[" + getShortNameForPrimitive(primitiveClass);
}
private String getReturnTypeFor(Class primitiveClass) {
return "()" + getShortNameForPrimitive(primitiveClass);
}
private int getNewArrayConstantForPrimitive(String primitiveClassName) {
if (primitiveClassName == null) {
return 0;
}
if (primitiveClassName == "char") {
return Constants.T_CHAR;
}
if (primitiveClassName == "int") {
return Constants.T_INT;
}
if (primitiveClassName == "double") {
return Constants.T_DOUBLE;
}
if (primitiveClassName == "float") {
return Constants.T_FLOAT;
}
if (primitiveClassName == "long") {
return Constants.T_BOOLEAN;
}
if (primitiveClassName == "short") {
return Constants.T_LONG;
}
if (primitiveClassName == "byte") {
return Constants.T_BYTE;
}
if (primitiveClassName == "boolean") {
return Constants.T_BOOLEAN;
}
return 0;
}
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 = typeMappingInfoToGeneratedClasses.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 String getShortNameForPrimitive(Class primitiveClass) {
Type thePrimitiveType = Type.getType(primitiveClass);
return thePrimitiveType.toString();
}
private String getToPrimitiveStringForObjectClass(String javaClassName) {
if (javaClassName == null) {
return null;
}
if (javaClassName == "char") {
return "charValue";
}
if (javaClassName == "int") {
return "intValue";
}
if (javaClassName == "double") {
return "doubleValue";
}
if (javaClassName == "float") {
return "floatValue";
}
if (javaClassName == "long") {
return "longValue";
}
if (javaClassName == "short") {
return "shortValue";
}
if (javaClassName == "byte") {
return "byteValue";
}
if (javaClassName == "boolean") {
return "booleanValue";
}
return null;
}
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<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>();
ArrayList<JavaClass> jClasses = getTypeInfoClasses();
for (JavaClass jClass : jClasses) {
if (jClass.getPackageName().equals(packageName)) {
String key = jClass.getQualifiedName();
typeInfos.put(key, typeInfo.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 SchemaTypeInfo addClass(JavaClass javaClass) {
if (javaClass == null) {
return null;
} else if (helper.isAnnotationPresent(javaClass, XmlTransient.class)) {
return null;
}
if (typeInfo == null) {
// this is the first class. Initialize all the properties
this.typeInfoClasses = new ArrayList<JavaClass>();
this.typeInfo = new HashMap<String, TypeInfo>();
this.typeQNames = new ArrayList<QName>();
this.userDefinedSchemaTypes = new HashMap<String, QName>();
this.packageToNamespaceMappings = new HashMap<String, NamespaceInfo>();
this.namespaceResolver = new NamespaceResolver();
}
JavaClass[] jClasses = new JavaClass[] { javaClass };
buildNewTypeInfo(jClasses);
TypeInfo info = typeInfo.get(javaClass.getQualifiedName());
NamespaceInfo namespaceInfo;
String packageName = javaClass.getPackageName();
namespaceInfo = this.packageToNamespaceMappings.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("##default") || elementName.equals("")) {
if (javaClass.getName().indexOf("$") != -1) {
elementName = Introspector.decapitalize(javaClass.getName().substring(javaClass.getName().lastIndexOf('$') + 1));
} else {
elementName = Introspector.decapitalize(javaClass.getName().substring(javaClass.getName().lastIndexOf('.') + 1));
}
// TCK Compliancy
if (elementName.length() >= 3) {
int idx = elementName.length() - 1;
char ch = elementName.charAt(idx - 1);
if (Character.isDigit(ch)) {
char lastCh = Character.toUpperCase(elementName.charAt(idx));
elementName = elementName.substring(0, idx) + lastCh;
}
}
}
String rootNamespace = xmlRE.getNamespace();
QName rootElemName = null;
if (rootNamespace.equals("##default")) {
rootElemName = new QName(namespaceInfo.getNamespace(), elementName);
} else {
rootElemName = new QName(rootNamespace, elementName);
}
schemaInfo.getGlobalElementDeclarations().add(rootElemName);
ElementDeclaration declaration = new ElementDeclaration(rootElemName, javaClass, javaClass.getRawName(), false);
this.getGlobalElements().put(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);
postBuildTypeInfo(javaClasses);
}
/**
* 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> getTypeMappingInfoToGeneratedClasses() {
return this.typeMappingInfoToGeneratedClasses;
}
public Map<TypeMappingInfo, Class> getTypeMappingInfoToAdapterClasses() {
return this.typeMappingInfoToAdapterClasses;
}
public Map<TypeMappingInfo, QName> getTypeMappingInfoToSchemaType(){
return this.typeMappingInfoToSchemaType;
}
String getDefaultTargetNamespace() {
return this.defaultTargetNamespace;
}
void setDefaultTargetNamespace(String defaultTargetNamespace) {
this.defaultTargetNamespace = defaultTargetNamespace;
}
public void setDefaultNamespaceAllowed(boolean isDefaultNamespaceAllowed) {
this.isDefaultNamespaceAllowed = isDefaultNamespaceAllowed;
}
HashMap<QName, ElementDeclaration> getElementDeclarationsForScope(String scopeClassName) {
return this.elementDeclarations.get(scopeClassName);
}
}