1 /**
2 * Redistribution and use of this software and associated documentation
3 * ("Software"), with or without modification, are permitted provided
4 * that the following conditions are met:
5 *
6 * 1. Redistributions of source code must retain copyright
7 * statements and notices. Redistributions must also contain a
8 * copy of this document.
9 *
10 * 2. Redistributions in binary form must reproduce the
11 * above copyright notice, this list of conditions and the
12 * following disclaimer in the documentation and/or other
13 * materials provided with the distribution.
14 *
15 * 3. The name "Exolab" must not be used to endorse or promote
16 * products derived from this Software without prior written
17 * permission of Intalio, Inc. For written permission,
18 * please contact info@exolab.org.
19 *
20 * 4. Products derived from this Software may not be called "Exolab"
21 * nor may "Exolab" appear in their names without prior written
22 * permission of Intalio, Inc. Exolab is a registered
23 * trademark of Intalio, Inc.
24 *
25 * 5. Due credit should be given to the Exolab Project
26 * (http://www.exolab.org/).
27 *
28 * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32 * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Copyright 1999-2004 (C) Intalio, Inc. All Rights Reserved.
42 *
43 * $Id$
44 */
45
46 package org.exolab.castor.xml;
47
48 import java.io.PrintWriter;
49 import java.io.Reader;
50 import java.util.HashMap;
51 import java.util.Iterator;
52 import java.util.StringTokenizer;
53
54 import javax.xml.stream.XMLEventReader;
55 import javax.xml.stream.XMLStreamReader;
56 import javax.xml.transform.Source;
57 import javax.xml.transform.dom.DOMSource;
58 import javax.xml.transform.sax.SAXSource;
59 import javax.xml.transform.stream.StreamSource;
60
61 import org.apache.commons.logging.Log;
62 import org.apache.commons.logging.LogFactory;
63 import org.castor.mapping.BindingType;
64 import org.castor.mapping.MappingUnmarshaller;
65 import org.castor.xml.BackwardCompatibilityContext;
66 import org.castor.xml.InternalContext;
67 import org.castor.xml.UnmarshalListenerAdapter;
68 import org.castor.xml.XMLProperties;
69 import org.exolab.castor.mapping.Mapping;
70 import org.exolab.castor.mapping.MappingException;
71 import org.exolab.castor.mapping.MappingLoader;
72 import org.exolab.castor.types.AnyNode;
73 import org.exolab.castor.util.ObjectFactory;
74 import org.exolab.castor.xml.location.FileLocation;
75 import org.exolab.castor.xml.util.AnyNode2SAX2;
76 import org.exolab.castor.xml.util.DOMEventProducer;
77 import org.w3c.dom.Node;
78 import org.xml.sax.ContentHandler;
79 import org.xml.sax.EntityResolver;
80 import org.xml.sax.InputSource;
81 import org.xml.sax.Parser;
82 import org.xml.sax.SAXException;
83 import org.xml.sax.XMLReader;
84
85 /**
86 * An unmarshaller to allowing unmarshalling of XML documents to
87 * Java Objects. The Class must specify
88 * the proper access methods (setters/getters) in order for instances
89 * of the Class to be properly unmarshalled.
90 *
91 * @author <a href="mailto:kvisco-at-intalio.com">Keith Visco</a>
92 * @version $Revision$ $Date: 2006-02-23 14:16:51 -0700 (Thu, 23 Feb 2006) $
93 */
94 public class Unmarshaller {
95
96 /**
97 * Logger from commons-logging.
98 */
99 private static final Log LOG = LogFactory.getLog(Unmarshaller.class);
100
101 //----------------------------/
102 //- Private Member Variables -/
103 //----------------------------/
104
105 /**
106 * The Class that this Unmarshaller was created with.
107 */
108 private Class _class = null;
109
110 /**
111 * A boolean indicating whether or not collections (including
112 * arrays) should be cleared upon initial use by Castor.
113 * False by default for backward compatibility.
114 */
115 private boolean _clearCollections = false;
116
117 /**
118 * A user specified IDResolver for resolving IDREFs.
119 */
120 private IDResolver _idResolver = null;
121
122 /**
123 * A boolean that specifies whether or not
124 * non-matched attributes should be ignored upon
125 * unmarshalling.
126 */
127 private boolean _ignoreExtraAtts = true;
128
129 /**
130 * A boolean that specifies whether or not
131 * non-matched elements should be ignored upon
132 * unmarshalling.
133 */
134 private boolean _ignoreExtraElements = false;
135
136 /**
137 * The instance of _class to Unmarshal into (optional)
138 */
139 private Object _instanceObj = null;
140
141 /**
142 * The EntityResolver used for resolving entities
143 */
144 EntityResolver entityResolver = null;
145
146 /**
147 * The class loader to use
148 */
149 private ClassLoader _loader = null;
150
151 /**
152 * A boolean to indicate that objects should
153 * be re-used where appropriate
154 */
155 private boolean _reuseObjects = false;
156
157
158 /**
159 * The unmarshaller listener that listens to unmarshalling event
160 */
161 private org.castor.xml.UnmarshalListener _unmarshalListener = null;
162
163 /**
164 * The flag indicating whether or not to validate during
165 * unmarshalling
166 */
167 private boolean _validate = false;
168
169 /**
170 * A flag indicating the unmarshaller should preserve
171 * "ignorable" whitespace. The XML instance can
172 * control it's own behavior using the xml:space
173 * attribute. This sets the "default" behavior
174 * when xml:space="default".
175 */
176 private boolean _wsPreserve = false;
177
178 /**
179 * A list of namespace To Package Mappings
180 */
181 private HashMap _namespaceToPackage = null;
182
183 /**
184 * An optional factory for unmarshalling objects
185 */
186 private ObjectFactory _objectFactory;
187
188 /**
189 * The Castor XML context to use at unmarshalling.
190 */
191 private InternalContext _internalContext;
192
193 //----------------/
194 //- Constructors -/
195 //----------------/
196
197 /**
198 * An empty default constructor which:
199 * - sets the internal context to the backward compatibility context
200 * - all other flags to defaults
201 * Internally the Unmarshaller(Class) constructor is called.
202 */
203 public Unmarshaller() {
204 this((Class) null);
205 }
206
207 /**
208 * A constructor which sets the root class.
209 *
210 * Internally calls constructor Unmarshaller(InternalContext, Class) with
211 * an instance of BackwardCompatibilityContext as context.
212 *
213 * @param clazz root class for unmarshalling
214 */
215 public Unmarshaller(final Class clazz) {
216 this(new BackwardCompatibilityContext(), clazz);
217 }
218
219 /**
220 * Creates a new basic Unmarshaller.
221 *
222 * When using this constructor it will most likely be
223 * necessary to use a mapping file or ClassDescriptorResolver
224 * So that the Unmarshaller can find the classes during the
225 * unmarshalling process.
226 *
227 * @param internalContext the {@link InternalContext} to use
228 *
229 * @throws IllegalArgumentException if internalContext is null
230 */
231 public Unmarshaller(final InternalContext internalContext) {
232 this(internalContext, (Class) null, (ClassLoader) null);
233 }
234
235 /**
236 * Creates a new Unmarshaller with the given Class.
237 *
238 * @param internalContext the {@link InternalContext} to use
239 * @param c the Class to create the Unmarshaller for, this
240 * may be null, if the Unmarshaller#setMapping is called
241 * to load a mapping for the root element of xml document.
242 *
243 * @throws IllegalArgumentException if internalContext is null
244 */
245 public Unmarshaller(final InternalContext internalContext, final Class c) {
246 this(internalContext, c, null);
247 } //-- Unmarshaller(Class)
248
249 /**
250 * Creates a new {@link Unmarshaller} with the given Class.
251 *
252 * @param internalContext the {@link InternalContext} to be used, for config, and such...
253 * @param c the {@link Class} to create the {@link Unmarshaller} for, this
254 * may be null, if the Unmarshaller#setMapping is called
255 * to load a mapping for the root element of xml document.
256 * @param loader The {@link ClassLoader} to use.
257 *
258 * @throws IllegalArgumentException if internalContext is null
259 */
260 public Unmarshaller(
261 final InternalContext internalContext,
262 final Class c, final ClassLoader loader) {
263 super();
264
265 checkNotNull(internalContext, "InternalContext must not be null.");
266 setInternalContext(internalContext);
267
268 setClass(c);
269 _loader = loader;
270 if ((loader == null) && (c != null)) {
271 _loader = c.getClassLoader();
272 }
273 _internalContext.setClassLoader(_loader);
274 }
275
276 /**
277 * Creates a new Unmarshaller with the given Mapping.
278 * An instance of BackwardsCompatibilityContext is used as InternalContext.
279 *
280 * @param mapping The Mapping to use.
281 * @throws MappingException in case that Unmarshaller fails to be instantiated
282 */
283 public Unmarshaller(final Mapping mapping) throws MappingException {
284 this(new BackwardCompatibilityContext(), mapping);
285 }
286
287 /**
288 * Creates a new Unmarshaller with the given Mapping.
289 *
290 * @param internalContext the internal context to use
291 * @param mapping The Mapping to use.
292 * @throws MappingException in case that Unmarshaller fails to be instantiated
293 *
294 * @throws IllegalArgumentException if internalContext is null
295 */
296 public Unmarshaller(final InternalContext internalContext, final Mapping mapping)
297 throws MappingException {
298 this(internalContext, null, null);
299 if (mapping != null) {
300 setMapping(mapping);
301 this._loader = mapping.getClassLoader();
302 }
303 }
304
305 /**
306 * Creates a new Unmarshaller with the given Object.
307 *
308 * @param root the instance to unmarshal into. This
309 * may be null, if the Unmarshaller#setMapping is called
310 * to load a mapping for the root element of xml document.
311 */
312 public Unmarshaller(final Object root) {
313 this(new BackwardCompatibilityContext(), root);
314 }
315
316 /**
317 * Creates a new Unmarshaller with the given Object.
318 *
319 * @param internalContext the internal context to use
320 * @param root the instance to unmarshal into. This
321 * may be null, if the Unmarshaller#setMapping is called
322 * to load a mapping for the root element of xml document.
323 *
324 * @throws IllegalArgumentException if internalContext is null
325 */
326 public Unmarshaller(final InternalContext internalContext, final Object root) {
327 this(internalContext, null, null);
328 if (root != null) {
329 final Class clazz = root.getClass();
330 setClass(clazz);
331 _loader = clazz.getClassLoader();
332 }
333 _instanceObj = root;
334 }
335
336 /**
337 * Adds a mapping from the given namespace URI to the given
338 * package name.
339 *
340 * @param nsURI the namespace URI to map from
341 * @param packageName the package name to map to
342 */
343 public void addNamespaceToPackageMapping(final String nsURI, final String packageName) {
344 if (_namespaceToPackage == null) {
345 _namespaceToPackage = new HashMap();
346 }
347 String iNsUri = (nsURI == null) ? "" : nsURI;
348 String iPackageName = (packageName == null) ? "" : packageName;
349 _namespaceToPackage.put(iNsUri, iPackageName);
350
351 } //-- addNamespaceToPackageMapping
352
353
354 /**
355 * Creates and initalizes an UnmarshalHandler
356 * @return the new UnmarshalHandler
357 **/
358 public UnmarshalHandler createHandler() {
359
360 UnmarshalHandler handler = new UnmarshalHandler(_internalContext, _class);
361
362 handler.setClearCollections(_clearCollections);
363 handler.setReuseObjects(_reuseObjects);
364 handler.setValidation(_validate);
365 handler.setIgnoreExtraAttributes(_ignoreExtraAtts);
366 handler.setIgnoreExtraElements(_ignoreExtraElements);
367 handler.setInternalContext(_internalContext);
368 handler.setWhitespacePreserve(_wsPreserve);
369
370 // If the object factory has been set, set it on the handler
371 if (this._objectFactory != null) {
372 handler.setObjectFactory(this._objectFactory);
373 }
374
375 //-- copy namespaceToPackageMappings
376 if (_namespaceToPackage != null) {
377 Iterator keys = _namespaceToPackage.keySet().iterator();
378 while (keys.hasNext()) {
379 String nsURI = (String)keys.next();
380 String pkgName = (String) _namespaceToPackage.get(nsURI);
381 handler.addNamespaceToPackageMapping(nsURI, pkgName);
382 }
383 }
384
385 if (_instanceObj != null) {
386 handler.setRootObject(_instanceObj);
387 }
388 if (_idResolver != null)
389 handler.setIDResolver(_idResolver);
390
391 if (_loader != null)
392 handler.setClassLoader(_loader);
393
394 if (_unmarshalListener != null)
395 handler.setUnmarshalListener(_unmarshalListener);
396
397 return handler;
398 } //-- createHandler
399
400 /**
401 * Indicates whether or not validation should be performed during umarshalling.
402 * @return True if validation is performed during umarshalling.
403 */
404 public boolean isValidating() {
405 return _validate;
406 }
407
408 /**
409 * Sets the 'expected' {@link Class} instance on the Unmarshaller.
410 *
411 * @param clazz the Class to create the Unmarshaller for, this
412 * may be null, if the Unmarshaller#setMapping is called
413 * to load a mapping for the root element of xml document.
414 */
415 public void setClass(Class clazz) {
416 _class = clazz;
417 } //-- setClass(Class)
418
419 /**
420 * Sets the 'expected' {@link Object} instance on the Unmarshaller, into
421 * which will be unmarshalled.
422 *
423 * @param root the instance to unmarshal into. This
424 * may be null, if the Unmarshaller#setMapping is called
425 * to load a mapping for the root element of xml document.
426 */
427 public void setObject(Object root) {
428 _instanceObj = root;
429 } //-- setObject(Object)
430
431 /**
432 * Sets the ClassLoader to use when loading new classes.
433 * <br />
434 * <b>Note:</b>This ClassLoader is used for classes
435 * loaded by the unmarshaller only. If a Mapping has
436 * been set, the Mapping has it's own ClassLoader and
437 * may also need to be set propertly.
438 * <br />
439 *
440 * @param loader the ClassLoader to use
441 **/
442 public void setClassLoader(ClassLoader loader) {
443 this._loader = loader;
444 } //-- setClassLoader
445
446
447 /**
448 * Sets whether or not to clear collections (including arrays)
449 * upon first use to remove default values. By default, and
450 * for backward compatibility with previous versions of Castor
451 * this value is false, indicating that collections are not
452 * cleared before initial use by Castor.
453 *
454 * @param clear the boolean value that when true indicates
455 * collections should be cleared upon first use.
456 */
457 public void setClearCollections(boolean clear) {
458 _clearCollections = clear;
459 } //-- setClearCollections
460
461 /**
462 * Custom debugging is replaced with commons-logging
463 * @deprecated
464 **/
465 public void setDebug(boolean debug) {
466 // no-op
467 } //-- setDebug
468
469 /**
470 * Sets the EntityResolver to use when resolving system and
471 * public ids with respect to entites and Document Type.
472 * @param entityResolver the EntityResolver to use when
473 * resolving System and Public ids.
474 **/
475 public void setEntityResolver(EntityResolver entityResolver) {
476 this.entityResolver = entityResolver;
477 } //-- entityResolver
478
479 /**
480 * Sets the IDResolver to use when resolving IDREFs for
481 * which no associated element may exist in XML document.
482 *
483 * @param idResolver the IDResolver to use when resolving
484 * IDREFs for which no associated element may exist in the
485 * XML document.
486 **/
487 public void setIDResolver(IDResolver idResolver) {
488 _idResolver = idResolver;
489 } //-- idResolver
490
491 /**
492 * Sets whether or not attributes that do not match
493 * a specific field should simply be ignored or
494 * reported as an error. By default, extra attributes
495 * are ignored.
496 *
497 * @param ignoreExtraAtts a boolean that when true will
498 * allow non-matched attributes to simply be ignored.
499 */
500 public void setIgnoreExtraAttributes(boolean ignoreExtraAtts) {
501 _ignoreExtraAtts = ignoreExtraAtts;
502 } //-- setIgnoreExtraAttributes
503
504 /**
505 * Sets whether or not elements that do not match
506 * a specific field should simply be ignored or
507 * reported as an error. By default, extra elements
508 * are flagged as an error.
509 *
510 * @param ignoreExtraElements a boolean that when true will
511 * allow non-matched elements to simply be ignored.
512 */
513 public void setIgnoreExtraElements(boolean ignoreExtraElements) {
514 _ignoreExtraElements = ignoreExtraElements;
515 } //-- setIgnoreExtraElements
516
517 /**
518 * Logging is replaced with commons-logging.
519 * @param printWriter the PrintWriter to use for logging
520 * @deprecated
521 **/
522 public void setLogWriter(PrintWriter printWriter) {
523 // no-op
524 } //-- setLogWriter
525
526 /**
527 * Sets the Mapping to use during unmarshalling. If the Mapping has a ClassLoader it
528 * will be used during unmarshalling.
529 *
530 * @param mapping Mapping to use during unmarshalling.
531 * @see #setResolver
532 */
533 public void setMapping(final Mapping mapping) throws MappingException {
534 if (_loader == null) {
535 _loader = mapping.getClassLoader();
536 }
537
538 MappingUnmarshaller mum = new MappingUnmarshaller();
539 MappingLoader resolver = mum.getMappingLoader(mapping, BindingType.XML);
540 _internalContext.getXMLClassDescriptorResolver().setMappingLoader(resolver);
541 }
542
543 /**
544 * Sets a boolean that when true indicates that objects
545 * contained within the object model should be re-used
546 * where appropriate. This is only valid when unmarshalling
547 * to an existing object.
548 *
549 * @param reuse the boolean indicating whether or not
550 * to re-use existing objects in the object model.
551 **/
552 public void setReuseObjects(boolean reuse) {
553 _reuseObjects = reuse;
554 } //-- setReuseObjects
555
556 /**
557 * Sets an optional {@link org.exolab.castor.xml.UnmarshalListener} to receive pre and
558 * post unmarshal notification for each Object in the tree.
559 * An UnmarshalListener is often used to allow objects to
560 * appropriately initialize themselves by taking application
561 * specific behavior as they are unloaded.
562 * Current only one (1) listener is allowed. If you need
563 * register multiple listeners, you will have to create
564 * your own master listener that will forward the
565 * event notifications and manage the multiple
566 * listeners.<br/>
567 * The deprecated listener set with this method will be wrapped by an
568 * adapter.
569 *
570 * @param listener the {@link org.exolab.castor.xml.UnmarshalListener} to set.
571 * @deprecated replaced by {@link org.castor.xml.UnmarshalListener}
572 */
573 public void setUnmarshalListener(org.exolab.castor.xml.UnmarshalListener listener) {
574 if (listener == null) {
575 _unmarshalListener = null;
576 } else {
577 UnmarshalListenerAdapter adapter = new UnmarshalListenerAdapter();
578 adapter.setOldListener(listener);
579 _unmarshalListener = adapter;
580 }
581 }
582
583 /**
584 * Sets an optional {@link org.castor.xml.UnmarshalListener} to receive pre and
585 * post unmarshal notification for each Object in the tree.
586 * An UnmarshalListener is often used to allow objects to
587 * appropriately initialize themselves by taking application
588 * specific behavior as they are unloaded.
589 * Current only one (1) listener is allowed. If you need
590 * register multiple listeners, you will have to create
591 * your own master listener that will forward the
592 * event notifications and manage the multiple
593 * listeners.
594 *
595 * @param listener the {@link org.castor.xml.UnmarshalListener} to set.
596 */
597 public void setUnmarshalListener(org.castor.xml.UnmarshalListener listener) {
598 _unmarshalListener = listener;
599 }
600
601 /**
602 * Sets the flag for validation.
603 *
604 * @param validate A boolean to indicate whether or not validation should be done
605 * during umarshalling.
606 * <br/>
607 * By default validation will be performed.
608 */
609 public void setValidation(boolean validate) {
610 _validate = validate;
611 } //-- setValidation
612
613 /**
614 * Sets the top-level whitespace (xml:space) to either
615 * preserving or non preserving. The XML document
616 * can override this value using xml:space on specific
617 * elements.This sets the "default" behavior
618 * when xml:space="default".
619 *
620 * @param preserve a boolean that when true enables
621 * whitespace preserving by default.
622 */
623 public void setWhitespacePreserve(boolean preserve) {
624 _wsPreserve = preserve;
625 } //-- setWhitespacePreserve
626
627 /**
628 * Unmarshals Objects of this Unmarshaller's Class type.
629 * The Class must specify the proper access methods
630 * (setters/getters) in order for instances of the Class
631 * to be properly unmarshalled.
632 * @param reader the Reader to read the XML from
633 * @exception MarshalException when there is an error during
634 * the unmarshalling process
635 * @exception ValidationException when there is a validation error
636 **/
637 public Object unmarshal(Reader reader)
638 throws MarshalException, ValidationException
639 {
640 return unmarshal(new InputSource(reader));
641 } //-- unmarshal(Reader reader)
642
643 /**
644 * Unmarshals Objects of this Unmarshaller's Class type.
645 * The Class must specify the proper access methods
646 * (setters/getters) in order for instances of the Class
647 * to be properly unmarshalled.
648 * @param eventProducer the EventProducer which produces
649 * the SAX events
650 * @exception MarshalException when there is an error during
651 * the unmarshalling process
652 * @exception ValidationException when there is a validation error
653 * @deprecated please use @see #unmarshal(SAX2EventProducer) instead.
654 **/
655 public Object unmarshal(EventProducer eventProducer)
656 throws MarshalException, ValidationException
657 {
658 UnmarshalHandler handler = createHandler();
659 eventProducer.setDocumentHandler(handler);
660 try {
661 eventProducer.start();
662 }
663 catch(org.xml.sax.SAXException sx) {
664 convertSAXExceptionToMarshalException(handler, sx);
665 }
666 return handler.getObject();
667
668 } //-- unmarshal(EventProducer)
669
670 /**
671 * Unmarshals Objects of this Unmarshaller's Class type.
672 * The Class must specify the proper access methods
673 * (setters/getters) in order for instances of the Class
674 * to be properly unmarshalled.
675 * @param eventProducer the SAX2EventProducer instance which produces
676 * the SAX 2 events
677 * @exception MarshalException when there is an error during
678 * the unmarshalling process
679 * @exception ValidationException when there is a validation error
680 * @since 1.0M3
681 **/
682 public Object unmarshal(SAX2EventProducer eventProducer)
683 throws MarshalException, ValidationException
684 {
685 UnmarshalHandler handler = createHandler();
686 eventProducer.setContentHandler(handler);
687 try {
688 eventProducer.start();
689 }
690 catch(org.xml.sax.SAXException sx) {
691 convertSAXExceptionToMarshalException(handler, sx);
692 }
693 return handler.getObject();
694
695 } //-- unmarshal(SAX2EventProducer)
696
697 /**
698 * Unmarshals objects of this {@link Unmarshaller}'s Class type
699 * from an {@link AnyNode} instance.
700 *
701 * The Class must specify the proper access methods
702 * (setters/getters) in order for instances of the Class
703 * to be properly unmarshalled.
704 *
705 * @param anyNode {@link AnyNode} instance to be unmarshalled from
706 * @exception MarshalException when there is an error during
707 * the unmarshalling process
708 * @return The {@link Object} instance that is a result of unmarshalling.
709 **/
710 public Object unmarshal(final AnyNode anyNode)
711 throws MarshalException {
712 UnmarshalHandler handler = createHandler();
713 try {
714 AnyNode2SAX2.fireEvents(anyNode, handler);
715 } catch (SAXException sex) {
716 convertSAXExceptionToMarshalException(handler, sex);
717 }
718 return handler.getObject();
719 }
720
721 /**
722 * Unmarshals Objects of this Unmarshaller's Class type.
723 * The Class must specify the proper access methods
724 * (setters/getters) in order for instances of the Class
725 * to be properly unmarshalled.
726 * @param source the InputSource to read the XML from
727 * @exception MarshalException when there is an error during
728 * the unmarshalling process
729 * @exception ValidationException when there is a validation error
730 **/
731 public Object unmarshal(InputSource source)
732 throws MarshalException, ValidationException
733 {
734 XMLReader reader = null;
735 Parser parser = null;
736
737 //-- First try XMLReader
738 try {
739 reader = _internalContext.getXMLReader();
740 if (entityResolver != null) {
741 reader.setEntityResolver(entityResolver);
742 }
743 } catch (RuntimeException rx) {
744 LOG.debug("Unable to create SAX XMLReader, attempting SAX Parser.");
745 }
746
747 if (reader == null) {
748 parser = _internalContext.getParser();
749 if (parser == null)
750 throw new MarshalException("Unable to create SAX Parser.");
751 if (entityResolver != null)
752 parser.setEntityResolver(entityResolver);
753 }
754
755
756 UnmarshalHandler handler = createHandler();
757
758
759 try {
760 if (reader != null) {
761 reader.setContentHandler(handler);
762 reader.setErrorHandler(handler);
763 reader.parse(source);
764 }
765 else {
766 parser.setDocumentHandler(handler);
767 parser.setErrorHandler(handler);
768 parser.parse(source);
769 }
770 }
771 catch (java.io.IOException ioe) {
772 throw new MarshalException(ioe);
773 }
774 catch (org.xml.sax.SAXException sx) {
775 convertSAXExceptionToMarshalException(handler, sx);
776 }
777
778 return handler.getObject();
779 } //-- unmarshal(InputSource)
780
781
782 /**
783 * Unmarshals Objects of this Unmarshaller's Class type.
784 * The Class must specify the proper access methods
785 * (setters/getters) in order for instances of the Class
786 * to be properly unmarshalled.
787 * @param node the DOM node to read the XML from
788 * @exception MarshalException when there is an error during
789 * the unmarshalling process
790 * @exception ValidationException when there is a validation error
791 **/
792 public Object unmarshal(Node node)
793 throws MarshalException, ValidationException
794 {
795 return unmarshal(new DOMEventProducer(node));
796 } //-- unmarshal(EventProducer)
797
798 /**
799 * Unmarshals objects of this {@link Unmarshaller}'s class type. The class must
800 * specify the proper access methods (setters/getters) in order for
801 * instances of the class to be properly unmarshalled.
802 *
803 * @param eventReader
804 * the StaX {@link XMLEventReader} to read XML from.
805 * @exception MarshalException
806 * indicates a general problem during the unmarshalling process.
807 * @throws ValidationException
808 * indicates a problem related to validation.
809 *
810 * @since 1.3.2
811 **/
812 public Object unmarshal(XMLEventReader eventReader)
813 throws MarshalException, ValidationException {
814 return unmarshal(BaseSax2EventFromStaxProducer.createSax2EventFromStax(eventReader));
815 }
816
817 /**
818 * Unmarshals objects of this {@link Unmarshaller}'s class type. The class must
819 * specify the proper access methods (setters/getters) in order for
820 * instances of the class to be properly unmarshalled.
821 *
822 * @param streamReader
823 * the STaX {@link XMLStreamReader} to read XML from.
824 * @exception MarshalException
825 * indicates a general problem during the unmarshalling process.
826 * @throws ValidationException
827 * indicates a problem related to validation.
828 *
829 * @since 1.3.2
830 **/
831 public Object unmarshal(XMLStreamReader streamReader)
832 throws MarshalException, ValidationException {
833 return unmarshal(BaseSax2EventFromStaxProducer.createSax2EventFromStax(streamReader));
834 }
835
836 /**
837 * Unmarshals objects of this {@link Unmarshaller}'s class type. <br/>
838 * The class must specify the proper access methods (setters/getters) in
839 * order for instances of the class to be properly unmarshalled. </br/>
840 *
841 * @param eventProducer
842 * the {@link SAX2EventAndErrorProducer} instance which produces
843 * the SAX 2 events and handles SAX 2 errors.
844 * @exception MarshalException
845 * indiactes a general error during the unmarshalling
846 * process.
847 * @exception ValidationException
848 * indicates a validation error.
849 * @since 1.3.2
850 **/
851 public Object unmarshal(SAX2EventAndErrorProducer eventProducer)
852 throws MarshalException, ValidationException
853 {
854 UnmarshalHandler handler = createHandler();
855 eventProducer.setContentHandler(handler);
856 eventProducer.setErrorHandler(handler);
857 try {
858 eventProducer.start();
859 }
860 catch(org.xml.sax.SAXException sx) {
861 convertSAXExceptionToMarshalException(handler, sx);
862 }
863 return handler.getObject();
864
865 }
866
867 /**
868 * Unmarshals ths given {@link Source} instance. Currently this method will support fallowing classes {@link
869 * DOMSource}, {@link SAXSource} and {@link StreamSource}.
870 *
871 * @param source the source to unmarshal
872 *
873 * @return the unmarshalled object instance
874 *
875 * @throws IllegalArgumentException if the given source is null or it is unsupported
876 * @throws MarshalException indiactes a general error during the unmarshalling process.
877 * @throws ValidationException indicates a validation error.
878 */
879 public Object unmarshal(Source source) throws MarshalException, ValidationException {
880 checkNotNull(source, "The given 'javax.xml.transform.Source' instance is null.");
881
882 if (source instanceof DOMSource) {
883 DOMSource domSource = (DOMSource) source;
884 if (domSource.getNode() != null) {
885 return unmarshal(domSource.getNode());
886 }
887 } else if (source instanceof SAXSource) {
888 SAXSource saxSource = (SAXSource) source;
889
890 if (saxSource.getInputSource() != null) {
891 // TODO should the XMLReader from the SAXSource should be used instead ?
892 return unmarshal(saxSource.getInputSource());
893 }
894 } else if (source instanceof StreamSource) {
895 StreamSource streamSource = (StreamSource) source;
896
897 if (streamSource.getInputStream() != null) {
898 return unmarshal(new InputSource(streamSource.getInputStream()));
899 } else if (streamSource.getReader() != null) {
900 return unmarshal(streamSource.getReader());
901 }
902 }
903
904 throw new IllegalArgumentException(
905 "The given 'javax.transform.xml.Source' is not supported, or were incorrectly instantiated.");
906 }
907
908 /**
909 * Converts a SAXException to a (localised) MarshalException.
910 * @param handler The {@link UnmarshalHandler} required to obtain DocumentLocator instance.
911 * @param sex The {@link SAXException} instance
912 * @throws MarshalException The {@link MarshalException} instance derived from the SAX exception.
913 */
914 private void convertSAXExceptionToMarshalException(UnmarshalHandler handler, SAXException sex) throws MarshalException {
915 Exception except = sex.getException();
916 if (except == null) {
917 except = sex;
918 }
919 MarshalException marshalEx = new MarshalException(except);
920 if (handler.getDocumentLocator() != null) {
921 FileLocation location = new FileLocation();
922 location.setFilename(handler.getDocumentLocator().getSystemId());
923 location.setLineNumber(handler.getDocumentLocator().getLineNumber());
924 location.setColumnNumber(handler.getDocumentLocator().getColumnNumber());
925 marshalEx.setLocation(location);
926 }
927 throw marshalEx;
928 }
929
930 //-------------------------/
931 //- Public Static Methods -/
932 //-------------------------/
933 /**
934 * Returns a ContentHandler for the given UnmarshalHandler
935 *
936 * @return the ContentHandler
937 */
938 public static ContentHandler getContentHandler(UnmarshalHandler handler)
939 throws SAXException
940 {
941 return handler;
942 } //-- getContentHandler
943
944 /**
945 * Unmarshals Objects of the given Class type. The Class must specify
946 * the proper access methods (setters/getters) in order for instances
947 * of the Class to be properly unmarshalled.
948 *
949 * <p><b>Note:</b>This is a *static* method, any mapping files set
950 * on a particular Unmarshaller instance, and any changes made
951 * via setters will be unavailable to this method.</p>
952 *
953 * @param c the Class to create a new instance of
954 * @param reader the Reader to read the XML from
955 * @exception MarshalException when there is an error during
956 * the unmarshalling process
957 * @exception ValidationException when there is a validation error
958 **/
959 public static Object unmarshal(Class c, Reader reader)
960 throws MarshalException, ValidationException
961 {
962 Unmarshaller unmarshaller = createUnmarshaller(c);
963 return unmarshaller.unmarshal(reader);
964 } //-- void unmarshal(Writer)
965
966 /**
967 * Helper method for static #unmarshal methods to create
968 * an {@link Unmarshaller} instance.
969 *
970 * @param clazz The root class to be used during unmarshalling.
971 * @return An {@link Unmarshaller} instance.
972 */
973 private static Unmarshaller createUnmarshaller(final Class clazz) {
974 XMLContext xmlContext = new XMLContext();
975 Unmarshaller unmarshaller = xmlContext.createUnmarshaller();
976 unmarshaller.setClass(clazz);
977
978 // TODO: Should this be at level INFO?
979 if (LOG.isDebugEnabled()) {
980 LOG.debug("*static* unmarshal method called, this will ignore any "
981 + "mapping files or changes made to an Unmarshaller instance.");
982 }
983
984 //-- for backward compatibility with Castor versions
985 //-- prior to version 0.9.5.3
986 unmarshaller.setWhitespacePreserve(true);
987
988 return unmarshaller;
989 }
990
991 /**
992 * Unmarshals Objects of the given Class type. The Class must specify
993 * the proper access methods (setters/getters) in order for instances
994 * of the Class to be properly unmarshalled.
995 *
996 * <p><b>Note:</b>This is a *static* method, any mapping files set
997 * on a particular Unmarshaller instance, and any changes made
998 * via setters will be unavailable to this method.</p>
999 *
1000 * @param c the Class to create a new instance of
1001 * @param source the InputSource to read the XML from
1002 * @exception MarshalException when there is an error during
1003 * the unmarshalling process
1004 * @exception ValidationException when there is a validation error
1005 */
1006 public static Object unmarshal(Class c, InputSource source)
1007 throws MarshalException, ValidationException
1008 {
1009 Unmarshaller unmarshaller = createUnmarshaller(c);
1010
1011 return unmarshaller.unmarshal(source);
1012 } //-- void unmarshal(Writer)
1013
1014 /**
1015 * Unmarshals Objects of the given Class type. The Class must specify
1016 * the proper access methods (setters/getters) in order for instances
1017 * of the Class to be properly unmarshalled.
1018 *
1019 * <p><b>Note:</b>This is a *static* method, any mapping files set
1020 * on a particular Unmarshaller instance, and any changes made
1021 * via setters will be unavailable to this method.</p>
1022 *
1023 * @param c The Class to create a new instance of.
1024 * @param node The DOM Node to read the XML from.
1025 * @exception MarshalException When there is an error during the unmarshalling
1026 * process.
1027 * @exception ValidationException When there is a validation error.
1028 */
1029 public static Object unmarshal(Class c, Node node)
1030 throws MarshalException, ValidationException {
1031 Unmarshaller unmarshaller = createUnmarshaller(c);
1032
1033 return unmarshaller.unmarshal(node);
1034 } //-- void unmarshal(Writer)
1035
1036 /**
1037 * Set an object factory for the unmarshaller. This factory will be used to
1038 * construct the objects being unmarshalled.
1039 *
1040 * @param objectFactory
1041 * Factory used for constructing objects during unmarshalling.
1042 */
1043 public void setObjectFactory(final ObjectFactory objectFactory) {
1044 this._objectFactory = objectFactory;
1045 } // -- setObjectFactory
1046
1047 /**
1048 * Returns the value of the given Castor XML-specific property.
1049 *
1050 * @param name
1051 * Qualified name of the CASTOR XML-specific property.
1052 * @return The current value of the given property.
1053 * @since 1.1.2
1054 */
1055 public String getProperty(final String name) {
1056 Object propertyValue = _internalContext.getProperty(name);
1057 if ((propertyValue != null) && !(propertyValue instanceof String)) {
1058 String message =
1059 "Requested property: " + name
1060 + " is not of type String, but: " + propertyValue.getClass()
1061 + " throwing IllegalStateException.";
1062 LOG.warn(message);
1063 throw new IllegalStateException(message);
1064 }
1065 return (String) propertyValue;
1066 }
1067
1068 /**
1069 * Sets a custom value of a given Castor XML-specific property.
1070 *
1071 * @param name
1072 * Name of the Castor XML property
1073 * @param value
1074 * Custom value to set.
1075 * @since 1.1.2
1076 */
1077 public void setProperty(final String name, final String value) {
1078 _internalContext.setProperty(name, value);
1079 }
1080
1081 /**
1082 * To set the internal XML Context to be used.
1083 * @param internalContext the context to be used
1084 */
1085 public void setInternalContext(final InternalContext internalContext) {
1086 _internalContext = internalContext;
1087 deriveProperties();
1088 }
1089
1090 /**
1091 * Derive class-level properties from {@link XMLProperties} as defined
1092 * {@link InternalContext}. This method will be called after a new {@link InternalContext}
1093 * has been set.
1094 * @link #setInternalContext(InternalContext)
1095 */
1096 private void deriveProperties() {
1097 _validate = _internalContext.marshallingValidation();
1098 _ignoreExtraElements = (!_internalContext.strictElements());
1099
1100 //-- process namespace to package mappings
1101 String mappings =
1102 _internalContext.getStringProperty(XMLProperties.NAMESPACE_PACKAGE_MAPPINGS);
1103 if (mappings != null && mappings.length() > 0) {
1104 StringTokenizer tokens = new StringTokenizer(mappings, ",");
1105 while (tokens.hasMoreTokens()) {
1106 String token = tokens.nextToken();
1107 int sepIdx = token.indexOf('=');
1108 if (sepIdx < 0) {
1109 continue;
1110 }
1111 String ns = token.substring(0, sepIdx).trim();
1112 String javaPackage = token.substring(sepIdx + 1).trim();
1113 addNamespaceToPackageMapping(ns, javaPackage);
1114 }
1115 }
1116 }
1117
1118 /**
1119 * To get the internal XML Context that is in use.
1120 * @return the {@link InternalContext} in use
1121 */
1122 public InternalContext getInternalContext() {
1123 return _internalContext;
1124 }
1125
1126 /**
1127 * Sets the XMLClassDescriptorResolver to use during unmarshalling
1128 * @param xmlClassDescriptorResolver the XMLClassDescriptorResolver to use
1129 * @see #setMapping
1130 * <BR />
1131 * <B>Note:</B> This method will nullify any Mapping
1132 * currently being used by this Unmarshaller
1133 */
1134 public void setResolver(XMLClassDescriptorResolver xmlClassDescriptorResolver) {
1135 _internalContext.setResolver(xmlClassDescriptorResolver);
1136 }
1137
1138 /**
1139 * Checks if passed parameter is not null. In case it is, a {@link IllegalArgumentException} is thrown.
1140 *
1141 * @param param the parameter to check
1142 * @param msg the error message to use for thrown exception
1143 *
1144 * @throws IllegalArgumentException if param is null
1145 */
1146 private static void checkNotNull(Object param, String msg) {
1147
1148 if (param == null) {
1149 throw new IllegalArgumentException(msg);
1150 }
1151 }
1152 } //-- Unmarshaller
1153