001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2.runtime.unmarshaller;
027:
028: import java.io.IOException;
029: import java.io.InputStream;
030:
031: import javax.xml.bind.JAXBContext;
032: import javax.xml.bind.JAXBElement;
033: import javax.xml.bind.JAXBException;
034: import javax.xml.bind.PropertyException;
035: import javax.xml.bind.UnmarshalException;
036: import javax.xml.bind.Unmarshaller;
037: import javax.xml.bind.UnmarshallerHandler;
038: import javax.xml.bind.ValidationEvent;
039: import javax.xml.bind.ValidationEventHandler;
040: import javax.xml.bind.annotation.adapters.XmlAdapter;
041: import javax.xml.bind.attachment.AttachmentUnmarshaller;
042: import javax.xml.bind.helpers.AbstractUnmarshallerImpl;
043: import javax.xml.stream.XMLEventReader;
044: import javax.xml.stream.XMLStreamConstants;
045: import javax.xml.stream.XMLStreamException;
046: import javax.xml.stream.XMLStreamReader;
047: import javax.xml.stream.events.XMLEvent;
048: import javax.xml.transform.Source;
049: import javax.xml.transform.dom.DOMSource;
050: import javax.xml.transform.sax.SAXSource;
051: import javax.xml.transform.stream.StreamSource;
052: import javax.xml.validation.Schema;
053:
054: import com.sun.xml.internal.bind.IDResolver;
055: import com.sun.xml.internal.bind.unmarshaller.DOMScanner;
056: import com.sun.xml.internal.bind.unmarshaller.InfosetScanner;
057: import com.sun.xml.internal.bind.unmarshaller.Messages;
058: import com.sun.xml.internal.bind.v2.runtime.AssociationMap;
059: import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
060: import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
061:
062: import org.w3c.dom.Document;
063: import org.w3c.dom.Element;
064: import org.w3c.dom.Node;
065: import org.xml.sax.InputSource;
066: import org.xml.sax.SAXException;
067: import org.xml.sax.XMLReader;
068: import org.xml.sax.helpers.DefaultHandler;
069:
070: /**
071: * Default Unmarshaller implementation.
072: *
073: * <p>
074: * This class can be extended by the generated code to provide
075: * type-safe unmarshall methods.
076: *
077: * @author
078: * <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
079: */
080: public final class UnmarshallerImpl extends AbstractUnmarshallerImpl
081: implements ValidationEventHandler {
082: /** Owning {@link JAXBContext} */
083: protected final JAXBContextImpl context;
084:
085: /**
086: * schema which will be used to validate during calls to unmarshal
087: */
088: private Schema schema;
089:
090: public final UnmarshallingContext coordinator;
091:
092: /** Unmarshaller.Listener */
093: private Listener externalListener;
094:
095: /**
096: * The attachment unmarshaller used to support MTOM and swaRef.
097: */
098: private AttachmentUnmarshaller attachmentUnmarshaller;
099: private IDResolver idResolver = new DefaultIDResolver();
100:
101: public UnmarshallerImpl(JAXBContextImpl context,
102: AssociationMap assoc) {
103: this .context = context;
104: this .coordinator = new UnmarshallingContext(this , assoc);
105:
106: try {
107: setEventHandler(this );
108: } catch (JAXBException e) {
109: throw new AssertionError(e); // impossible
110: }
111: }
112:
113: public UnmarshallerHandler getUnmarshallerHandler() {
114: return getUnmarshallerHandler(true, null);
115: }
116:
117: private SAXConnector getUnmarshallerHandler(boolean intern,
118: JaxBeanInfo expectedType) {
119: XmlVisitor h = createUnmarshallerHandler(null, false,
120: expectedType);
121: if (intern)
122: h = new InterningXmlVisitor(h);
123: return new SAXConnector(h, null);
124: }
125:
126: /**
127: * Creates and configures a new unmarshalling pipe line.
128: * Depending on the setting, we put a validator as a filter.
129: *
130: * @return
131: * A component that implements both {@link UnmarshallerHandler}
132: * and {@link ValidationEventHandler}. All the parsing errors
133: * should be reported to this error handler for the unmarshalling
134: * process to work correctly.
135: *
136: * Also, returned handler expects all the XML names to be interned.
137: *
138: */
139: public final XmlVisitor createUnmarshallerHandler(
140: InfosetScanner scanner, boolean inplace,
141: JaxBeanInfo expectedType) {
142:
143: coordinator.reset(scanner, inplace, expectedType, idResolver);
144: XmlVisitor unmarshaller = coordinator;
145:
146: // delegate to JAXP 1.3 for validation if the client provided a schema
147: if (schema != null)
148: unmarshaller = new ValidatingUnmarshaller(schema,
149: unmarshaller);
150:
151: if (attachmentUnmarshaller != null
152: && attachmentUnmarshaller.isXOPPackage())
153: unmarshaller = new MTOMDecorator(this , unmarshaller,
154: attachmentUnmarshaller);
155:
156: return unmarshaller;
157: }
158:
159: private static final DefaultHandler dummyHandler = new DefaultHandler();
160:
161: public static boolean needsInterning(XMLReader reader) {
162: // attempt to set it to true, which could fail
163: try {
164: reader.setFeature(
165: "http://xml.org/sax/features/string-interning",
166: true);
167: } catch (SAXException e) {
168: // if it fails that's fine. we'll work around on our side
169: }
170:
171: try {
172: if (reader
173: .getFeature("http://xml.org/sax/features/string-interning"))
174: return false; // no need for intern
175: } catch (SAXException e) {
176: // unrecognized/unsupported
177: }
178: // otherwise we need intern
179: return true;
180: }
181:
182: protected Object unmarshal(XMLReader reader, InputSource source)
183: throws JAXBException {
184: return unmarshal0(reader, source, null);
185: }
186:
187: protected <T> JAXBElement<T> unmarshal(XMLReader reader,
188: InputSource source, Class<T> expectedType)
189: throws JAXBException {
190: if (expectedType == null)
191: throw new IllegalArgumentException();
192: return (JAXBElement) unmarshal0(reader, source,
193: getBeanInfo(expectedType));
194: }
195:
196: private Object unmarshal0(XMLReader reader, InputSource source,
197: JaxBeanInfo expectedType) throws JAXBException {
198:
199: SAXConnector connector = getUnmarshallerHandler(
200: needsInterning(reader), expectedType);
201:
202: reader.setContentHandler(connector);
203: // saxErrorHandler will be set by the getUnmarshallerHandler method.
204: // configure XMLReader so that the error will be sent to it.
205: // This is essential for the UnmarshallerHandler to be able to abort
206: // unmarshalling when an error is found.
207: //
208: // Note that when this XMLReader is provided by the client code,
209: // it might be already configured to call a client error handler.
210: // This will clobber such handler, if any.
211: //
212: // Ryan noted that we might want to report errors to such a client
213: // error handler as well.
214: reader.setErrorHandler(coordinator);
215:
216: try {
217: reader.parse(source);
218: } catch (IOException e) {
219: throw new UnmarshalException(e);
220: } catch (SAXException e) {
221: throw createUnmarshalException(e);
222: }
223:
224: Object result = connector.getResult();
225:
226: // avoid keeping unnecessary references too long to let the GC
227: // reclaim more memory.
228: // setting null upsets some parsers, so use a dummy instance instead.
229: reader.setContentHandler(dummyHandler);
230: reader.setErrorHandler(dummyHandler);
231:
232: return result;
233: }
234:
235: @Override
236: public <T> JAXBElement<T> unmarshal(Source source,
237: Class<T> expectedType) throws JAXBException {
238: if (source instanceof SAXSource) {
239: SAXSource ss = (SAXSource) source;
240:
241: XMLReader reader = ss.getXMLReader();
242: if (reader == null)
243: reader = getXMLReader();
244:
245: return unmarshal(reader, ss.getInputSource(), expectedType);
246: }
247: if (source instanceof StreamSource) {
248: return unmarshal(getXMLReader(),
249: streamSourceToInputSource((StreamSource) source),
250: expectedType);
251: }
252: if (source instanceof DOMSource)
253: return unmarshal(((DOMSource) source).getNode(),
254: expectedType);
255:
256: // we don't handle other types of Source
257: throw new IllegalArgumentException();
258: }
259:
260: public Object unmarshal0(Source source, JaxBeanInfo expectedType)
261: throws JAXBException {
262: if (source instanceof SAXSource) {
263: SAXSource ss = (SAXSource) source;
264:
265: XMLReader reader = ss.getXMLReader();
266: if (reader == null)
267: reader = getXMLReader();
268:
269: return unmarshal0(reader, ss.getInputSource(), expectedType);
270: }
271: if (source instanceof StreamSource) {
272: return unmarshal0(getXMLReader(),
273: streamSourceToInputSource((StreamSource) source),
274: expectedType);
275: }
276: if (source instanceof DOMSource)
277: return unmarshal0(((DOMSource) source).getNode(),
278: expectedType);
279:
280: // we don't handle other types of Source
281: throw new IllegalArgumentException();
282: }
283:
284: public final ValidationEventHandler getEventHandler() {
285: try {
286: return super .getEventHandler();
287: } catch (JAXBException e) {
288: // impossible
289: throw new AssertionError();
290: }
291: }
292:
293: @Override
294: public <T> JAXBElement<T> unmarshal(Node node, Class<T> expectedType)
295: throws JAXBException {
296: if (expectedType == null)
297: throw new IllegalArgumentException();
298: return (JAXBElement) unmarshal0(node, getBeanInfo(expectedType));
299: }
300:
301: public final Object unmarshal(Node node) throws JAXBException {
302: return unmarshal0(node, null);
303: }
304:
305: // just to make the the test harness happy by making this method accessible
306: @Deprecated
307: public final Object unmarshal(SAXSource source)
308: throws JAXBException {
309: return super .unmarshal(source);
310: }
311:
312: public final Object unmarshal0(Node node, JaxBeanInfo expectedType)
313: throws JAXBException {
314: try {
315: final DOMScanner scanner = new DOMScanner();
316:
317: InterningXmlVisitor handler = new InterningXmlVisitor(
318: createUnmarshallerHandler(null, false, expectedType));
319: scanner
320: .setContentHandler(new SAXConnector(handler,
321: scanner));
322:
323: if (node instanceof Element)
324: scanner.scan((Element) node);
325: else if (node instanceof Document)
326: scanner.scan((Document) node);
327: else
328: // no other type of input is supported
329: throw new IllegalArgumentException();
330:
331: return handler.getContext().getResult();
332: } catch (SAXException e) {
333: throw createUnmarshalException(e);
334: }
335: }
336:
337: @Override
338: public Object unmarshal(XMLStreamReader reader)
339: throws JAXBException {
340: return unmarshal0(reader, null);
341: }
342:
343: @Override
344: public <T> JAXBElement<T> unmarshal(XMLStreamReader reader,
345: Class<T> expectedType) throws JAXBException {
346: if (expectedType == null)
347: throw new IllegalArgumentException();
348: return (JAXBElement) unmarshal0(reader,
349: getBeanInfo(expectedType));
350: }
351:
352: public Object unmarshal0(XMLStreamReader reader,
353: JaxBeanInfo expectedType) throws JAXBException {
354: if (reader == null) {
355: throw new IllegalArgumentException(Messages
356: .format(Messages.NULL_READER));
357: }
358:
359: int eventType = reader.getEventType();
360: if (eventType != XMLStreamConstants.START_ELEMENT
361: && eventType != XMLStreamConstants.START_DOCUMENT) {
362: // TODO: convert eventType into event name
363: throw new IllegalStateException(Messages.format(
364: Messages.ILLEGAL_READER_STATE, eventType));
365: }
366:
367: XmlVisitor h = createUnmarshallerHandler(null, false,
368: expectedType);
369: StAXConnector connector = StAXStreamConnector.create(reader, h);
370:
371: try {
372: connector.bridge();
373: } catch (XMLStreamException e) {
374: throw handleStreamException(e);
375: }
376:
377: return h.getContext().getResult();
378: }
379:
380: @Override
381: public <T> JAXBElement<T> unmarshal(XMLEventReader reader,
382: Class<T> expectedType) throws JAXBException {
383: if (expectedType == null)
384: throw new IllegalArgumentException();
385: return (JAXBElement) unmarshal0(reader,
386: getBeanInfo(expectedType));
387: }
388:
389: @Override
390: public Object unmarshal(XMLEventReader reader) throws JAXBException {
391: return unmarshal0(reader, null);
392: }
393:
394: private Object unmarshal0(XMLEventReader reader,
395: JaxBeanInfo expectedType) throws JAXBException {
396: if (reader == null) {
397: throw new IllegalArgumentException(Messages
398: .format(Messages.NULL_READER));
399: }
400:
401: try {
402: XMLEvent event = reader.peek();
403:
404: if (!event.isStartElement() && !event.isStartDocument()) {
405: // TODO: convert event into event name
406: throw new IllegalStateException(Messages.format(
407: Messages.ILLEGAL_READER_STATE, event
408: .getEventType()));
409: }
410:
411: // Quick hack until SJSXP fixes 6270116
412: boolean isZephyr = reader.getClass().getName().equals(
413: "com.sun.xml.internal.stream.XMLReaderImpl");
414: XmlVisitor h = createUnmarshallerHandler(null, false,
415: expectedType);
416: if (!isZephyr)
417: h = new InterningXmlVisitor(h);
418: new StAXEventConnector(reader, h).bridge();
419: return h.getContext().getResult();
420: } catch (XMLStreamException e) {
421: throw handleStreamException(e);
422: }
423: }
424:
425: public Object unmarshal0(InputStream input, JaxBeanInfo expectedType)
426: throws JAXBException {
427: return unmarshal0(getXMLReader(), new InputSource(input),
428: expectedType);
429: }
430:
431: private static JAXBException handleStreamException(
432: XMLStreamException e) {
433: // StAXStreamConnector wraps SAXException to XMLStreamException.
434: // XMLStreamException doesn't print its nested stack trace when it prints
435: // its stack trace, so if we wrap XMLStreamException in JAXBException,
436: // it becomes harder to find out the real problem.
437: // So we unwrap them here. But we don't want to unwrap too eagerly, because
438: // that could throw away some meaningful exception information.
439: Throwable ne = e.getNestedException();
440: if (ne instanceof JAXBException)
441: return (JAXBException) ne;
442: if (ne instanceof SAXException)
443: return new UnmarshalException(ne);
444: return new UnmarshalException(e);
445: }
446:
447: public Object getProperty(String name) throws PropertyException {
448: if (name.equals(IDResolver.class.getName())) {
449: return idResolver;
450: }
451: return super .getProperty(name);
452: }
453:
454: public void setProperty(String name, Object value)
455: throws PropertyException {
456: if (name.equals(FACTORY)) {
457: coordinator.setFactories(value);
458: return;
459: }
460: if (name.equals(IDResolver.class.getName())) {
461: idResolver = (IDResolver) value;
462: return;
463: }
464: super .setProperty(name, value);
465: }
466:
467: public static final String FACTORY = "com.sun.xml.internal.bind.ObjectFactory";
468:
469: @Override
470: public void setSchema(Schema schema) {
471: this .schema = schema;
472: }
473:
474: @Override
475: public Schema getSchema() {
476: return schema;
477: }
478:
479: @Override
480: public AttachmentUnmarshaller getAttachmentUnmarshaller() {
481: return attachmentUnmarshaller;
482: }
483:
484: @Override
485: public void setAttachmentUnmarshaller(AttachmentUnmarshaller au) {
486: this .attachmentUnmarshaller = au;
487: }
488:
489: /**
490: * @deprecated since 2.0
491: */
492: @Override
493: public boolean isValidating() {
494: throw new UnsupportedOperationException();
495: }
496:
497: /**
498: * @deprecated since 2.0
499: */
500: @Override
501: public void setValidating(boolean validating) {
502: throw new UnsupportedOperationException();
503: }
504:
505: @Override
506: public <A extends XmlAdapter> void setAdapter(Class<A> type,
507: A adapter) {
508: if (type == null)
509: throw new IllegalArgumentException();
510: coordinator.putAdapter(type, adapter);
511: }
512:
513: @Override
514: public <A extends XmlAdapter> A getAdapter(Class<A> type) {
515: if (type == null)
516: throw new IllegalArgumentException();
517: if (coordinator.containsAdapter(type))
518: // so as not to create a new instance when this method is called
519: return coordinator.getAdapter(type);
520: else
521: return null;
522: }
523:
524: // opening up for public use
525: public UnmarshalException createUnmarshalException(SAXException e) {
526: return super .createUnmarshalException(e);
527: }
528:
529: /**
530: * Default error handling behavior fot {@link Unmarshaller}.
531: */
532: public boolean handleEvent(ValidationEvent event) {
533: return event.getSeverity() != ValidationEvent.FATAL_ERROR;
534: }
535:
536: private static InputSource streamSourceToInputSource(StreamSource ss) {
537: InputSource is = new InputSource();
538: is.setSystemId(ss.getSystemId());
539: is.setByteStream(ss.getInputStream());
540: is.setCharacterStream(ss.getReader());
541:
542: return is;
543: }
544:
545: public <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz)
546: throws JAXBException {
547: return context.getBeanInfo(clazz, true);
548: }
549:
550: @Override
551: public Listener getListener() {
552: return externalListener;
553: }
554:
555: @Override
556: public void setListener(Listener listener) {
557: externalListener = listener;
558: }
559: }
|