325N/A/*
325N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.xml.internal.bind.v2.runtime.unmarshaller;
325N/A
325N/Aimport java.io.IOException;
325N/Aimport java.io.InputStream;
325N/A
325N/Aimport javax.xml.bind.JAXBContext;
325N/Aimport javax.xml.bind.JAXBElement;
325N/Aimport javax.xml.bind.JAXBException;
325N/Aimport javax.xml.bind.PropertyException;
325N/Aimport javax.xml.bind.UnmarshalException;
325N/Aimport javax.xml.bind.Unmarshaller;
325N/Aimport javax.xml.bind.UnmarshallerHandler;
325N/Aimport javax.xml.bind.ValidationEvent;
325N/Aimport javax.xml.bind.ValidationEventHandler;
325N/Aimport javax.xml.bind.annotation.adapters.XmlAdapter;
325N/Aimport javax.xml.bind.attachment.AttachmentUnmarshaller;
325N/Aimport javax.xml.bind.helpers.AbstractUnmarshallerImpl;
325N/Aimport javax.xml.stream.XMLEventReader;
325N/Aimport javax.xml.stream.XMLStreamConstants;
325N/Aimport javax.xml.stream.XMLStreamException;
325N/Aimport javax.xml.stream.XMLStreamReader;
325N/Aimport javax.xml.stream.events.XMLEvent;
325N/Aimport javax.xml.transform.Source;
325N/Aimport javax.xml.transform.dom.DOMSource;
325N/Aimport javax.xml.transform.sax.SAXSource;
325N/Aimport javax.xml.transform.stream.StreamSource;
325N/Aimport javax.xml.validation.Schema;
325N/A
325N/Aimport com.sun.xml.internal.bind.IDResolver;
325N/Aimport com.sun.xml.internal.bind.api.ClassResolver;
325N/Aimport com.sun.xml.internal.bind.unmarshaller.DOMScanner;
325N/Aimport com.sun.xml.internal.bind.unmarshaller.InfosetScanner;
325N/Aimport com.sun.xml.internal.bind.unmarshaller.Messages;
325N/Aimport com.sun.xml.internal.bind.v2.ClassFactory;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.AssociationMap;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
325N/A
325N/Aimport java.io.Closeable;
325N/Aimport org.w3c.dom.Document;
325N/Aimport org.w3c.dom.Element;
325N/Aimport org.w3c.dom.Node;
325N/Aimport org.xml.sax.InputSource;
325N/Aimport org.xml.sax.SAXException;
325N/Aimport org.xml.sax.XMLReader;
325N/Aimport org.xml.sax.helpers.DefaultHandler;
325N/A
325N/A/**
325N/A * Default Unmarshaller implementation.
325N/A *
325N/A * <p>
325N/A * This class can be extended by the generated code to provide
325N/A * type-safe unmarshall methods.
325N/A *
325N/A * @author
325N/A * <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
325N/A */
325N/Apublic final class UnmarshallerImpl extends AbstractUnmarshallerImpl implements ValidationEventHandler, Closeable
325N/A{
325N/A /** Owning {@link JAXBContext} */
325N/A protected final JAXBContextImpl context;
325N/A
325N/A /**
325N/A * schema which will be used to validate during calls to unmarshal
325N/A */
325N/A private Schema schema;
325N/A
325N/A public final UnmarshallingContext coordinator;
325N/A
325N/A /** Unmarshaller.Listener */
325N/A private Listener externalListener;
325N/A
325N/A /**
325N/A * The attachment unmarshaller used to support MTOM and swaRef.
325N/A */
325N/A private AttachmentUnmarshaller attachmentUnmarshaller;
325N/A private IDResolver idResolver = new DefaultIDResolver();
325N/A
325N/A public UnmarshallerImpl( JAXBContextImpl context, AssociationMap assoc ) {
325N/A this.context = context;
325N/A this.coordinator = new UnmarshallingContext( this, assoc );
325N/A
325N/A try {
325N/A setEventHandler(this);
325N/A } catch (JAXBException e) {
325N/A throw new AssertionError(e); // impossible
325N/A }
325N/A }
325N/A
325N/A public UnmarshallerHandler getUnmarshallerHandler() {
325N/A return getUnmarshallerHandler(true,null);
325N/A }
325N/A
325N/A private SAXConnector getUnmarshallerHandler( boolean intern, JaxBeanInfo expectedType ) {
325N/A XmlVisitor h = createUnmarshallerHandler(null,false,expectedType);
325N/A if(intern)
325N/A h = new InterningXmlVisitor(h);
325N/A return new SAXConnector(h,null);
325N/A }
325N/A
325N/A /**
325N/A * Creates and configures a new unmarshalling pipe line.
325N/A * Depending on the setting, we put a validator as a filter.
325N/A *
325N/A * @return
325N/A * A component that implements both {@link UnmarshallerHandler}
325N/A * and {@link ValidationEventHandler}. All the parsing errors
325N/A * should be reported to this error handler for the unmarshalling
325N/A * process to work correctly.
325N/A *
325N/A * Also, returned handler expects all the XML names to be interned.
325N/A *
325N/A */
325N/A public final XmlVisitor createUnmarshallerHandler(InfosetScanner scanner, boolean inplace, JaxBeanInfo expectedType ) {
325N/A
325N/A coordinator.reset(scanner,inplace,expectedType,idResolver);
325N/A XmlVisitor unmarshaller = coordinator;
325N/A
325N/A // delegate to JAXP 1.3 for validation if the client provided a schema
325N/A if (schema != null)
325N/A unmarshaller = new ValidatingUnmarshaller(schema,unmarshaller);
325N/A
325N/A if(attachmentUnmarshaller!=null && attachmentUnmarshaller.isXOPPackage())
325N/A unmarshaller = new MTOMDecorator(this,unmarshaller,attachmentUnmarshaller);
325N/A
325N/A return unmarshaller;
325N/A }
325N/A
325N/A private static final DefaultHandler dummyHandler = new DefaultHandler();
325N/A
325N/A public static boolean needsInterning( XMLReader reader ) {
325N/A // attempt to set it to true, which could fail
325N/A try {
325N/A reader.setFeature("http://xml.org/sax/features/string-interning",true);
325N/A } catch (SAXException e) {
325N/A // if it fails that's fine. we'll work around on our side
325N/A }
325N/A
325N/A try {
325N/A if( reader.getFeature("http://xml.org/sax/features/string-interning") )
325N/A return false; // no need for intern
325N/A } catch (SAXException e) {
325N/A // unrecognized/unsupported
325N/A }
325N/A // otherwise we need intern
325N/A return true;
325N/A }
325N/A
325N/A protected Object unmarshal( XMLReader reader, InputSource source ) throws JAXBException {
325N/A return unmarshal0(reader,source,null);
325N/A }
325N/A
325N/A protected <T> JAXBElement<T> unmarshal( XMLReader reader, InputSource source, Class<T> expectedType ) throws JAXBException {
325N/A if(expectedType==null)
325N/A throw new IllegalArgumentException();
325N/A return (JAXBElement)unmarshal0(reader,source,getBeanInfo(expectedType));
325N/A }
325N/A
325N/A private Object unmarshal0( XMLReader reader, InputSource source, JaxBeanInfo expectedType ) throws JAXBException {
325N/A
325N/A SAXConnector connector = getUnmarshallerHandler(needsInterning(reader),expectedType);
325N/A
325N/A reader.setContentHandler(connector);
325N/A // saxErrorHandler will be set by the getUnmarshallerHandler method.
325N/A // configure XMLReader so that the error will be sent to it.
325N/A // This is essential for the UnmarshallerHandler to be able to abort
325N/A // unmarshalling when an error is found.
325N/A //
325N/A // Note that when this XMLReader is provided by the client code,
325N/A // it might be already configured to call a client error handler.
325N/A // This will clobber such handler, if any.
325N/A //
325N/A // Ryan noted that we might want to report errors to such a client
325N/A // error handler as well.
325N/A reader.setErrorHandler(coordinator);
325N/A
325N/A try {
325N/A reader.parse(source);
325N/A } catch( IOException e ) {
325N/A coordinator.clearStates();
325N/A throw new UnmarshalException(e);
325N/A } catch( SAXException e ) {
325N/A coordinator.clearStates();
325N/A throw createUnmarshalException(e);
325N/A }
325N/A
325N/A Object result = connector.getResult();
325N/A
325N/A // avoid keeping unnecessary references too long to let the GC
325N/A // reclaim more memory.
325N/A // setting null upsets some parsers, so use a dummy instance instead.
325N/A reader.setContentHandler(dummyHandler);
325N/A reader.setErrorHandler(dummyHandler);
325N/A
325N/A return result;
325N/A }
325N/A
325N/A @Override
325N/A public <T> JAXBElement<T> unmarshal( Source source, Class<T> expectedType ) throws JAXBException {
325N/A if(source instanceof SAXSource) {
325N/A SAXSource ss = (SAXSource)source;
325N/A
325N/A XMLReader reader = ss.getXMLReader();
325N/A if( reader == null )
325N/A reader = getXMLReader();
325N/A
325N/A return unmarshal( reader, ss.getInputSource(), expectedType );
325N/A }
325N/A if(source instanceof StreamSource) {
325N/A return unmarshal( getXMLReader(), streamSourceToInputSource((StreamSource)source), expectedType );
325N/A }
325N/A if(source instanceof DOMSource)
325N/A return unmarshal( ((DOMSource)source).getNode(), expectedType );
325N/A
325N/A // we don't handle other types of Source
325N/A throw new IllegalArgumentException();
325N/A }
325N/A
325N/A public Object unmarshal0( Source source, JaxBeanInfo expectedType ) throws JAXBException {
325N/A if(source instanceof SAXSource) {
325N/A SAXSource ss = (SAXSource)source;
325N/A
325N/A XMLReader reader = ss.getXMLReader();
325N/A if( reader == null )
325N/A reader = getXMLReader();
325N/A
325N/A return unmarshal0( reader, ss.getInputSource(), expectedType );
325N/A }
325N/A if(source instanceof StreamSource) {
325N/A return unmarshal0( getXMLReader(), streamSourceToInputSource((StreamSource)source), expectedType );
325N/A }
325N/A if(source instanceof DOMSource)
325N/A return unmarshal0( ((DOMSource)source).getNode(), expectedType );
325N/A
325N/A // we don't handle other types of Source
325N/A throw new IllegalArgumentException();
325N/A }
325N/A
325N/A
325N/A @Override
325N/A public final ValidationEventHandler getEventHandler() {
325N/A try {
325N/A return super.getEventHandler();
325N/A } catch (JAXBException e) {
325N/A // impossible
325N/A throw new AssertionError();
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Returns true if an event handler is installed.
325N/A * <p>
325N/A * The default handler ignores any errors, and for that this method returns false.
325N/A */
325N/A public final boolean hasEventHandler() {
325N/A return getEventHandler()!=this;
325N/A }
325N/A
325N/A @Override
325N/A public <T> JAXBElement<T> unmarshal(Node node, Class<T> expectedType) throws JAXBException {
325N/A if(expectedType==null)
325N/A throw new IllegalArgumentException();
325N/A return (JAXBElement)unmarshal0(node,getBeanInfo(expectedType));
325N/A }
325N/A
325N/A public final Object unmarshal( Node node ) throws JAXBException {
325N/A return unmarshal0(node,null);
325N/A }
325N/A
325N/A // just to make the the test harness happy by making this method accessible
325N/A @Deprecated
325N/A public final Object unmarshal( SAXSource source ) throws JAXBException {
325N/A return super.unmarshal(source);
325N/A }
325N/A
325N/A public final Object unmarshal0( Node node, JaxBeanInfo expectedType ) throws JAXBException {
325N/A try {
325N/A final DOMScanner scanner = new DOMScanner();
325N/A
325N/A InterningXmlVisitor handler = new InterningXmlVisitor(createUnmarshallerHandler(null,false,expectedType));
325N/A scanner.setContentHandler(new SAXConnector(handler,scanner));
325N/A
325N/A if(node.getNodeType() == Node.ELEMENT_NODE)
325N/A scanner.scan((Element)node);
325N/A else
325N/A if(node.getNodeType() == Node.DOCUMENT_NODE)
325N/A scanner.scan((Document)node);
325N/A else
325N/A // no other type of input is supported
325N/A throw new IllegalArgumentException("Unexpected node type: "+node);
325N/A
325N/A Object retVal = handler.getContext().getResult();
325N/A handler.getContext().clearResult();
325N/A return retVal;
325N/A } catch( SAXException e ) {
325N/A throw createUnmarshalException(e);
325N/A }
325N/A }
325N/A
325N/A @Override
325N/A public Object unmarshal(XMLStreamReader reader) throws JAXBException {
325N/A return unmarshal0(reader,null);
325N/A }
325N/A
325N/A @Override
325N/A public <T> JAXBElement<T> unmarshal(XMLStreamReader reader, Class<T> expectedType) throws JAXBException {
325N/A if(expectedType==null)
325N/A throw new IllegalArgumentException();
325N/A return (JAXBElement)unmarshal0(reader,getBeanInfo(expectedType));
325N/A }
325N/A
325N/A public Object unmarshal0(XMLStreamReader reader, JaxBeanInfo expectedType) throws JAXBException {
325N/A if (reader == null) {
325N/A throw new IllegalArgumentException(
325N/A Messages.format(Messages.NULL_READER));
325N/A }
325N/A
325N/A int eventType = reader.getEventType();
325N/A if (eventType != XMLStreamConstants.START_ELEMENT
325N/A && eventType != XMLStreamConstants.START_DOCUMENT) {
325N/A // TODO: convert eventType into event name
325N/A throw new IllegalStateException(
325N/A Messages.format(Messages.ILLEGAL_READER_STATE,eventType));
325N/A }
325N/A
325N/A XmlVisitor h = createUnmarshallerHandler(null,false,expectedType);
325N/A StAXConnector connector=StAXStreamConnector.create(reader,h);
325N/A
325N/A try {
325N/A connector.bridge();
325N/A } catch (XMLStreamException e) {
325N/A throw handleStreamException(e);
325N/A }
325N/A
325N/A Object retVal = h.getContext().getResult();
325N/A h.getContext().clearResult();
325N/A return retVal;
325N/A }
325N/A
325N/A @Override
325N/A public <T> JAXBElement<T> unmarshal(XMLEventReader reader, Class<T> expectedType) throws JAXBException {
325N/A if(expectedType==null)
325N/A throw new IllegalArgumentException();
325N/A return (JAXBElement)unmarshal0(reader,getBeanInfo(expectedType));
325N/A }
325N/A
325N/A @Override
325N/A public Object unmarshal(XMLEventReader reader) throws JAXBException {
325N/A return unmarshal0(reader,null);
325N/A }
325N/A
325N/A private Object unmarshal0(XMLEventReader reader,JaxBeanInfo expectedType) throws JAXBException {
325N/A if (reader == null) {
325N/A throw new IllegalArgumentException(
325N/A Messages.format(Messages.NULL_READER));
325N/A }
325N/A
325N/A try {
325N/A XMLEvent event = reader.peek();
325N/A
325N/A if (!event.isStartElement() && !event.isStartDocument()) {
325N/A // TODO: convert event into event name
325N/A throw new IllegalStateException(
325N/A Messages.format(
325N/A Messages.ILLEGAL_READER_STATE,event.getEventType()));
325N/A }
325N/A
325N/A // Quick hack until SJSXP fixes 6270116
325N/A boolean isZephyr = reader.getClass().getName().equals("com.sun.xml.internal.stream.XMLReaderImpl");
325N/A XmlVisitor h = createUnmarshallerHandler(null,false,expectedType);
325N/A if(!isZephyr)
325N/A h = new InterningXmlVisitor(h);
325N/A new StAXEventConnector(reader,h).bridge();
325N/A return h.getContext().getResult();
325N/A } catch (XMLStreamException e) {
325N/A throw handleStreamException(e);
325N/A }
325N/A }
325N/A
325N/A public Object unmarshal0( InputStream input, JaxBeanInfo expectedType ) throws JAXBException {
325N/A return unmarshal0(getXMLReader(),new InputSource(input),expectedType);
325N/A }
325N/A
325N/A private static JAXBException handleStreamException(XMLStreamException e) {
325N/A // StAXStreamConnector wraps SAXException to XMLStreamException.
325N/A // XMLStreamException doesn't print its nested stack trace when it prints
325N/A // its stack trace, so if we wrap XMLStreamException in JAXBException,
325N/A // it becomes harder to find out the real problem.
325N/A // So we unwrap them here. But we don't want to unwrap too eagerly, because
325N/A // that could throw away some meaningful exception information.
325N/A Throwable ne = e.getNestedException();
325N/A if(ne instanceof JAXBException)
325N/A return (JAXBException)ne;
325N/A if(ne instanceof SAXException)
325N/A return new UnmarshalException(ne);
325N/A return new UnmarshalException(e);
325N/A }
325N/A
325N/A @Override
325N/A public Object getProperty(String name) throws PropertyException {
325N/A if(name.equals(IDResolver.class.getName())) {
325N/A return idResolver;
325N/A }
325N/A return super.getProperty(name);
325N/A }
325N/A
325N/A @Override
325N/A public void setProperty(String name, Object value) throws PropertyException {
325N/A if(name.equals(FACTORY)) {
325N/A coordinator.setFactories(value);
325N/A return;
325N/A }
325N/A if(name.equals(IDResolver.class.getName())) {
325N/A idResolver = (IDResolver)value;
325N/A return;
325N/A }
325N/A if(name.equals(ClassResolver.class.getName())) {
325N/A coordinator.classResolver = (ClassResolver)value;
325N/A return;
325N/A }
325N/A if(name.equals(ClassLoader.class.getName())) {
325N/A coordinator.classLoader = (ClassLoader)value;
325N/A return;
325N/A }
325N/A super.setProperty(name, value);
325N/A }
325N/A
325N/A public static final String FACTORY = "com.sun.xml.internal.bind.ObjectFactory";
325N/A
325N/A @Override
325N/A public void setSchema(Schema schema) {
325N/A this.schema = schema;
325N/A }
325N/A
325N/A @Override
325N/A public Schema getSchema() {
325N/A return schema;
325N/A }
325N/A
325N/A @Override
325N/A public AttachmentUnmarshaller getAttachmentUnmarshaller() {
325N/A return attachmentUnmarshaller;
325N/A }
325N/A
325N/A @Override
325N/A public void setAttachmentUnmarshaller(AttachmentUnmarshaller au) {
325N/A this.attachmentUnmarshaller = au;
325N/A }
325N/A
325N/A /**
325N/A * @deprecated since 2.0
325N/A */
325N/A @Override
325N/A public boolean isValidating() {
325N/A throw new UnsupportedOperationException();
325N/A }
325N/A
325N/A /**
325N/A * @deprecated since 2.0
325N/A */
325N/A @Override
325N/A public void setValidating(boolean validating) {
325N/A throw new UnsupportedOperationException();
325N/A }
325N/A
325N/A @Override
325N/A public <A extends XmlAdapter> void setAdapter(Class<A> type, A adapter) {
325N/A if(type==null)
325N/A throw new IllegalArgumentException();
325N/A coordinator.putAdapter(type,adapter);
325N/A }
325N/A
325N/A @Override
325N/A public <A extends XmlAdapter> A getAdapter(Class<A> type) {
325N/A if(type==null)
325N/A throw new IllegalArgumentException();
325N/A if(coordinator.containsAdapter(type))
325N/A // so as not to create a new instance when this method is called
325N/A return coordinator.getAdapter(type);
325N/A else
325N/A return null;
325N/A }
325N/A
325N/A // opening up for public use
325N/A @Override
325N/A public UnmarshalException createUnmarshalException( SAXException e ) {
325N/A return super.createUnmarshalException(e);
325N/A }
325N/A
325N/A
325N/A /**
325N/A * Default error handling behavior for {@link Unmarshaller}.
325N/A */
325N/A public boolean handleEvent(ValidationEvent event) {
325N/A return event.getSeverity()!=ValidationEvent.FATAL_ERROR;
325N/A }
325N/A
325N/A private static InputSource streamSourceToInputSource( StreamSource ss ) {
325N/A InputSource is = new InputSource();
325N/A is.setSystemId( ss.getSystemId() );
325N/A is.setByteStream( ss.getInputStream() );
325N/A is.setCharacterStream( ss.getReader() );
325N/A
325N/A return is;
325N/A }
325N/A
325N/A public <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz) throws JAXBException {
325N/A return context.getBeanInfo(clazz,true);
325N/A }
325N/A
325N/A @Override
325N/A public Listener getListener() {
325N/A return externalListener;
325N/A }
325N/A
325N/A @Override
325N/A public void setListener(Listener listener) {
325N/A externalListener = listener;
325N/A }
325N/A
325N/A public UnmarshallingContext getContext() {
325N/A return coordinator;
325N/A }
325N/A
325N/A @Override
325N/A @SuppressWarnings("FinalizeDeclaration")
325N/A protected void finalize() throws Throwable {
325N/A try {
325N/A ClassFactory.cleanCache();
325N/A } finally {
325N/A super.finalize();
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Must be called from same thread which created the UnmarshallerImpl instance.
325N/A * @throws IOException
325N/A */
325N/A public void close() throws IOException {
325N/A ClassFactory.cleanCache();
325N/A }
325N/A
325N/A}