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