/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Emil Ong
*/
package com.caucho.jaxb;
import com.caucho.jaxb.skeleton.ClassSkeleton;
import com.caucho.util.L10N;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.xml.bind.Binder;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.MarshalException;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.helpers.AbstractMarshallerImpl;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.validation.Schema;
import java.io.IOException;
import java.util.HashMap;
/**
* A very basic implementation of Binder.
**/
public class BinderImpl extends Binder<Node> {
private static final L10N L = new L10N(BinderImpl.class);
private final JAXBContextImpl _context;
/*
private Marshaller _marshaller;
private Unmarshaller _unmarshaller;*/
private Schema _schema;
private ValidationEventHandler _validationEventHandler;
private final HashMap<Object,Node> _jaxbToXmlMap
= new HashMap<Object,Node>();
private final HashMap<Node,Object> _xmlToJaxbMap
= new HashMap<Node,Object>();
BinderImpl(JAXBContextImpl context)
{
_context = context;
_validationEventHandler = _context.DEFAULT_VALIDATION_EVENT_HANDLER;
}
/*
public Marshaller getMarshaller()
throws JAXBException
{
if (_marshaller == null)
_marshaller = _context.createMarshaller();
return _marshaller;
}
public Unmarshaller getUnmarshaller()
throws JAXBException
{
if (_unmarshaller == null)
_unmarshaller = _context.createUnmarshaller();
return _unmarshaller;
}*/
public ValidationEventHandler getEventHandler()
throws JAXBException
{
return _validationEventHandler;
}
public void setEventHandler(ValidationEventHandler handler)
throws JAXBException
{
if (handler == null)
_validationEventHandler = _context.DEFAULT_VALIDATION_EVENT_HANDLER;
else
_validationEventHandler = handler;
}
public Object getProperty(String name)
throws PropertyException
{
if (name == null)
throw new IllegalArgumentException(name);
if ("jaxb.encoding".equals(name)) {
// XXX
return null;
}
else if ("jaxb.formatted.output".equals(name)) {
// XXX
return null;
}
else if ("jaxb.schemaLocation".equals(name)) {
// XXX
return null;
}
else if ("jaxb.noNamespaceSchemaLocation".equals(name)) {
// XXX
return null;
}
else if ("jaxb.fragment".equals(name)) {
// XXX
return null;
}
else
throw new PropertyException(name);
}
public void setProperty(String name, Object value)
throws PropertyException
{
if (name == null)
throw new IllegalArgumentException(name);
if ("jaxb.encoding".equals(name)) { /* XXX */ }
else if ("jaxb.formatted.output".equals(name)) { /* XXX */ }
else if ("jaxb.schemaLocation".equals(name)) { /* XXX */ }
else if ("jaxb.noNamespaceSchemaLocation".equals(name)) { /* XXX */ }
else if ("jaxb.fragment".equals(name)) { /* XXX */ }
else
throw new PropertyException(name);
}
public Schema getSchema()
{
return _schema;
}
public void setSchema(Schema schema)
{
_schema = schema;
}
public Object getJAXBNode(Node node)
{
if (node == null)
throw new IllegalArgumentException(L.l("Node may not be null"));
return _xmlToJaxbMap.get(node);
}
public Node getXMLNode(Object jaxbObject)
{
if (jaxbObject == null)
throw new IllegalArgumentException(L.l("JAXB object may not be null"));
if (jaxbObject instanceof JAXBElement)
return _jaxbToXmlMap.get(((JAXBElement) jaxbObject).getValue());
else
return _jaxbToXmlMap.get(jaxbObject);
}
public void marshal(Object jaxbObject, Node xmlNode)
throws JAXBException
{
// XXX Schema
if (jaxbObject == null)
throw new IllegalArgumentException(L.l("JAXB object may not be null"));
if (xmlNode == null)
throw new IllegalArgumentException(L.l("Node may not be null"));
ClassSkeleton skeleton = _context.findSkeletonForObject(jaxbObject);
if (skeleton == null)
throw new MarshalException(L.l("Unable to marshal {0}: its type unknown to this JAXBContext", jaxbObject));
Document doc = xmlNode.getOwnerDocument();
if (xmlNode.getNodeType() == Node.DOCUMENT_NODE)
doc = (Document) xmlNode;
Node child = doc.createElement("root");
try {
xmlNode.appendChild(skeleton.bindTo(this, child, jaxbObject, null, null));
}
catch (IOException e) {
throw new JAXBException(e);
}
}
public Object unmarshal(Node xmlNode)
throws JAXBException
{
// XXX Schema
if (xmlNode == null)
throw new IllegalArgumentException(L.l("Node may not be null"));
Node root = xmlNode;
if (xmlNode.getNodeType() == Node.DOCUMENT_NODE) {
Document doc = (Document) xmlNode;
root = doc.getDocumentElement();
}
QName name = JAXBUtil.qnameFromNode(root);
ClassSkeleton skeleton = _context.getRootElement(name);
if (skeleton == null)
throw new UnmarshalException(L.l("Root element {0} is unknown to this context",
name));
try {
return skeleton.bindFrom(this, null, new NodeIterator(root));
}
catch (IOException e) {
throw new JAXBException(e);
}
}
public <T> JAXBElement<T> unmarshal(Node xmlNode, Class<T> declaredType)
throws JAXBException
{
if (xmlNode == null)
throw new IllegalArgumentException(L.l("Node may not be null"));
Node root = xmlNode;
if (xmlNode.getNodeType() == Node.DOCUMENT_NODE) {
Document doc = (Document) xmlNode;
root = doc.getDocumentElement();
}
ClassSkeleton skeleton = _context.findSkeletonForClass(declaredType);
if (skeleton == null)
throw new UnmarshalException(L.l("Type {0} is unknown to this context",
declaredType));
try {
T value = (T) skeleton.bindFrom(this, null, new NodeIterator(root));
QName qname = JAXBUtil.qnameFromNode(root);
return new JAXBElement<T>(qname, declaredType, value);
}
catch (IOException e) {
throw new JAXBException(e);
}
}
public Object updateJAXB(Node xmlNode)
throws JAXBException
{
Node root = xmlNode;
if (xmlNode.getNodeType() == Node.DOCUMENT_NODE) {
Document doc = (Document) xmlNode;
root = doc.getDocumentElement();
}
Object jaxbObject = getJAXBNode(root);
if (jaxbObject == null)
throw new JAXBException(L.l("Unknown xmlNode"));
ClassSkeleton skeleton = _context.findSkeletonForObject(jaxbObject);
if (skeleton == null) {
// we strip the JAXBElement when we bind objects, so rewrap
// the object and try again
QName qname = JAXBUtil.qnameFromNode(root);
jaxbObject = new JAXBElement(qname, jaxbObject.getClass(), jaxbObject);
skeleton = _context.findSkeletonForObject(jaxbObject);
if (skeleton == null)
throw new UnmarshalException(L.l("Type {0} is unknown to this context",
jaxbObject.getClass()));
}
try {
return skeleton.bindFrom(this, jaxbObject, new NodeIterator(root));
}
catch (IOException e) {
throw new JAXBException(e);
}
}
public Node updateXML(Object jaxbObject)
throws JAXBException
{
return updateXML(jaxbObject, getXMLNode(jaxbObject));
}
public Node updateXML(Object jaxbObject, Node xmlNode)
throws JAXBException
{
ClassSkeleton skeleton = _context.findSkeletonForObject(jaxbObject);
if (skeleton == null)
throw new MarshalException(L.l("Unable to update {0}: its type unknown to this JAXBContext", jaxbObject));
try {
return skeleton.bindTo(this, xmlNode, jaxbObject, null, null);
}
catch (IOException e) {
throw new JAXBException(e);
}
}
public void bind(Object jaxbObject, Node xmlNode)
{
if (jaxbObject instanceof JAXBElement)
jaxbObject = ((JAXBElement) jaxbObject).getValue();
// ensure that old bindings one way or the other are cleaned up
Node oldNode = _jaxbToXmlMap.get(jaxbObject);
if (xmlNode != oldNode)
_xmlToJaxbMap.remove(oldNode);
else {
Object oldObject = _xmlToJaxbMap.get(xmlNode);
if (oldObject != jaxbObject)
_jaxbToXmlMap.remove(oldObject);
}
// insert the new binding
_jaxbToXmlMap.put(jaxbObject, xmlNode);
_xmlToJaxbMap.put(xmlNode, jaxbObject);
}
public void invalidate(Node root)
{
if (root == null)
return;
remove(root);
for (Node node = root.getFirstChild();
node != null;
node = node.getNextSibling())
invalidate(root);
}
private void remove(Node root)
{
if (_xmlToJaxbMap.containsKey(root)) {
Object obj = _xmlToJaxbMap.remove(root);
if (_jaxbToXmlMap.containsKey(obj))
_jaxbToXmlMap.remove(obj);
}
}
}