aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.bind.v2.runtime; aoqi@0: aoqi@0: import javax.xml.bind.Binder; aoqi@0: import javax.xml.bind.JAXBElement; aoqi@0: import javax.xml.bind.JAXBException; aoqi@0: import javax.xml.bind.PropertyException; aoqi@0: import javax.xml.bind.ValidationEventHandler; aoqi@0: import javax.xml.validation.Schema; aoqi@0: import javax.xml.namespace.QName; aoqi@0: aoqi@0: import com.sun.xml.internal.bind.unmarshaller.InfosetScanner; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.output.DOMOutput; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl; aoqi@0: aoqi@0: import org.w3c.dom.Element; aoqi@0: import org.w3c.dom.Node; aoqi@0: import org.xml.sax.SAXException; aoqi@0: aoqi@0: /** aoqi@0: * Implementation of {@link Binder}. aoqi@0: * aoqi@0: * TODO: investigate how much in-place unmarshalling is implemented aoqi@0: * - some preliminary work is there. Probably buggy. aoqi@0: * TODO: work on the marshaller side. aoqi@0: * aoqi@0: * @author aoqi@0: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) aoqi@0: */ aoqi@0: public class BinderImpl extends Binder { aoqi@0: aoqi@0: /** aoqi@0: * The parent context object. aoqi@0: */ aoqi@0: private final JAXBContextImpl context; aoqi@0: aoqi@0: /** aoqi@0: * Lazily created unmarshaller to do XML->Java binding. aoqi@0: * @see #getUnmarshaller() aoqi@0: */ aoqi@0: private UnmarshallerImpl unmarshaller; aoqi@0: aoqi@0: /** aoqi@0: * Lazily create marshaller to do Java->XML binding. aoqi@0: * @see #getMarshaller() aoqi@0: */ aoqi@0: private MarshallerImpl marshaller; aoqi@0: aoqi@0: private final InfosetScanner scanner; aoqi@0: aoqi@0: /** aoqi@0: * A {@link Binder} always works with the same aoqi@0: * association map. aoqi@0: */ aoqi@0: private final AssociationMap assoc = new AssociationMap(); aoqi@0: aoqi@0: BinderImpl(JAXBContextImpl _context,InfosetScanner scanner) { aoqi@0: this.context = _context; aoqi@0: this.scanner = scanner; aoqi@0: } aoqi@0: aoqi@0: private UnmarshallerImpl getUnmarshaller() { aoqi@0: if(unmarshaller==null) aoqi@0: unmarshaller = new UnmarshallerImpl(context,assoc); aoqi@0: return unmarshaller; aoqi@0: } aoqi@0: aoqi@0: private MarshallerImpl getMarshaller() { aoqi@0: if(marshaller==null) aoqi@0: marshaller = new MarshallerImpl(context,assoc); aoqi@0: return marshaller; aoqi@0: } aoqi@0: aoqi@0: public void marshal(Object jaxbObject, XmlNode xmlNode) throws JAXBException { aoqi@0: if ((xmlNode == null) || (jaxbObject == null)) aoqi@0: throw new IllegalArgumentException(); aoqi@0: getMarshaller().marshal(jaxbObject,createOutput(xmlNode)); aoqi@0: } aoqi@0: aoqi@0: // TODO move this to a sub class once we support something other than W3C DOM aoqi@0: private DOMOutput createOutput(XmlNode xmlNode) { aoqi@0: return new DOMOutput((Node)xmlNode,assoc); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: public Object updateJAXB(XmlNode xmlNode) throws JAXBException { aoqi@0: return associativeUnmarshal(xmlNode,true,null); aoqi@0: } aoqi@0: aoqi@0: public Object unmarshal( XmlNode xmlNode ) throws JAXBException { aoqi@0: return associativeUnmarshal(xmlNode,false,null); aoqi@0: } aoqi@0: aoqi@0: public JAXBElement unmarshal(XmlNode xmlNode, Class expectedType) throws JAXBException { aoqi@0: if(expectedType==null) throw new IllegalArgumentException(); aoqi@0: return (JAXBElement)associativeUnmarshal(xmlNode,true,expectedType); aoqi@0: } aoqi@0: aoqi@0: public void setSchema(Schema schema) { aoqi@0: getMarshaller().setSchema(schema); aoqi@0: getUnmarshaller().setSchema(schema); aoqi@0: } aoqi@0: aoqi@0: public Schema getSchema() { aoqi@0: return getUnmarshaller().getSchema(); aoqi@0: } aoqi@0: aoqi@0: private Object associativeUnmarshal(XmlNode xmlNode, boolean inplace, Class expectedType) throws JAXBException { aoqi@0: if (xmlNode == null) aoqi@0: throw new IllegalArgumentException(); aoqi@0: aoqi@0: JaxBeanInfo bi = null; aoqi@0: if(expectedType!=null) aoqi@0: bi = context.getBeanInfo(expectedType, true); aoqi@0: aoqi@0: InterningXmlVisitor handler = new InterningXmlVisitor( aoqi@0: getUnmarshaller().createUnmarshallerHandler(scanner,inplace,bi)); aoqi@0: scanner.setContentHandler(new SAXConnector(handler,scanner.getLocator())); aoqi@0: try { aoqi@0: scanner.scan(xmlNode); aoqi@0: } catch( SAXException e ) { aoqi@0: throw unmarshaller.createUnmarshalException(e); aoqi@0: } aoqi@0: aoqi@0: return handler.getContext().getResult(); aoqi@0: } aoqi@0: aoqi@0: public XmlNode getXMLNode(Object jaxbObject) { aoqi@0: if(jaxbObject==null) aoqi@0: throw new IllegalArgumentException(); aoqi@0: AssociationMap.Entry e = assoc.byPeer(jaxbObject); aoqi@0: if(e==null) return null; aoqi@0: return e.element(); aoqi@0: } aoqi@0: aoqi@0: public Object getJAXBNode(XmlNode xmlNode) { aoqi@0: if(xmlNode==null) aoqi@0: throw new IllegalArgumentException(); aoqi@0: AssociationMap.Entry e = assoc.byElement(xmlNode); aoqi@0: if(e==null) return null; aoqi@0: if(e.outer()!=null) return e.outer(); aoqi@0: return e.inner(); aoqi@0: } aoqi@0: aoqi@0: public XmlNode updateXML(Object jaxbObject) throws JAXBException { aoqi@0: return updateXML(jaxbObject,getXMLNode(jaxbObject)); aoqi@0: } aoqi@0: aoqi@0: public XmlNode updateXML(Object jaxbObject, XmlNode xmlNode) throws JAXBException { aoqi@0: if(jaxbObject==null || xmlNode==null) throw new IllegalArgumentException(); aoqi@0: aoqi@0: // TODO aoqi@0: // for now just marshal aoqi@0: // TODO: object model independenc aoqi@0: Element e = (Element)xmlNode; aoqi@0: Node ns = e.getNextSibling(); aoqi@0: Node p = e.getParentNode(); aoqi@0: p.removeChild(e); aoqi@0: aoqi@0: // if the type object is passed, the following step is necessary to make aoqi@0: // the marshalling successful. aoqi@0: JaxBeanInfo bi = context.getBeanInfo(jaxbObject, true); aoqi@0: if(!bi.isElement()) aoqi@0: jaxbObject = new JAXBElement(new QName(e.getNamespaceURI(),e.getLocalName()),bi.jaxbType,jaxbObject); aoqi@0: aoqi@0: aoqi@0: getMarshaller().marshal(jaxbObject,p); aoqi@0: Node newNode = p.getLastChild(); aoqi@0: p.removeChild(newNode); aoqi@0: p.insertBefore(newNode,ns); aoqi@0: aoqi@0: return (XmlNode)newNode; aoqi@0: } aoqi@0: aoqi@0: public void setEventHandler(ValidationEventHandler handler) throws JAXBException { aoqi@0: getUnmarshaller().setEventHandler(handler); aoqi@0: getMarshaller().setEventHandler(handler); aoqi@0: } aoqi@0: aoqi@0: public ValidationEventHandler getEventHandler() { aoqi@0: return getUnmarshaller().getEventHandler(); aoqi@0: } aoqi@0: aoqi@0: public Object getProperty(String name) throws PropertyException { aoqi@0: if (name == null) aoqi@0: throw new IllegalArgumentException(Messages.NULL_PROPERTY_NAME.format()); aoqi@0: aoqi@0: // exclude RI properties that don't make sense for Binder aoqi@0: if (excludeProperty(name)) { aoqi@0: throw new PropertyException(name); aoqi@0: } aoqi@0: aoqi@0: Object prop = null; aoqi@0: PropertyException pe = null; aoqi@0: aoqi@0: try { aoqi@0: prop = getMarshaller().getProperty(name); aoqi@0: return prop; aoqi@0: } catch (PropertyException p) { aoqi@0: pe = p; aoqi@0: } aoqi@0: aoqi@0: try { aoqi@0: prop = getUnmarshaller().getProperty(name); aoqi@0: return prop; aoqi@0: } catch (PropertyException p) { aoqi@0: pe = p; aoqi@0: } aoqi@0: aoqi@0: pe.setStackTrace(Thread.currentThread().getStackTrace()); aoqi@0: throw pe; aoqi@0: } aoqi@0: aoqi@0: public void setProperty(String name, Object value) throws PropertyException { aoqi@0: if (name == null) aoqi@0: throw new IllegalArgumentException(Messages.NULL_PROPERTY_NAME.format()); aoqi@0: aoqi@0: // exclude RI properties that don't make sense for Binder aoqi@0: if (excludeProperty(name)) { aoqi@0: throw new PropertyException(name, value); aoqi@0: } aoqi@0: aoqi@0: PropertyException pe = null; aoqi@0: aoqi@0: try { aoqi@0: getMarshaller().setProperty(name, value); aoqi@0: return; aoqi@0: } catch (PropertyException p) { aoqi@0: pe = p; aoqi@0: } aoqi@0: aoqi@0: try { aoqi@0: getUnmarshaller().setProperty(name, value); aoqi@0: return; aoqi@0: } catch (PropertyException p) { aoqi@0: pe = p; aoqi@0: } aoqi@0: aoqi@0: // replace the stacktrace - we don't want to see a trace aoqi@0: // originating from Un|Marshaller.setProperty aoqi@0: pe.setStackTrace(Thread.currentThread().getStackTrace()); aoqi@0: throw pe; aoqi@0: } aoqi@0: aoqi@0: private boolean excludeProperty(String name) { aoqi@0: return name.equals( aoqi@0: MarshallerImpl.ENCODING_HANDLER) || aoqi@0: name.equals(MarshallerImpl.XMLDECLARATION) || aoqi@0: name.equals(MarshallerImpl.XML_HEADERS); aoqi@0: } aoqi@0: }