/* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ /* * @(#)$Id: UnmarshallerImpl.java,v 1.14 2005/07/27 21:17:39 kohsuke Exp $ */ package com.sun.xml.bind.v2.runtime.unmarshaller; import com.sun.xml.fastinfoset.stax.StAXDocumentParser; import java.io.IOException; import java.net.URL; import javax.xml.bind.DatatypeConverter; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.PropertyException; import javax.xml.bind.UnmarshalException; import javax.xml.bind.Unmarshaller; import javax.xml.bind.UnmarshallerHandler; import javax.xml.bind.ValidationEventHandler; import javax.xml.bind.ValidationEvent; import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.attachment.AttachmentUnmarshaller; import javax.xml.bind.helpers.AbstractUnmarshallerImpl; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.events.XMLEvent; import javax.xml.validation.Schema; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import javax.xml.transform.sax.SAXSource; import com.sun.xml.bind.DatatypeConverterImpl; import com.sun.xml.bind.unmarshaller.DOMScanner; import com.sun.xml.bind.unmarshaller.InfosetScanner; import com.sun.xml.bind.unmarshaller.Messages; import com.sun.xml.bind.v2.AssociationMap; import com.sun.xml.bind.v2.runtime.JAXBContextImpl; import com.sun.xml.bind.v2.runtime.JaxBeanInfo; import com.sun.xml.bind.v2.stax.XMLEventReaderToContentHandler; import com.sun.xml.bind.v2.stax.XMLStreamReaderToContentHandler; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; /** * Default Unmarshaller implementation. * *

* This class can be extended by the generated code to provide * type-safe unmarshall methods. * * @author * Kohsuke KAWAGUCHI */ public final class UnmarshallerImpl extends AbstractUnmarshallerImpl implements ValidationEventHandler { /** Owning {@link JAXBContext} */ protected final JAXBContextImpl context; /** * schema which will be used to validate during calls to unmarshal */ private Schema schema; public final UnmarshallingContext coordinator; /** * The attachment unmarshaller used to support MTOM and swaRef. */ private AttachmentUnmarshaller attachmentUnmarshaller; public UnmarshallerImpl( JAXBContextImpl context, AssociationMap assoc ) { this.context = context; this.coordinator = new UnmarshallingContext( this, assoc ); // initialize datatype converter with ours DatatypeConverter.setDatatypeConverter(DatatypeConverterImpl.theInstance); try { setEventHandler(this); } catch (JAXBException e) { throw new AssertionError(e); // impossible } } public UnmarshallerHandler getUnmarshallerHandler() { return getUnmarshallerHandler(true,null); } private SAXConnector getUnmarshallerHandler( boolean intern, JaxBeanInfo expectedType ) { XmlVisitor h = createUnmarshallerHandler(null,false,expectedType); if(intern) h = new InterningXmlVisitor(h); return new SAXConnector(h,null); } /** * Creates and configures a new unmarshalling pipe line. * Depending on the setting, we put a validator as a filter. * * @return * A component that implements both {@link UnmarshallerHandler} * and {@link ValidationEventHandler}. All the parsing errors * should be reported to this error handler for the unmarshalling * process to work correctly. * * Also, returned handler expects all the XML names to be interned. * */ public final XmlVisitor createUnmarshallerHandler(InfosetScanner scanner, boolean inplace, JaxBeanInfo expectedType ) { coordinator.reset(scanner,inplace,expectedType); XmlVisitor unmarshaller = coordinator; // delegate to JAXP 1.3 for validation if the client provided a schema if (schema != null) unmarshaller = new ValidatingUnmarshaller(schema,unmarshaller); if(attachmentUnmarshaller!=null && attachmentUnmarshaller.isXOPPackage()) unmarshaller = new MTOMDecorator(this,unmarshaller,attachmentUnmarshaller); return unmarshaller; } private static final DefaultHandler dummyHandler = new DefaultHandler(); public static boolean needsInterning( XMLReader reader ) { // attempt to set it to true, which could fail try { reader.setFeature("http://xml.org/sax/features/string-interning",true); } catch (SAXException e) { ; } try { if( reader.getFeature("http://xml.org/sax/features/string-interning") ) return false; // no need for intern } catch (SAXException e) { ; // unrecognized/unsupported } // otherwise we need intern return true; } protected Object unmarshal( XMLReader reader, InputSource source ) throws JAXBException { return unmarshal0(reader,source,null); } protected JAXBElement unmarshal( XMLReader reader, InputSource source, Class expectedType ) throws JAXBException { if(expectedType==null) throw new IllegalArgumentException(); return (JAXBElement)unmarshal0(reader,source,getBeanInfo(expectedType)); } private Object unmarshal0( XMLReader reader, InputSource source, JaxBeanInfo expectedType ) throws JAXBException { SAXConnector connector = getUnmarshallerHandler(needsInterning(reader),expectedType); reader.setContentHandler(connector); // saxErrorHandler will be set by the getUnmarshallerHandler method. // configure XMLReader so that the error will be sent to it. // This is essential for the UnmarshallerHandler to be able to abort // unmarshalling when an error is found. // // Note that when this XMLReader is provided by the client code, // it might be already configured to call a client error handler. // This will clobber such handler, if any. // // Ryan noted that we might want to report errors to such a client // error handler as well. reader.setErrorHandler(coordinator); try { reader.parse(source); } catch( IOException e ) { throw new JAXBException(e); } catch( SAXException e ) { throw createUnmarshalException(e); } Object result = connector.getResult(); // avoid keeping unnecessary references too long to let the GC // reclaim more memory. // setting null upsets some parsers, so use a dummy instance instead. reader.setContentHandler(dummyHandler); reader.setErrorHandler(dummyHandler); return result; } @Override public JAXBElement unmarshal( Source source, Class expectedType ) throws JAXBException { if(source instanceof SAXSource) { SAXSource ss = (SAXSource)source; XMLReader reader = ss.getXMLReader(); if( reader == null ) reader = getXMLReader(); return unmarshal( reader, ss.getInputSource(), expectedType ); } if(source instanceof StreamSource) { return unmarshal( getXMLReader(), streamSourceToInputSource((StreamSource)source), expectedType ); } if(source instanceof DOMSource) return unmarshal( ((DOMSource)source).getNode(), expectedType ); // we don't handle other types of Source throw new IllegalArgumentException(); } public final ValidationEventHandler getEventHandler() { try { return super.getEventHandler(); } catch (JAXBException e) { // impossible throw new AssertionError(); } } @Override public JAXBElement unmarshal(Node node, Class expectedType) throws JAXBException { if(expectedType==null) throw new IllegalArgumentException(); return (JAXBElement)unmarshal0(node,getBeanInfo(expectedType)); } public final Object unmarshal( Node node ) throws JAXBException { return unmarshal0(node,null); } public final Object unmarshal0( Node node, JaxBeanInfo expectedType ) throws JAXBException { try { final DOMScanner scanner = new DOMScanner(); InterningXmlVisitor handler = new InterningXmlVisitor(createUnmarshallerHandler(null,false,expectedType)); scanner.setContentHandler(new SAXConnector(handler,scanner)); if(node instanceof Element) scanner.scan((Element)node); else if(node instanceof Document) scanner.scan((Document)node); else // no other type of input is supported throw new IllegalArgumentException(); return handler.getContext().getResult(); } catch( SAXException e ) { throw createUnmarshalException(e); } } @Override public Object unmarshal(XMLStreamReader reader) throws JAXBException { return unmarshal0(reader,null); } @Override public JAXBElement unmarshal(XMLStreamReader reader, Class expectedType) throws JAXBException { if(expectedType==null) throw new IllegalArgumentException(); return (JAXBElement)unmarshal0(reader,getBeanInfo(expectedType)); } public Object unmarshal0(XMLStreamReader reader, JaxBeanInfo expectedType) throws JAXBException { if (reader == null) { throw new IllegalArgumentException( Messages.format(Messages.NULL_READER)); } int eventType = reader.getEventType(); if (eventType != XMLStreamConstants.START_ELEMENT && eventType != XMLStreamConstants.START_DOCUMENT) { // TODO: convert eventType into event name throw new IllegalStateException( Messages.format(Messages.ILLEGAL_READER_STATE,eventType)); } if (reader instanceof StAXDocumentParser) { StAXDocumentParser fastInfosetStreamReader = (StAXDocumentParser)reader; fastInfosetStreamReader.setStringInterning(true); XmlVisitor h = createUnmarshallerHandler(null,false,expectedType); try { new FastInfosetConnector(fastInfosetStreamReader, h).bridge(); } catch (XMLStreamException e) { throw handleStreamException(e); } return h.getContext().getResult(); } else { // Quick hack until SJSXP fixes 6270116 boolean isZephyr = reader.getClass().getName().equals("com.sun.xml.stream.XMLReaderImpl"); UnmarshallerHandler h = getUnmarshallerHandler(!isZephyr,expectedType); try { new XMLStreamReaderToContentHandler(reader,h).bridge(); } catch (XMLStreamException e) { throw handleStreamException(e); } return h.getResult(); } } @Override public JAXBElement unmarshal(XMLEventReader reader, Class expectedType) throws JAXBException { if(expectedType==null) throw new IllegalArgumentException(); return (JAXBElement)unmarshal0(reader,getBeanInfo(expectedType)); } @Override public Object unmarshal(XMLEventReader reader) throws JAXBException { return unmarshal0(reader,null); } private Object unmarshal0(XMLEventReader reader,JaxBeanInfo expectedType) throws JAXBException { if (reader == null) { throw new IllegalArgumentException( Messages.format(Messages.NULL_READER)); } try { XMLEvent event = reader.peek(); if (!event.isStartElement() && !event.isStartDocument()) { // TODO: convert event into event name throw new IllegalStateException( Messages.format( Messages.ILLEGAL_READER_STATE,event.getEventType())); } UnmarshallerHandler h = getUnmarshallerHandler(true,expectedType); new XMLEventReaderToContentHandler(reader, h).bridge(); return h.getResult(); } catch (XMLStreamException e) { throw handleStreamException(e); } } // remove this method when you remove this use from BridgeImpl public Object unmarshal0( URL input, JaxBeanInfo expectedType ) throws JAXBException { return unmarshal0(getXMLReader(),new InputSource(input.toExternalForm()),expectedType); } private static JAXBException handleStreamException(XMLStreamException e) { // XMLStreamReaderToContentHandler wraps SAXException to XMLStreamException. // XMLStreamException doesn't print its nested stack trace when it prints // its stack trace, so if we wrap XMLStreamException in JAXBException, // it becomes harder to find out the real problem. // So we unwrap them here. But we don't want to unwrap too eagerly, because // that could throw away some meaningful exception information. Throwable ne = e.getNestedException(); if(ne instanceof JAXBException) return (JAXBException)ne; if(ne instanceof SAXException) return new JAXBException(ne); return new JAXBException(e); } public void setProperty(String name, Object value) throws PropertyException { if(name.equals(FACTORY)) { coordinator.setFactories(value); return; } super.setProperty(name, value); } public static final String FACTORY = "com.sun.xml.bind.ObjectFactory"; @Override public void setSchema(Schema schema) { this.schema = schema; } @Override public Schema getSchema() { return schema; } @Override public AttachmentUnmarshaller getAttachmentUnmarshaller() { return attachmentUnmarshaller; } @Override public void setAttachmentUnmarshaller(AttachmentUnmarshaller au) { this.attachmentUnmarshaller = au; } /** * @deprecated since 2.0 */ @Override public boolean isValidating() { throw new UnsupportedOperationException(); } /** * @deprecated since 2.0 */ @Override public void setValidating(boolean validating) { throw new UnsupportedOperationException(); } @Override public void setAdapter(Class type, A adapter) { if(type==null) throw new IllegalArgumentException(); coordinator.putAdapter(type,adapter); } @Override public A getAdapter(Class type) { if(type==null) throw new IllegalArgumentException(); if(coordinator.containsAdapter(type)) // so as not to create a new instance when this method is called return coordinator.getAdapter(type); else return null; } // opening up for public use public UnmarshalException createUnmarshalException( SAXException e ) { return super.createUnmarshalException(e); } /** * Default error handling behavior fot {@link Unmarshaller}. */ public boolean handleEvent(ValidationEvent event) { return event.getSeverity()!=ValidationEvent.FATAL_ERROR; } private static InputSource streamSourceToInputSource( StreamSource ss ) { InputSource is = new InputSource(); is.setSystemId( ss.getSystemId() ); is.setByteStream( ss.getInputStream() ); is.setCharacterStream( ss.getReader() ); return is; } public JaxBeanInfo getBeanInfo(Class clazz) throws JAXBException { return context.getBeanInfo(clazz,true); } }