/*******************************************************************************
* Copyright (c) 1998, 2008 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;
import java.io.OutputStream;
import java.io.Writer;
import java.io.File;
import java.util.HashMap;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.MarshalException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.attachment.AttachmentMarshaller;
import javax.xml.bind.helpers.DefaultValidationEventHandler;
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 javax.xml.validation.SchemaFactory;
import java.lang.reflect.Constructor;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
import org.eclipse.persistence.oxm.XMLConstants;
import org.eclipse.persistence.oxm.XMLMarshaller;
import org.eclipse.persistence.oxm.XMLRoot;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.jaxb.attachment.*;
/**
* 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.0 Marshaller interface provides the required functionality
* by acting as a thin wrapper on the existing XMLMarshaller API.
*
* @author mmacivor
* @since Oracle TopLink 11.1.1.0.0
* @see javax.xml.bind.Marshaller
* @see org.eclipse.persistence.oxm.XMLMarshaller
*/
public class JAXBMarshaller implements javax.xml.bind.Marshaller {
private ValidationEventHandler validationEventHandler;
private XMLMarshaller xmlMarshaller;
public static final String XML_JAVATYPE_ADAPTERS = "xml-javatype-adapters";
private static String STAX_RESULT_CLASS_NAME = "javax.xml.transform.stax.StAXResult";
/**
* This constructor initializes various settings on the XML marshaller, and
* stores the provided JAXBIntrospector instance for usage in marshal()
* calls.
*
* @param newXMLMarshaller
* @param newIntrospector
*/
public JAXBMarshaller(XMLMarshaller newXMLMarshaller, JAXBIntrospector newIntrospector) {
super();
validationEventHandler = new DefaultValidationEventHandler();
xmlMarshaller = newXMLMarshaller;
xmlMarshaller.setEncoding("UTF-8");
xmlMarshaller.setFormattedOutput(false);
JAXBMarshalListener listener = new JAXBMarshalListener(this);
xmlMarshaller.setMarshalListener(listener);
}
/**
* 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.
*
* @param elt
* @return
*/
private XMLRoot createXMLRootFromJAXBElement(JAXBElement elt) {
// create an XMLRoot to hand into the marshaller
XMLRoot xmlroot = new XMLRoot();
xmlroot.setObject(elt.getValue());
QName qname = elt.getName();
xmlroot.setLocalName(qname.getLocalPart());
xmlroot.setNamespaceURI(qname.getNamespaceURI());
if(elt.getDeclaredType() == ClassConstants.ABYTE || elt.getDeclaredType() == ClassConstants.APBYTE) {
xmlroot.setSchemaType(XMLConstants.BASE_64_BINARY_QNAME);
} else {
xmlroot.setSchemaType((QName)org.eclipse.persistence.internal.oxm.XMLConversionManager.getDefaultJavaTypes().get(elt.getDeclaredType()));
}
return xmlroot;
}
public XmlAdapter getAdapter(Class javaClass) {
HashMap result = (HashMap) xmlMarshaller.getProperty(XML_JAVATYPE_ADAPTERS);
if (result == null) {
return null;
}
return (XmlAdapter) result.get(javaClass);
}
public AttachmentMarshaller getAttachmentMarshaller() {
return ((AttachmentMarshallerAdapter)xmlMarshaller.getAttachmentMarshaller()).getAttachmentMarshaller();
}
public ValidationEventHandler getEventHandler() throws JAXBException {
return validationEventHandler;
}
public Marshaller.Listener getListener() {
return ((JAXBMarshalListener) xmlMarshaller.getMarshalListener()).getListener();
}
public Node getNode(Object object) throws JAXBException {
throw new UnsupportedOperationException();
}
public Object getProperty(String key) throws PropertyException {
if (key == null) {
throw new IllegalArgumentException();
} else if (JAXB_FORMATTED_OUTPUT.equals(key)) {
return new Boolean(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 (XMLConstants.JAXB_FRAGMENT.equals(key)) {
return new Boolean(xmlMarshaller.isFragment());
}
throw new PropertyException("Unsupported Property");
}
public Schema getSchema() {
try {
//TODO: Need to generate a proper schema and convert to Sun Schema model
return SchemaFactory.newInstance(XMLConstants.SCHEMA_URL).newSchema();
} catch (org.xml.sax.SAXException ex) {
}
return null;
}
public void marshal(Object object, ContentHandler contentHandler) throws JAXBException {
if (object == null || contentHandler == null) {
throw new IllegalArgumentException();
}
// TODO: in our case, we only care if the object is an instance
// of JAXBElement, and do not need to use the introspector...
// let the JAXBIntrospector determine if the object is a JAXBElement
//if (jaxbIntrospector.isElement(object)) {
// use the JAXBElement's properties to populate an XMLRoot
// object = createXMLRootFromJAXBElement((JAXBElement) object);
//}
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
object = createXMLRootFromJAXBElement((JAXBElement) object);
}
try {
xmlMarshaller.marshal(object, contentHandler);
} catch (Exception e) {
throw new MarshalException(e);
}
}
// TODO: add support for StAX
public void marshal(Object object, XMLEventWriter eventWriter) throws JAXBException {
try {
Class staxResult = PrivilegedAccessHelper.getClassForName(STAX_RESULT_CLASS_NAME);
Constructor cons = PrivilegedAccessHelper.getDeclaredConstructorFor(staxResult, new Class[]{XMLEventWriter.class}, false);
Result result = (Result)PrivilegedAccessHelper.invokeConstructor(cons, new Object[]{eventWriter});
this.marshal(object, result);
} catch(Exception ex) {
throw new MarshalException(ex);
}
}
public void marshal(Object object, Node node) throws JAXBException {
if (object == null || node == null) {
throw new IllegalArgumentException();
}
// let the JAXBIntrospector determine if the object is a JAXBElement
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
object = createXMLRootFromJAXBElement((JAXBElement) object);
}
try {
xmlMarshaller.marshal(object, node);
} catch (Exception e) {
throw new MarshalException(e);
}
}
public void marshal(Object object, OutputStream outputStream) throws JAXBException {
if (object == null || outputStream == null) {
throw new IllegalArgumentException();
}
// let the JAXBIntrospector determine if the object is a JAXBElement
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
object = createXMLRootFromJAXBElement((JAXBElement) object);
}
try {
xmlMarshaller.marshal(object, outputStream);
} catch (Exception e) {
throw new MarshalException(e);
}
}
public void marshal(Object object, File file) throws JAXBException {
try {
java.io.FileWriter writer = new java.io.FileWriter(file);
marshal(object, writer);
} catch(Exception ex) {
throw new MarshalException(ex);
}
}
public void marshal(Object object, Result result) throws JAXBException {
if (object == null || result == null) {
throw new IllegalArgumentException();
}
// let the JAXBIntrospector determine if the object is a JAXBElement
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
object = createXMLRootFromJAXBElement((JAXBElement) object);
}
try {
xmlMarshaller.marshal(object, result);
} catch (Exception e) {
throw new MarshalException(e);
}
}
// TODO: add support for StAX
public void marshal(Object object, XMLStreamWriter streamWriter) throws JAXBException {
try {
Class staxResult = PrivilegedAccessHelper.getClassForName(STAX_RESULT_CLASS_NAME);
Constructor cons = PrivilegedAccessHelper.getDeclaredConstructorFor(staxResult, new Class[]{XMLStreamWriter.class}, false);
Result result = (Result)PrivilegedAccessHelper.invokeConstructor(cons, new Object[]{streamWriter});
this.marshal(object, result);
} catch(Exception ex) {
throw new MarshalException(ex);
}
}
public void marshal(Object object, Writer writer) throws JAXBException {
if (object == null || writer == null) {
throw new IllegalArgumentException();
}
// let the JAXBIntrospector determine if the object is a JAXBElement
if (object instanceof JAXBElement) {
// use the JAXBElement's properties to populate an XMLRoot
object = createXMLRootFromJAXBElement((JAXBElement) object);
}
try {
xmlMarshaller.marshal(object, writer);
} catch (Exception e) {
throw new MarshalException(e);
}
}
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);
}
public void setAdapter(XmlAdapter adapter) {
setAdapter(adapter.getClass(), adapter);
}
public void setAttachmentMarshaller(AttachmentMarshaller attachmentMarshaller) {
xmlMarshaller.setAttachmentMarshaller(new AttachmentMarshallerAdapter(attachmentMarshaller));
}
public void setEventHandler(ValidationEventHandler newValidationEventHandler) throws JAXBException {
if (null == newValidationEventHandler) {
validationEventHandler = new DefaultValidationEventHandler();
} else {
validationEventHandler = newValidationEventHandler;
}
}
public void setListener(Marshaller.Listener listener) {
((JAXBMarshalListener) xmlMarshaller.getMarshalListener()).setListener(listener);
}
public void setMarshalCallbacks(java.util.HashMap callbacks) {
((JAXBMarshalListener) xmlMarshaller.getMarshalListener()).setClassBasedMarshalEvents(callbacks);
}
public void setProperty(String key, Object value) throws PropertyException {
try {
if (key == null) {
throw new IllegalArgumentException();
} else if (JAXB_FORMATTED_OUTPUT.equals(key)) {
Boolean formattedOutput = (Boolean) value;
xmlMarshaller.setFormattedOutput(formattedOutput.booleanValue());
} 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 (XMLConstants.JAXB_FRAGMENT.equals(key)) {
Boolean fragment = (Boolean) value;
xmlMarshaller.setFragment(fragment.booleanValue());
} else {
throw new PropertyException(key, value);
}
} catch (ClassCastException exception) {
throw new PropertyException(key, exception);
}
}
public void setSchema(Schema schema) {
}
}