blob: 8e81fbbeebbf9d628113bd18c180f5e043365ebc [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// Marcel Valovy - 2.6.0 - added case insensitive unmarshalling property,
// added Bean Validation support.
package org.eclipse.persistence.jaxb;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.MarshalException;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.PropertyException;
import jakarta.xml.bind.ValidationEventHandler;
import jakarta.xml.bind.annotation.adapters.XmlAdapter;
import jakarta.xml.bind.attachment.AttachmentMarshaller;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.validation.Schema;
import org.eclipse.persistence.core.queries.CoreAttributeGroup;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.BeanValidationException;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.jaxb.ObjectGraphImpl;
import org.eclipse.persistence.internal.jaxb.WrappedValue;
import org.eclipse.persistence.internal.jaxb.many.ManyValue;
import org.eclipse.persistence.internal.localization.JAXBLocalization;
import org.eclipse.persistence.internal.oxm.Constants;
import org.eclipse.persistence.internal.oxm.Root;
import org.eclipse.persistence.internal.oxm.record.namespaces.MapNamespacePrefixMapper;
import org.eclipse.persistence.internal.oxm.record.namespaces.NamespacePrefixMapperWrapper;
import org.eclipse.persistence.jaxb.JAXBContext.RootLevelXmlAdapter;
import org.eclipse.persistence.jaxb.attachment.AttachmentMarshallerAdapter;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.LogLevel;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.oxm.CharacterEscapeHandler;
import org.eclipse.persistence.oxm.JSONWithPadding;
import org.eclipse.persistence.oxm.MediaType;
import org.eclipse.persistence.oxm.NamespacePrefixMapper;
import org.eclipse.persistence.oxm.XMLMarshalListener;
import org.eclipse.persistence.oxm.XMLMarshaller;
import org.eclipse.persistence.oxm.record.MarshalRecord;
import org.eclipse.persistence.oxm.record.XMLEventWriterRecord;
import org.eclipse.persistence.oxm.record.XMLStreamWriterRecord;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
/**
* INTERNAL:
* <p>
* <b>Purpose:</b>To Provide an implementation of the JAXB 2.0 Marshaller
* Interface
* <p>
* <b>Responsibilities:</b>
* <ul>
* <li>Provide a JAXB wrapper on the XMLMarshaller API</li>
* <li>Perform Object to XML Conversions</li>
* </ul>
* <p>This implementation of the JAXB 2.1/2.2 Marshaller interface provides the
* required functionality by acting as a thin wrapper on the existing
* XMLMarshaller API.</p>
*
* @author mmacivor
* @since Oracle TopLink 11.1.1.0.0
* @see jakarta.xml.bind.Marshaller
* @see org.eclipse.persistence.jaxb.MarshallerProperties
* @see org.eclipse.persistence.oxm.XMLMarshaller
*/
public class JAXBMarshaller implements jakarta.xml.bind.Marshaller {
private JAXBBeanValidator beanValidator;
private BeanValidationMode beanValidationMode;
// The actual type is ValidatorFactory. It's done due to optional nature of jakarta.validation.
private Object prefValidatorFactory;
private boolean bvNoOptimisation = false;
private Class<?>[] beanValidationGroups;
private final XMLMarshaller xmlMarshaller;
private final JAXBContext jaxbContext;
private ValidationEventHandler validationEventHandler;
public static final String XML_JAVATYPE_ADAPTERS = "xml-javatype-adapters";
private static final String SUN_NAMESPACE_PREFIX_MAPPER = "org.glassfish.jaxb.namespacePrefixMapper";
private static final String SUN_JSE_NAMESPACE_PREFIX_MAPPER = "com.sun.xml.internal.bind.namespacePrefixMapper";
private static final String SUN_INDENT_STRING = "org.glassfish.jaxb.indentString";
private static final String SUN_JSE_INDENT_STRING = "com.sun.xml.internal.bind.indentString";
private static final String SUN_CHARACTER_ESCAPE_HANDLER_MARSHALLER = "org.glassfish.jaxb.marshaller.CharacterEscapeHandler";
private static final String SUN_JSE_CHARACTER_ESCAPE_HANDLER_MARSHALLER = "com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler";
private static final String SUN_CHARACTER_ESCAPE_HANDLER= "org.glassfish.jaxb.characterEscapeHandler";
private static final String SUN_JSE_CHARACTER_ESCAPE_HANDLER = "com.sun.xml.internal.bind.characterEscapeHandler";
// XML_DECLARATION is the "opposite" to JAXB_FRAGMENT. If XML_DECLARATION is set to false it means JAXB_FRAGMENT should be set to true.
private static final String XML_DECLARATION = "org.glassfish.jaxb.xmlDeclaration";
private static final String XML_HEADERS = "org.glassfish.jaxb.xmlHeaders";
private static final String OBJECT_IDENTITY_CYCLE_DETECTION = "org.glassfish.jaxb.objectIdentitityCycleDetection";
/**
* This constructor initializes various settings on the XML marshaller.
*
* @param newXMLMarshaller xml marshaller
* @param jaxbContext jaxb context
*/
public JAXBMarshaller(XMLMarshaller newXMLMarshaller, JAXBContext jaxbContext) {
this.jaxbContext = jaxbContext;
validationEventHandler = JAXBContext.DEFAULT_VALIDATION_EVENT_HANDLER;
beanValidationMode = BeanValidationMode.AUTO;
if (BeanValidationChecker.isBeanValidationPresent()) {
beanValidator = JAXBBeanValidator.getMarshallingBeanValidator(jaxbContext);
}
xmlMarshaller = newXMLMarshaller;
xmlMarshaller.setErrorHandler(new JAXBErrorHandler(validationEventHandler));
xmlMarshaller.setEncoding("UTF-8");
xmlMarshaller.setFormattedOutput(false);
xmlMarshaller.getProperties().put(Constants.JAXB_MARSHALLER, this);
}
/**
* Create an instance of XMLRoot populated from the contents of the provided
* JAXBElement. XMLRoot will be used to hold the contents of the JAXBElement
* while the marshal operation is performed by TopLink OXM. This will avoid
* adding any runtime dependencies to TopLink.
*
*/
private Root createXMLRootFromJAXBElement(JAXBElement elt) {
// create an XMLRoot to hand into the marshaller
Root xmlroot = new Root();
Object objectValue = elt.getValue();
xmlroot.setObject(objectValue);
QName qname = elt.getName();
xmlroot.setLocalName(qname.getLocalPart());
xmlroot.setNamespaceURI(qname.getNamespaceURI());
xmlroot.setDeclaredType(elt.getDeclaredType());
xmlroot.setNil(elt.isNil());
if (elt.getDeclaredType() == CoreClassConstants.ABYTE || elt.getDeclaredType() == CoreClassConstants.APBYTE ||
elt.getDeclaredType().getCanonicalName().equals("jakarta.activation.DataHandler") ||
elt.getDeclaredType().isEnum()) {
// need a binary data mapping so need to wrap
Class generatedClass = getClassToGeneratedClasses().get(elt.getDeclaredType().getCanonicalName());
if(!elt.getDeclaredType().isEnum()) {
xmlroot.setSchemaType(Constants.BASE_64_BINARY_QNAME);
}
if (generatedClass != null && WrappedValue.class.isAssignableFrom(generatedClass)) {
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(generatedClass).getDescriptor(generatedClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((WrappedValue) newObject).setValue(objectValue);
xmlroot.setObject(newObject);
return xmlroot;
}
} else {
xmlroot.setSchemaType(org.eclipse.persistence.internal.oxm.XMLConversionManager.getDefaultJavaTypes().get(elt.getDeclaredType()));
}
if (elt instanceof WrappedValue) {
xmlroot.setObject(elt);
return xmlroot;
}
Map<QName, Class> qNameToGeneratedClasses = jaxbContext.getQNameToGeneratedClasses();
if (qNameToGeneratedClasses != null) {
Class theClass = qNameToGeneratedClasses.get(qname);
if (theClass != null && WrappedValue.class.isAssignableFrom(theClass)) {
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(theClass).getDescriptor(theClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((WrappedValue) newObject).setValue(objectValue);
xmlroot.setObject(newObject);
return xmlroot;
}
}
Class generatedClass = null;
if (jaxbContext.getTypeMappingInfoToGeneratedType() != null) {
if (jaxbContext.getTypeToTypeMappingInfo() != null) {
if (elt.getDeclaredType() != null && elt.getDeclaredType().isArray()) {
TypeMappingInfo tmi = jaxbContext.getTypeToTypeMappingInfo().get(elt.getDeclaredType());
generatedClass = jaxbContext.getTypeMappingInfoToGeneratedType().get(tmi);
} else if (elt instanceof JAXBTypeElement) {
Type objectType = ((JAXBTypeElement) elt).getType();
TypeMappingInfo tmi = jaxbContext.getTypeToTypeMappingInfo().get(objectType);
generatedClass = jaxbContext.getTypeMappingInfoToGeneratedType().get(tmi);
}
}
} else {
if (elt.getDeclaredType() != null && elt.getDeclaredType().isArray()) {
if (jaxbContext.getArrayClassesToGeneratedClasses() != null) {
generatedClass = jaxbContext.getArrayClassesToGeneratedClasses().get(elt.getDeclaredType().getCanonicalName());
}
} else if (elt instanceof JAXBTypeElement) {
Type objectType = ((JAXBTypeElement) elt).getType();
generatedClass = jaxbContext.getCollectionClassesToGeneratedClasses().get(objectType);
}
}
if (generatedClass != null) {
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(generatedClass).getDescriptor(generatedClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((ManyValue) newObject).setItem(objectValue);
xmlroot.setObject(newObject);
}
return xmlroot;
}
@Override
public XmlAdapter getAdapter(Class javaClass) {
HashMap result = (HashMap) xmlMarshaller.getProperty(XML_JAVATYPE_ADAPTERS);
if (result == null) {
return null;
}
return (XmlAdapter) result.get(javaClass);
}
@Override
public AttachmentMarshaller getAttachmentMarshaller() {
if (xmlMarshaller.getAttachmentMarshaller() == null) {
return null;
}
return ((AttachmentMarshallerAdapter) xmlMarshaller.getAttachmentMarshaller()).getAttachmentMarshaller();
}
@Override
public ValidationEventHandler getEventHandler() throws JAXBException {
return validationEventHandler;
}
@Override
public Marshaller.Listener getListener() {
XMLMarshalListener xmlMarshalListener = xmlMarshaller.getMarshalListener();
if(null != xmlMarshalListener) {
return ((JAXBMarshalListener) xmlMarshalListener).getListener();
}
return null;
}
@Override
public Node getNode(Object object) throws JAXBException {
throw new UnsupportedOperationException();
}
/**
* Get a property from the JAXBMarshaller. Attempting to get any unsupported
* property will result in a jakarta.xml.bind.PropertyException
* @see org.eclipse.persistence.jaxb.MarshallerProperties
*/
@Override
public Object getProperty(String key) throws PropertyException {
if (key == null) {
throw new IllegalArgumentException();
} else if (JAXB_FORMATTED_OUTPUT.equals(key)) {
return xmlMarshaller.isFormattedOutput();
} else if (JAXB_ENCODING.equals(key)) {
return xmlMarshaller.getEncoding();
} else if (JAXB_SCHEMA_LOCATION.equals(key)) {
return xmlMarshaller.getSchemaLocation();
} else if (JAXB_NO_NAMESPACE_SCHEMA_LOCATION.equals(key)) {
return xmlMarshaller.getNoNamespaceSchemaLocation();
} else if (Constants.JAXB_FRAGMENT.equals(key)) {
return xmlMarshaller.isFragment();
} else if (MarshallerProperties.MEDIA_TYPE.equals(key)) {
return xmlMarshaller.getMediaType();
} else if (MarshallerProperties.NAMESPACE_PREFIX_MAPPER.equals(key)) {
return xmlMarshaller.getNamespacePrefixMapper();
} else if (MarshallerProperties.INDENT_STRING.equals(key) || SUN_INDENT_STRING.equals(key) || SUN_JSE_INDENT_STRING.equals(key)) {
return xmlMarshaller.getIndentString();
} else if (MarshallerProperties.CHARACTER_ESCAPE_HANDLER.equals(key)) {
return xmlMarshaller.getCharacterEscapeHandler();
} else if (XML_DECLARATION.equals(key)) {
return !xmlMarshaller.isFragment();
} else if (XML_HEADERS.equals(key)) {
return xmlMarshaller.getXmlHeader();
} else if (OBJECT_IDENTITY_CYCLE_DETECTION.equals(key)) {
return xmlMarshaller.isEqualUsingIdenity();
} else if (MarshallerProperties.JSON_ATTRIBUTE_PREFIX.equals(key)) {
return xmlMarshaller.getAttributePrefix();
} else if (MarshallerProperties.JSON_INCLUDE_ROOT.equals(key)) {
return xmlMarshaller.isIncludeRoot();
} else if (MarshallerProperties.JSON_VALUE_WRAPPER.equals(key)) {
return xmlMarshaller.getValueWrapper();
} else if (MarshallerProperties.JSON_NAMESPACE_SEPARATOR.equals(key)) {
return xmlMarshaller.getNamespaceSeparator();
} else if (MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME.equals(key)) {
return xmlMarshaller.isWrapperAsCollectionName();
} else if (MarshallerProperties.JSON_USE_XSD_TYPES_WITH_PREFIX.equals(key)) {
return xmlMarshaller.getJsonTypeConfiguration().isUseXsdTypesWithPrefix();
} else if (MarshallerProperties.JSON_TYPE_COMPATIBILITY.equals(key)) {
return xmlMarshaller.getJsonTypeConfiguration().isJsonTypeCompatibility();
} else if (MarshallerProperties.JSON_TYPE_ATTRIBUTE_NAME.equals(key)) {
return xmlMarshaller.getJsonTypeConfiguration().getJsonTypeAttributeName();
} else if (MarshallerProperties.JSON_DISABLE_NESTED_ARRAY_NAME.equals(key)) {
return xmlMarshaller.getJsonTypeConfiguration().isJsonDisableNestedArrayName();
} else if (SUN_CHARACTER_ESCAPE_HANDLER.equals(key) || SUN_JSE_CHARACTER_ESCAPE_HANDLER.equals(key) ||SUN_CHARACTER_ESCAPE_HANDLER_MARSHALLER.equals(key) || SUN_JSE_CHARACTER_ESCAPE_HANDLER_MARSHALLER.equals(key)) {
if (xmlMarshaller.getCharacterEscapeHandler() instanceof CharacterEscapeHandlerWrapper) {
CharacterEscapeHandlerWrapper wrapper = (CharacterEscapeHandlerWrapper) xmlMarshaller.getCharacterEscapeHandler();
return wrapper.getHandler();
}
return xmlMarshaller.getCharacterEscapeHandler();
} else if (SUN_NAMESPACE_PREFIX_MAPPER.equals(key) || SUN_JSE_NAMESPACE_PREFIX_MAPPER.equals(key)) {
NamespacePrefixMapperWrapper wrapper = (NamespacePrefixMapperWrapper) xmlMarshaller.getNamespacePrefixMapper();
if(wrapper == null){
return null;
}
return wrapper.getPrefixMapper();
} else if (MarshallerProperties.OBJECT_GRAPH.equals(key)) {
Object graph = xmlMarshaller.getMarshalAttributeGroup();
if(graph instanceof CoreAttributeGroup) {
return new ObjectGraphImpl((CoreAttributeGroup)graph);
}
return graph;
} else if (MarshallerProperties.BEAN_VALIDATION_MODE.equals(key)) {
return this.beanValidationMode;
} else if (MarshallerProperties.BEAN_VALIDATION_FACTORY.equals(key)) {
return this.prefValidatorFactory;
} else if (MarshallerProperties.BEAN_VALIDATION_GROUPS.equals(key)) {
return this.beanValidationGroups;
} else if (MarshallerProperties.BEAN_VALIDATION_NO_OPTIMISATION.equals(key)) {
return this.bvNoOptimisation;
} else if (MarshallerProperties.MOXY_LOG_PAYLOAD.equals(key)) {
return xmlMarshaller.isLogPayload();
}
throw new PropertyException(key);
}
@Override
public Schema getSchema() {
return xmlMarshaller.getSchema();
}
@Override
public void marshal(Object object, ContentHandler contentHandler) throws JAXBException {
if (object == null || contentHandler == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
xmlMarshaller.marshal(oxmObject, contentHandler);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
private Object wrapEnumeration(Object object, Class enumerationClass) {
Class generatedClass = this.getClassToGeneratedClasses().get(enumerationClass.getName());
if (generatedClass != null && WrappedValue.class.isAssignableFrom(generatedClass)) {
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(generatedClass).getDescriptor(generatedClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((WrappedValue) newObject).setValue(object);
object = newObject;
}
return object;
}
@Override
public void marshal(Object object, XMLEventWriter eventWriter) throws JAXBException {
if (object == null || eventWriter == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
XMLEventWriterRecord record = new XMLEventWriterRecord(eventWriter);
record.setMarshaller(this.xmlMarshaller);
this.xmlMarshaller.marshal(oxmObject, record);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception ex) {
throw new MarshalException(ex);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
public void marshal(Object object, XMLEventWriter eventWriter, TypeMappingInfo type) throws JAXBException {
if (jaxbContext.getTypeMappingInfoToGeneratedType() == null) {
marshal(object, eventWriter);
} else {
JAXBElement element = null;
Object value = object;
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
element = (JAXBElement) object;
value = element.getValue();
}
RootLevelXmlAdapter adapter = jaxbContext.getTypeMappingInfoToJavaTypeAdapters().get(type);
if (adapter != null) {
try {
value = adapter.getXmlAdapter().marshal(value);
} catch (Exception ex) {
throw new JAXBException(XMLMarshalException.marshalException(ex));
}
}
value = wrapObject(value, element, type);
marshal(value, eventWriter);
}
}
@Override
public void marshal(Object object, Node node) throws JAXBException {
if (object == null || node == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
xmlMarshaller.marshal(oxmObject, node);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
@Override
public void marshal(Object object, OutputStream outputStream) throws JAXBException {
if (object == null || outputStream == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
xmlMarshaller.marshal(oxmObject, outputStream);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
@Override
public void marshal(Object object, File file) throws JAXBException {
try {
FileOutputStream outputStream = new FileOutputStream(file);
try {
marshal(object, outputStream); // link to the other one
} finally {
outputStream.close();
}
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception ex) {
throw new MarshalException(ex);
}
}
@Override
public void marshal(Object object, Result result) throws JAXBException {
if (object == null || result == null) {
throw new IllegalArgumentException();
}
object = validateAndTransformIfNeeded(object); // xml bindings + json object
try {
xmlMarshaller.marshal(object, result);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
}
public void marshal(Object object, Result result, TypeMappingInfo type) throws JAXBException {
if (jaxbContext.getTypeMappingInfoToGeneratedType() == null) {
marshal(object, result);
} else {
JAXBElement element = null;
Object value = object;
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
element = (JAXBElement) object;
value = element.getValue();
}
if(jaxbContext.getTypeMappingInfoToJavaTypeAdapters().size() > 0){
RootLevelXmlAdapter adapter = jaxbContext.getTypeMappingInfoToJavaTypeAdapters().get(type);
if (adapter != null) {
try {
value = adapter.getXmlAdapter().marshal(value);
} catch (Exception ex) {
throw new JAXBException(XMLMarshalException.marshalException(ex));
}
}
}
value = wrapObject(value, element, type);
marshal(value, result);
}
}
@Override
public void marshal(Object object, XMLStreamWriter streamWriter) throws JAXBException {
if (object == null || streamWriter == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
XMLStreamWriterRecord record = new XMLStreamWriterRecord(streamWriter);
record.setMarshaller(this.xmlMarshaller);
this.xmlMarshaller.marshal(oxmObject, record);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception ex) {
throw new MarshalException(ex);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
private Object validateAndTransformIfNeeded(Object obj) throws BeanValidationException {
Object result = modifyObjectIfNeeded(obj);
if (beanValidator != null && beanValidator.shouldValidate(obj, beanValidationMode, prefValidatorFactory, bvNoOptimisation)) {
beanValidator.validate(result, beanValidationGroups);
}
return result;
}
private Object modifyObjectIfNeeded(Object obj) {
if (obj instanceof Collection) {
Collection objectList = (Collection) obj;
List newList = new ArrayList(objectList.size());
for (Object o : objectList) {
newList.add(modifySingleObjectIfNeeded(o));
}
return newList;
} else if (obj.getClass().isArray()) {
int arraySize = Array.getLength(obj);
List newList = new ArrayList(arraySize);
for (int x = 0; x < arraySize; x++) {
newList.add(modifySingleObjectIfNeeded(Array.get(obj, x)));
}
return newList;
} else {
return modifySingleObjectIfNeeded(obj);
}
}
private Object modifySingleObjectIfNeeded(Object obj){
// let the JAXBIntrospector determine if the object is a JAXBElement
if (obj instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
return createXMLRootFromJAXBElement((JAXBElement) obj);
} else if(obj != null && obj.getClass().isEnum()) {
return wrapEnumeration(obj, obj.getClass());
} else if (obj instanceof JSONWithPadding){
Object nestedObject = ((JSONWithPadding)obj).getObject();
if(nestedObject != null){
Object newNestedObject = modifyObjectIfNeeded(nestedObject);
if(nestedObject != newNestedObject){
return new JSONWithPadding(newNestedObject,((JSONWithPadding)obj).getCallbackName());
}
}
}
return obj;
}
public void marshal(Object object, XMLStreamWriter streamWriter, TypeMappingInfo type) throws JAXBException {
if (jaxbContext.getTypeMappingInfoToGeneratedType() == null) {
marshal(object, streamWriter);
} else {
JAXBElement element = null;
Object value = object;
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
element = (JAXBElement) object;
value = element.getValue();
}
if(jaxbContext.getTypeMappingInfoToJavaTypeAdapters().size() > 0){
RootLevelXmlAdapter adapter = jaxbContext.getTypeMappingInfoToJavaTypeAdapters().get(type);
if (adapter != null) {
try {
value = adapter.getXmlAdapter().marshal(value);
} catch (Exception ex) {
throw new JAXBException(XMLMarshalException.marshalException(ex));
}
}
}
value = wrapObject(value, element, type);
marshal(value, streamWriter);
}
}
private Object wrapObject(Object object, JAXBElement wrapperElement, TypeMappingInfo typeMappingInfo) {
if(jaxbContext.getTypeMappingInfoToGeneratedType().size() > 0){
Class generatedClass = jaxbContext.getTypeMappingInfoToGeneratedType().get(typeMappingInfo);
if(generatedClass != null && object == null && wrapperElement != null) {
return wrapObjectInXMLRoot(wrapperElement, null, typeMappingInfo);
}
if (generatedClass != null && WrappedValue.class.isAssignableFrom(generatedClass)) {
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(generatedClass).getDescriptor(generatedClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((WrappedValue) newObject).setValue(object);
object = newObject;
} else if (generatedClass != null) {
// should be a many value
ClassDescriptor desc = xmlMarshaller.getXMLContext().getSession(generatedClass).getDescriptor(generatedClass);
Object newObject = desc.getInstantiationPolicy().buildNewInstance();
((ManyValue) newObject).setItem(object);
object = newObject;
}
}
if (null == wrapperElement) {
Root xmlRoot = new Root();
QName xmlTagName = typeMappingInfo.getXmlTagName();
if (null == xmlTagName) {
return object;
}
xmlRoot.setNamespaceURI(typeMappingInfo.getXmlTagName().getNamespaceURI());
xmlRoot.setLocalName(typeMappingInfo.getXmlTagName().getLocalPart());
xmlRoot.setObject(object);
return xmlRoot;
}
return wrapObjectInXMLRoot(wrapperElement, object, typeMappingInfo);
}
private Root wrapObjectInXMLRoot(JAXBElement wrapperElement, Object value, TypeMappingInfo typeMappingInfo) {
Root xmlroot = new Root();
Object objectValue = value;
xmlroot.setObject(objectValue);
QName qname = wrapperElement.getName();
xmlroot.setLocalName(qname.getLocalPart());
xmlroot.setNamespaceURI(qname.getNamespaceURI());
xmlroot.setDeclaredType(wrapperElement.getDeclaredType());
if(typeMappingInfo != null){
xmlroot.setSchemaType(typeMappingInfo.getSchemaType());
} else if(value != null) {
if (value.getClass() == CoreClassConstants.ABYTE || value.getClass() == CoreClassConstants.APBYTE ||
value.getClass().getCanonicalName().equals("jakarta.activation.DataHandler")) {
xmlroot.setSchemaType(Constants.BASE_64_BINARY_QNAME);
}
}
return xmlroot;
}
@Override
public void marshal(Object object, Writer writer) throws JAXBException {
if (object == null || writer == null) {
throw new IllegalArgumentException();
}
Listener listener = getListener();
if(listener != null) {
if(object instanceof JAXBElement) {
listener.beforeMarshal(object);
}
}
Object oxmObject = validateAndTransformIfNeeded(object); // xml bindings + object
try {
xmlMarshaller.marshal(oxmObject, writer);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
if(listener != null) {
if(object instanceof JAXBElement) {
listener.afterMarshal(object);
}
}
}
public void marshal(Object object, MarshalRecord record) throws JAXBException {
if (object == null || record == null) {
throw new IllegalArgumentException();
}
object = validateAndTransformIfNeeded(object); // xml bindings + object
try {
record.setMarshaller(xmlMarshaller);
xmlMarshaller.marshal(object, record);
} catch (BeanValidationException bve) {
throw new MarshalException(bve.getMessage(), String.valueOf(bve.getErrorCode()), bve);
} catch (Exception e) {
throw new MarshalException(e);
}
}
public void marshal(Object object, MarshalRecord record, TypeMappingInfo type) throws JAXBException {
if (jaxbContext.getTypeMappingInfoToGeneratedType() == null) {
marshal(object, record);
} else {
JAXBElement element = null;
Object value = object;
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
element = (JAXBElement) object;
value = element.getValue();
}
RootLevelXmlAdapter adapter = jaxbContext.getTypeMappingInfoToJavaTypeAdapters().get(type);
if (adapter != null) {
try {
value = adapter.getXmlAdapter().marshal(value);
} catch (Exception ex) {
throw new JAXBException(XMLMarshalException.marshalException(ex));
}
}
value = wrapObject(value, element, type);
marshal(value, record);
}
}
@Override
public void setAdapter(Class javaClass, XmlAdapter adapter) {
HashMap result = (HashMap) xmlMarshaller.getProperty(XML_JAVATYPE_ADAPTERS);
if (result == null) {
result = new HashMap();
xmlMarshaller.getProperties().put(XML_JAVATYPE_ADAPTERS, result);
}
result.put(javaClass, adapter);
}
@Override
public void setAdapter(XmlAdapter adapter) {
setAdapter(adapter.getClass(), adapter);
}
@Override
public void setAttachmentMarshaller(AttachmentMarshaller attachmentMarshaller) {
if (attachmentMarshaller == null) {
xmlMarshaller.setAttachmentMarshaller(null);
} else {
xmlMarshaller.setAttachmentMarshaller(new AttachmentMarshallerAdapter(attachmentMarshaller));
}
}
@Override
public void setEventHandler(ValidationEventHandler newValidationEventHandler) throws JAXBException {
if (null == newValidationEventHandler) {
validationEventHandler = JAXBContext.DEFAULT_VALIDATION_EVENT_HANDLER;
} else {
validationEventHandler = newValidationEventHandler;
}
xmlMarshaller.setErrorHandler(new JAXBErrorHandler(validationEventHandler));
}
@Override
public void setListener(Marshaller.Listener listener) {
if(xmlMarshaller.getMarshalListener() == null) {
xmlMarshaller.setMarshalListener(new JAXBMarshalListener(jaxbContext, this));
}
((JAXBMarshalListener) xmlMarshaller.getMarshalListener()).setListener(listener);
}
public void setMarshalCallbacks(Map callbacks) {
if(callbacks == null || callbacks.isEmpty()) {
return;
}
if(xmlMarshaller.getMarshalListener() == null) {
xmlMarshaller.setMarshalListener(new JAXBMarshalListener(jaxbContext, this));
}
((JAXBMarshalListener) xmlMarshaller.getMarshalListener()).setClassBasedMarshalEvents(callbacks);
}
/**
* Set a property on the JAXBMarshaller. Attempting to set any unsupported
* property will result in a jakarta.xml.bind.PropertyException
* @see org.eclipse.persistence.jaxb.MarshallerProperties
*/
@Override
public void setProperty(String key, Object value) throws PropertyException {
try {
if (key == null) {
throw new IllegalArgumentException();
} else {
SessionLog logger = AbstractSessionLog.getLog();
if (logger.shouldLog(SessionLog.FINE, SessionLog.MOXY)) {
logger.log(SessionLog.FINE, SessionLog.MOXY, "moxy_set_marshaller_property", new Object[] {key, value});
}
if (MOXySystemProperties.moxyLogPayload != null && xmlMarshaller.isLogPayload() == null) {
xmlMarshaller.setLogPayload(MOXySystemProperties.moxyLogPayload);
}
if (Constants.JAXB_FRAGMENT.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
Boolean fragment = (Boolean) value;
xmlMarshaller.setFragment(fragment);
} else if (JAXB_FORMATTED_OUTPUT.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
Boolean formattedOutput = (Boolean) value;
xmlMarshaller.setFormattedOutput(formattedOutput);
} else if (JAXB_ENCODING.equals(key)) {
xmlMarshaller.setEncoding((String) value);
} else if (JAXB_SCHEMA_LOCATION.equals(key)) {
xmlMarshaller.setSchemaLocation((String) value);
} else if (JAXB_NO_NAMESPACE_SCHEMA_LOCATION.equals(key)) {
xmlMarshaller.setNoNamespaceSchemaLocation((String) value);
} else if (MarshallerProperties.NAMESPACE_PREFIX_MAPPER.equals(key)) {
if (value == null) {
xmlMarshaller.setNamespacePrefixMapper(null);
} else if (value instanceof Map) {
NamespacePrefixMapper namespacePrefixMapper = new MapNamespacePrefixMapper((Map) value);
xmlMarshaller.setNamespacePrefixMapper(namespacePrefixMapper);
} else {
xmlMarshaller.setNamespacePrefixMapper((NamespacePrefixMapper) value);
}
} else if (SUN_NAMESPACE_PREFIX_MAPPER.equals(key) || SUN_JSE_NAMESPACE_PREFIX_MAPPER.equals(key)) {
if (value == null) {
xmlMarshaller.setNamespacePrefixMapper(null);
} else {
xmlMarshaller.setNamespacePrefixMapper(new NamespacePrefixMapperWrapper(value));
}
} else if (MarshallerProperties.INDENT_STRING.equals(key) || SUN_INDENT_STRING.equals(key) || SUN_JSE_INDENT_STRING.equals(key)) {
xmlMarshaller.setIndentString((String) value);
} else if (MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS.equals(key)) {
xmlMarshaller.setMarshalEmptyCollections((Boolean) value);
} else if (MarshallerProperties.JSON_REDUCE_ANY_ARRAYS.equals(key)) {
xmlMarshaller.setReduceAnyArrays((Boolean) value);
} else if (MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME.equals(key)) {
xmlMarshaller.setWrapperAsCollectionName((Boolean) value);
} else if (MarshallerProperties.JSON_USE_XSD_TYPES_WITH_PREFIX.equals(key)) {
xmlMarshaller.getJsonTypeConfiguration().setUseXsdTypesWithPrefix((Boolean) value);
} else if (MarshallerProperties.JSON_TYPE_COMPATIBILITY.equals(key)) {
xmlMarshaller.getJsonTypeConfiguration().setJsonTypeCompatibility((Boolean) value);
} else if (MarshallerProperties.JSON_TYPE_ATTRIBUTE_NAME.equals(key)) {
xmlMarshaller.getJsonTypeConfiguration().setJsonTypeAttributeName((String) value);
} else if (MarshallerProperties.JSON_DISABLE_NESTED_ARRAY_NAME.equals(key)) {
xmlMarshaller.getJsonTypeConfiguration().setJsonDisableNestedArrayName((Boolean) value);
} else if (MarshallerProperties.CHARACTER_ESCAPE_HANDLER.equals(key)) {
xmlMarshaller.setCharacterEscapeHandler((CharacterEscapeHandler) value);
} else if (MarshallerProperties.MOXY_LOG_PAYLOAD.equals(key)) {
xmlMarshaller.setLogPayload(((Boolean) value));
} else if (MarshallerProperties.MOXY_LOGGING_LEVEL.equals(key)) {
if (value instanceof String) {
AbstractSessionLog.getLog().setLevel(LogLevel.toValue((String) value).getId(), SessionLog.MOXY);
} else {
AbstractSessionLog.getLog().setLevel(((LogLevel) value).getId(), SessionLog.MOXY);
}
} else if (SUN_CHARACTER_ESCAPE_HANDLER.equals(key) || SUN_JSE_CHARACTER_ESCAPE_HANDLER.equals(key) || SUN_CHARACTER_ESCAPE_HANDLER_MARSHALLER.equals(key) || SUN_JSE_CHARACTER_ESCAPE_HANDLER_MARSHALLER.equals(key)) {
if (value == null) {
xmlMarshaller.setCharacterEscapeHandler(null);
} else {
xmlMarshaller.setCharacterEscapeHandler(new CharacterEscapeHandlerWrapper(value));
}
} else if (XML_DECLARATION.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
Boolean fragment = !(Boolean) value;
xmlMarshaller.setFragment(fragment);
} else if (XML_HEADERS.equals(key)) {
xmlMarshaller.setXmlHeader((String) value);
} else if (OBJECT_IDENTITY_CYCLE_DETECTION.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
xmlMarshaller.setEqualUsingIdenity((Boolean) value);
} else if (MarshallerProperties.MEDIA_TYPE.equals(key)) {
MediaType mType = null;
if (value instanceof MediaType) {
mType = (MediaType) value;
} else if (value instanceof String) {
mType = MediaType.getMediaType((String) value);
}
if (mType == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
xmlMarshaller.setMediaType(mType);
} else if (MarshallerProperties.JSON_ATTRIBUTE_PREFIX.equals(key)) {
xmlMarshaller.setAttributePrefix((String) value);
} else if (MarshallerProperties.JSON_INCLUDE_ROOT.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
xmlMarshaller.setIncludeRoot((Boolean) value);
} else if (MarshallerProperties.JSON_VALUE_WRAPPER.equals(key)) {
if (value == null || (((String) value).length() == 0)) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
xmlMarshaller.setValueWrapper((String) value);
} else if (MarshallerProperties.JSON_NAMESPACE_SEPARATOR.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
xmlMarshaller.setNamespaceSeparator((Character) value);
} else if (MarshallerProperties.OBJECT_GRAPH.equals(key)) {
if (value == null) {
xmlMarshaller.setMarshalAttributeGroup(null);
} else if (value instanceof ObjectGraphImpl) {
xmlMarshaller.setMarshalAttributeGroup(((ObjectGraphImpl) value).getAttributeGroup());
} else if (value.getClass() == ClassConstants.STRING) {
xmlMarshaller.setMarshalAttributeGroup(value);
} else {
throw org.eclipse.persistence.exceptions.JAXBException.invalidValueForObjectGraph(value);
}
} else if (MarshallerProperties.BEAN_VALIDATION_MODE.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
this.beanValidationMode = ((BeanValidationMode) value);
} else if (MarshallerProperties.BEAN_VALIDATION_FACTORY.equals(key)) {
//noinspection StatementWithEmptyBody
if (value == null) {
// Allow null value for preferred validation factory.
}
this.prefValidatorFactory = value;
} else if (MarshallerProperties.BEAN_VALIDATION_GROUPS.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
this.beanValidationGroups = ((Class<?>[]) value);
} else if (MarshallerProperties.BEAN_VALIDATION_NO_OPTIMISATION.equals(key)) {
if (value == null) {
throw new PropertyException(key, Constants.EMPTY_STRING);
}
this.bvNoOptimisation = ((boolean) value);
} else {
throw new PropertyException(key, value);
}
}
} catch (ClassCastException exception) {
throw new PropertyException(key, exception);
}
}
@Override
public void setSchema(Schema schema) {
this.xmlMarshaller.setSchema(schema);
}
private Map<String, Class> getClassToGeneratedClasses() {
return jaxbContext.getClassToGeneratedClasses();
}
public JAXBContext getJaxbContext() {
return jaxbContext;
}
public XMLMarshaller getXMLMarshaller() {
return this.xmlMarshaller;
}
/**
* Returns constraint violations stored in the underlying
* {@link org.eclipse.persistence.jaxb.JAXBBeanValidator} instance.
*
* @return set of constraint violations from last unmarshal
*/
public Set<ConstraintViolationWrapper<Object>> getConstraintViolations() {
if (beanValidator != null) {
return beanValidator.getConstraintViolations();
}
return Collections.emptySet();
}
private static class CharacterEscapeHandlerWrapper extends org.eclipse.persistence.internal.oxm.record.CharacterEscapeHandlerWrapper implements CharacterEscapeHandler {
public CharacterEscapeHandlerWrapper(Object sunHandler) {
super(sunHandler);
}
}
}