View Javadoc
1   /**
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 1999-2004 (C) Intalio, Inc. All Rights Reserved.
32   *
33   * Portions of this file developed by Keith Visco after Jan 19 2005 are Copyright (C) Keith Visco.
34   * All Rights Reserverd.
35   *
36   * $Id$
37   */
38  package org.exolab.castor.xml;
39  
40  import java.io.PrintWriter;
41  import java.io.Serializable;
42  import java.lang.reflect.Array;
43  import java.lang.reflect.InvocationTargetException;
44  import java.lang.reflect.Method;
45  import java.lang.reflect.Modifier;
46  import java.text.MessageFormat;
47  import java.util.ArrayList;
48  import java.util.HashMap;
49  import java.util.Hashtable;
50  import java.util.Locale;
51  import java.util.Map;
52  import java.util.ResourceBundle;
53  import java.util.StringTokenizer;
54  
55  import org.apache.commons.lang3.StringUtils;
56  import org.apache.commons.logging.Log;
57  import org.apache.commons.logging.LogFactory;
58  import org.castor.core.util.Base64Decoder;
59  import org.castor.core.util.HexDecoder;
60  import org.castor.xml.InternalContext;
61  import org.exolab.castor.mapping.ClassDescriptor;
62  import org.exolab.castor.mapping.FieldHandler;
63  import org.exolab.castor.mapping.MapItem;
64  import org.exolab.castor.util.DefaultObjectFactory;
65  import org.exolab.castor.util.ObjectFactory;
66  import org.exolab.castor.xml.descriptors.PrimitivesClassDescriptor;
67  import org.exolab.castor.xml.descriptors.StringClassDescriptor;
68  import org.exolab.castor.xml.parsing.AnyNodeUnmarshalHandler;
69  import org.exolab.castor.xml.parsing.AttributeSetBuilder;
70  import org.exolab.castor.xml.parsing.NamespaceHandling;
71  import org.exolab.castor.xml.parsing.StrictElementHandler;
72  import org.exolab.castor.xml.parsing.UnmarshalListenerDelegate;
73  import org.exolab.castor.xml.parsing.UnmarshalStateStack;
74  import org.exolab.castor.xml.parsing.primitive.objects.PrimitiveObjectFactory;
75  import org.exolab.castor.xml.util.XMLFieldDescriptorImpl;
76  import org.xml.sax.AttributeList;
77  import org.xml.sax.Attributes;
78  import org.xml.sax.ContentHandler;
79  import org.xml.sax.DocumentHandler;
80  import org.xml.sax.ErrorHandler;
81  import org.xml.sax.Locator;
82  import org.xml.sax.SAXException;
83  import org.xml.sax.SAXParseException;
84  
85  /**
86   * An unmarshaller to allowing unmarshaling of XML documents to Java Objects. The Class must specify
87   * the proper access methods (setters/getters) in order for instances of the Class to be properly
88   * unmarshaled.
89   * 
90   * @author <a href="mailto:keith AT kvisco DOT com">Keith Visco</a>
91   * @version $Revision$ $Date: 2006-05-25 06:41:12 -0600 (Thu, 25 May 2006) $
92   */
93  public final class UnmarshalHandler extends MarshalFramework
94      implements ContentHandler, DocumentHandler, ErrorHandler {
95    /**
96     * Logger from commons-logging.
97     */
98    static final Log LOG = LogFactory.getLog(UnmarshalHandler.class);
99  
100   /** resource bundle */
101   protected static ResourceBundle resourceBundle;
102 
103   static {
104     resourceBundle = ResourceBundle.getBundle("UnmarshalHandlerMessages", Locale.getDefault());
105   }
106 
107   // ---------------------------/
108   // - Private Class Variables -/
109   // ---------------------------/
110 
111   /**
112    * The error message when no class descriptor has been found TODO: move to resource bundle
113    */
114   private static final String ERROR_DID_NOT_FIND_CLASSDESCRIPTOR =
115       "unable to find or create a ClassDescriptor for class: ";
116 
117   /**
118    * The built-in XML prefix used for xml:space, xml:lang and, as the XML 1.0 Namespaces document
119    * specifies, are reserved for use by XML and XML related specs.
120    **/
121   private static final String XML_PREFIX = "xml";
122 
123   /**
124    * Attribute name for default namespace declaration
125    **/
126   private static final String XMLNS = "xmlns";
127 
128   /**
129    * Attribute prefix for prefixed namespace declaration.
130    **/
131   private final static String XMLNS_PREFIX = "xmlns:";
132 
133   /**
134    * The type attribute (xsi:type) used to denote the XML Schema type of the parent element
135    **/
136   private static final String XSI_TYPE = "type";
137 
138   static final String XML_SPACE = "space";
139   static final String XML_SPACE_WITH_PREFIX = "xml:space";
140   static final String PRESERVE = "preserve";
141 
142   // ----------------------------/
143   // - Private Member Variables -/
144   // ----------------------------/
145 
146   private UnmarshalState _topState = null;
147   private Class<?> _topClass = null;
148 
149   /**
150    * The top-level instance object, this may be set by the user by calling #setRootObject();.
151    **/
152   private Object _topObject = null;
153 
154   /**
155    * Indicates whether or not collections should be cleared upon first use (to remove default
156    * values, or old values). False by default for backward compatibility.
157    */
158   private boolean _clearCollections = false;
159 
160   /**
161    * The SAX Document Locator.
162    **/
163   private Locator _locator = null;
164 
165   /**
166    * The IDResolver for resolving IDReferences.
167    **/
168   private IDResolver _idResolver = null;
169 
170   /**
171    * A flag indicating whether or not to perform validation.
172    **/
173   private boolean _validate = true;
174 
175   /**
176    * Hashtable to store idReference and ReferenceInfo
177    */
178   private Hashtable<String, ReferenceInfo> _resolveTable = new Hashtable<String, ReferenceInfo>();
179 
180   private Map<Class<?>, String> _javaPackages = null;
181 
182   private ClassLoader _loader = null;
183 
184   private static final StringClassDescriptor STRING_DESCRIPTOR = new StringClassDescriptor();
185 
186   /**
187    * The AnyNode to add (if any).
188    */
189   private org.exolab.castor.types.AnyNode _node = null;
190 
191   /**
192    * A reference to the ObjectFactory used to create instances of the classes if the FieldHandler is
193    * not used.
194    */
195   private ObjectFactory _objectFactory = new DefaultObjectFactory();
196 
197   /**
198    * A boolean to indicate that objects should be re-used where appropriate.
199    **/
200   private boolean _reuseObjects = false;
201 
202   /**
203    * A boolean that indicates attribute processing should be strict and an error should be flagged
204    * if any extra attributes exist.
205    **/
206   private boolean _strictAttributes = false;
207 
208   /**
209    * The top-level xml:space value.
210    */
211   private boolean _wsPreserve = false;
212 
213   /**
214    * {@link StrictElementHandler} that deals with (potentially) ignorable content.
215    */
216   private StrictElementHandler _strictElementHandler = new StrictElementHandler();
217 
218   /**
219    * {@link UnmarshalStateStack} that saves UnmarshalStates on a stack.
220    */
221   private UnmarshalStateStack _stateStack = new UnmarshalStateStack();
222 
223   /**
224    * {@link UnmarshalListenerDelegate} that deals with UnmarshalListener calls.
225    */
226   private UnmarshalListenerDelegate _delegateUnmarshalListener = new UnmarshalListenerDelegate();
227 
228   private AnyNodeUnmarshalHandler _anyNodeHandler = null;
229 
230   private NamespaceHandling _namespaceHandling = new NamespaceHandling();
231 
232   private AttributeSetBuilder _attributeSetFactory = null;
233 
234   // ----------------/
235   // - Constructors -/
236   // ----------------/
237 
238   /**
239    * Creates a new UnmarshalHandler The "root" class will be obtained by looking into the mapping
240    * for a descriptor that matches the root element.
241    **/
242   protected UnmarshalHandler() {
243     this(null);
244   }
245 
246   /**
247    * Creates a new UnmarshalHandler.
248    * 
249    * @param topClass the Class to create the UnmarshalHandler for
250    */
251   protected UnmarshalHandler(final Class<?> topClass) {
252     this(null, topClass);
253   }
254 
255   /**
256    * Creates a new UnmarshalHandler.
257    * 
258    * @param internalContext the {@link InternalContext} to use
259    * @param topClass the Class to work for
260    */
261   protected UnmarshalHandler(final InternalContext internalContext, final Class<?> topClass) {
262     super(internalContext);
263     _idResolver = new IDResolverImpl();
264     _javaPackages = new HashMap<Class<?>, String>();
265     _topClass = topClass;
266     _anyNodeHandler = new AnyNodeUnmarshalHandler(_namespaceHandling);
267     _attributeSetFactory = new AttributeSetBuilder(_namespaceHandling);
268   }
269 
270   /**
271    * Adds a mapping from the given namespace URI to the given package name
272    * 
273    * @param nsURI the namespace URI to map from
274    * @param packageName the package name to map to
275    */
276   public void addNamespaceToPackageMapping(String nsURI, String packageName) {
277     _namespaceHandling.addNamespaceToPackageMapping(nsURI, packageName);
278 
279   } // -- addNamespaceToPackageMapping
280 
281   /**
282    * Returns the Object that the UnmarshalHandler is currently handling (within the object model),
283    * or null if the current element is a simpleType.
284    *
285    * @return the Object currently being unmarshalled, or null if the current element is a
286    *         simpleType.
287    */
288   public Object getCurrentObject() {
289     if (!_stateStack.isEmpty()) {
290       UnmarshalState state = _stateStack.getLastState();
291       if (state != null) {
292         return state.getObject();
293       }
294     }
295     return null;
296   } // -- getCurrentObject
297 
298   /**
299    * Returns the "root" Object (ie. the entire object model) being unmarshalled.
300    *
301    * @return the root Object being unmarshalled.
302    **/
303   public Object getObject() {
304     if (_topState != null) {
305       return _topState.getObject();
306     }
307     return null;
308   }
309 
310   /**
311    * Sets the ClassLoader to use when loading classes
312    *
313    * @param loader the ClassLoader to use
314    **/
315   public void setClassLoader(ClassLoader loader) {
316     _loader = loader;
317   } // -- setClassLoader
318 
319   /**
320    * Sets whether or not to clear collections (including arrays) upon first use to remove default
321    * values. By default, and for backward compatibility with previous versions of Castor this value
322    * is false, indicating that collections are not cleared before initial use by Castor.
323    *
324    * @param clear the boolean value that when true indicates collections should be cleared upon
325    *        first use.
326    */
327   public void setClearCollections(boolean clear) {
328     _clearCollections = clear;
329   } // -- setClearCollections
330 
331   /**
332    * Included for backward compatibility. Debug is replaced with commons-logging.
333    * 
334    * @deprecated
335    **/
336   public void setDebug(boolean debug) {
337     // no-op
338   }
339 
340   /**
341    * Sets the IDResolver to use when resolving IDREFs for which no associated element may exist in
342    * XML document.
343    *
344    * @param idResolver the IDResolver to use when resolving IDREFs for which no associated element
345    *        may exist in the XML document.
346    **/
347   public void setIDResolver(final IDResolver idResolver) {
348     ((IDResolverImpl) _idResolver).setResolver(idResolver);
349   }
350 
351 
352   /**
353    * Sets whether or not attributes that do not match a specific field should simply be ignored or
354    * reported as an error. By default, extra attributes are ignored.
355    *
356    * @param ignoreExtraAtts a boolean that when true will allow non-matched attributes to simply be
357    *        ignored.
358    **/
359   public void setIgnoreExtraAttributes(boolean ignoreExtraAtts) {
360     _strictAttributes = (!ignoreExtraAtts);
361   } // -- setIgnoreExtraAttributes
362 
363   /**
364    * Sets whether or not elements that do not match a specific field should simply be ignored or
365    * reported as an error. By default, extra attributes are ignored.
366    *
367    * @param ignoreExtraElems a boolean that when true will allow non-matched attributes to simply be
368    *        ignored.
369    **/
370   public void setIgnoreExtraElements(boolean ignoreExtraElems) {
371     _strictElementHandler.setIgnoreExtraElements(ignoreExtraElems);
372   } // -- setIgnoreExtraElements
373 
374   /**
375    * Custom logging replaced with commons-logging.
376    * 
377    * @deprecated
378    **/
379   public void setLogWriter(PrintWriter printWriter) {
380     // no-op
381   } // -- setLogWriter
382 
383   /**
384    * Sets a boolean that when true indicates that objects contained within the object model should
385    * be re-used where appropriate. This is only valid when unmarshalling to an existing object.
386    *
387    * @param reuse the boolean indicating whether or not to re-use existing objects in the object
388    *        model.
389    **/
390   public void setReuseObjects(boolean reuse) {
391     _reuseObjects = reuse;
392   } // -- setReuseObjects
393 
394   // /**
395   // * Sets the ClassDescriptorResolver to use for loading and
396   // * resolving ClassDescriptors
397   // *
398   // * @param cdResolver the ClassDescriptorResolver to use
399   // **/
400   // public void setResolver(XMLClassDescriptorResolver cdResolver) {
401   // this._cdResolver = cdResolver;
402   // } //-- setResolver
403 
404   /**
405    * Sets the root (top-level) object to use for unmarshalling into.
406    *
407    * @param root the instance to unmarshal into.
408    **/
409   public void setRootObject(Object root) {
410     _topObject = root;
411   } // -- setRootObject
412 
413   /**
414    * Sets an {@link org.exolab.castor.xml.UnmarshalListener}.
415    *
416    * @param listener the {@link org.exolab.castor.xml.UnmarshalListener} to use with this instance
417    *        of the UnmarshalHandler.
418    * @deprecated please move to the new {@link org.castor.xml.UnmarshalListener} interface
419    */
420   public void setUnmarshalListener(org.exolab.castor.xml.UnmarshalListener listener) {
421     _delegateUnmarshalListener.setUnmarshalListener(listener);
422   }
423 
424   /**
425    * Sets an {@link org.castor.xml.UnmarshalListener}.
426    *
427    * @param listener the {@link org.castor.xml.UnmarshalListener} to use with this instance of the
428    *        UnmarshalHandler.
429    */
430   public void setUnmarshalListener(org.castor.xml.UnmarshalListener listener) {
431     _delegateUnmarshalListener.setUnmarshalListener(listener);
432   }
433 
434   /**
435    * Sets the flag for validation.
436    * 
437    * @param validate A boolean to indicate whether or not validation should be done during
438    *        umarshalling. <br/>
439    *        By default, validation will be performed.
440    */
441   public void setValidation(boolean validate) {
442     this._validate = validate;
443   } // -- setValidation
444 
445   /**
446    * Sets the top-level whitespace (xml:space) to either preserving or non preserving. The XML
447    * document can override this value using xml:space on specific elements. This sets the "default"
448    * behavior when xml:space="default".
449    *
450    * @param preserve a boolean that when true enables whitespace preserving by default.
451    */
452   public void setWhitespacePreserve(boolean preserve) {
453     _wsPreserve = preserve;
454   } // -- setWhitespacePreserve
455 
456   // -----------------------------------/
457   // - SAX Methods for DocumentHandler -/
458   // -----------------------------------/
459 
460   public void characters(char[] ch, int start, int length) throws SAXException {
461     new CharactersProcessor(this).compute(ch, start, length);
462   } // -- characters
463 
464 
465   public void endDocument() throws org.xml.sax.SAXException {
466     // -- I've found many application don't always call
467     // -- #endDocument, so I usually never put any
468     // -- important logic here
469   } // -- endDocument
470 
471 
472   public void endElement(String name) throws org.xml.sax.SAXException {
473     new EndElementProcessor(this).compute(name);
474   } // -- endElement
475 
476   /**
477    * Decode binary data and return decoded value.
478    * 
479    * @param descriptor {@link XMLFieldDescriptor} instance for the field whose value requires
480    *        decoding.
481    * @param binaryData The binary data value to be decoded
482    * @return Decode data.
483    */
484   byte[] decodeBinaryData(final XMLFieldDescriptor descriptor, final String binaryData) {
485     // -- Base64/HexBinary decoding
486     byte[] decodedValue;
487     if ((descriptor.isMultivalued() && HexDecoder.DATA_TYPE.equals(descriptor.getComponentType()))
488         || HexDecoder.DATA_TYPE.equals(descriptor.getSchemaType())) {
489       decodedValue = HexDecoder.decode(binaryData);
490     } else {
491       decodedValue = Base64Decoder.decode(binaryData);
492     }
493     return decodedValue;
494   }
495 
496   /**
497    * <p>
498    * ContentHandler#endElement
499    * </p>
500    *
501    * Signals the end of an element
502    *
503    * @param localName The name of the element.
504    */
505   public void endElement(String namespaceURI, String localName, String qName)
506       throws org.xml.sax.SAXException {
507     if (StringUtils.isEmpty(qName)) {
508       if (StringUtils.isEmpty(localName)) {
509         String error = resourceBundle.getString("unmarshalHandler.error.localName.and.qName.null");
510         throw new SAXException(error);
511       }
512       qName = localName;
513       if (StringUtils.isNotEmpty(namespaceURI)) {
514         // -- rebuild qName, for now
515         String prefix = _namespaceHandling.getNamespacePrefix(namespaceURI);
516         if (StringUtils.isEmpty(prefix))
517           qName = prefix + ":" + localName;
518       }
519     }
520 
521     endElement(qName);
522   } // -- endElement
523 
524 
525   /**
526    * Signals to end the namespace prefix mapping
527    * 
528    * @param prefix the namespace prefix
529    */
530   public void endPrefixMapping(String prefix) throws SAXException {
531     // -- nothing to do , already taken care of in
532     // -- endElement except if we are unmarshalling an
533     // -- AnyNode
534     if (_anyNodeHandler.hasAnyUnmarshaller()) {
535       _anyNodeHandler.endPrefixMapping(prefix);
536     }
537 
538   } // -- endPrefixMapping
539 
540 
541   public void ignorableWhitespace(char[] ch, int start, int length)
542       throws org.xml.sax.SAXException {
543 
544     // -- If we are skipping elements that have appeared in the XML but for
545     // -- which we have no mapping, skip the text and return
546     if (_strictElementHandler.skipElement()) {
547       return;
548     }
549 
550     if (_stateStack.isEmpty()) {
551       return;
552     }
553 
554     if (_anyNodeHandler.hasAnyUnmarshaller()) {
555       _anyNodeHandler.ignorableWhitespace(ch, start, length);
556     } else {
557       UnmarshalState state = _stateStack.getLastState();
558       if (state.isWhitespacePreserving()) {
559         if (state.getBuffer() == null)
560           state.setBuffer(new StringBuffer());
561         state.getBuffer().append(ch, start, length);
562       }
563     }
564   } // -- ignorableWhitespace
565 
566   public void processingInstruction(String target, String data) throws org.xml.sax.SAXException {
567     // -- do nothing for now
568   } // -- processingInstruction
569 
570   public void setDocumentLocator(Locator locator) {
571     this._locator = locator;
572   } // -- setDocumentLocator
573 
574   public Locator getDocumentLocator() {
575     return _locator;
576   } // -- getDocumentLocator
577 
578   /**
579    * Signals that an entity was skipped by the parser
580    *
581    * @param name the skipped entity's name
582    */
583   public void skippedEntity(String name) throws SAXException {
584     // -- do nothing
585 
586   } // -- skippedEntity
587 
588   /**
589    * Signals the start of a new document
590    */
591   public void startDocument() throws org.xml.sax.SAXException {
592 
593     // -- I've found many application don't always call
594     // -- #startDocument, so I usually never put any
595     // -- important logic here
596 
597   } // -- startDocument
598 
599   private void extractNamespaceInformation(Attributes attributes) {
600 
601     if (attributes == null || attributes.getLength() == 0) {
602       return;
603     }
604 
605     // look for any potential namespace declarations in case namespace
606     // processing was disabled on the XML parser
607     for (int i = 0; i < attributes.getLength(); i++) {
608       String attributeName = attributes.getQName(i);
609       if (StringUtils.isNotEmpty(attributeName)) {
610         if (attributeName.equals(XMLNS)) {
611           _namespaceHandling.addDefaultNamespace(attributes.getValue(i));
612         } else if (attributeName.startsWith(XMLNS_PREFIX)) {
613           String prefix = attributeName.substring(XMLNS_PREFIX.length());
614           _namespaceHandling.addNamespace(prefix, attributes.getValue(i));
615         }
616       } else {
617         // -- if qName is null or empty, just process as a normal
618         // -- attribute
619         attributeName = attributes.getLocalName(i);
620         if (XMLNS.equals(attributeName)) {
621           _namespaceHandling.addDefaultNamespace(attributes.getValue(i));
622         }
623       }
624     }
625   }
626 
627   /**
628    * <p>
629    * ContentHandler#startElement
630    * </p>
631    *
632    * Signals the start of element.
633    *
634    * @param localName The name of the element.
635    * @param atts The AttributeList containing the associated attributes for the element.
636    */
637   public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
638       throws org.xml.sax.SAXException {
639     if (LOG.isTraceEnabled()) {
640       String trace;
641       if (StringUtils.isNotEmpty(qName))
642         trace = MessageFormat.format(
643             resourceBundle.getString("unmarshalHandler.log.trace.startElement"),
644             new Object[] {qName});
645       else
646         trace = MessageFormat.format(
647             resourceBundle.getString("unmarshalHandler.log.trace.startElement"),
648             new Object[] {localName});
649       LOG.trace(trace);
650     }
651 
652     // -- If we are skipping elements that have appeared in the XML but for
653     // -- which we have no mapping, increase the ignore depth counter and return
654     if (_strictElementHandler.skipStartElement()) {
655       return;
656     }
657 
658     // -- if we are in an <any> section
659     // -- we delegate the event handling
660     if (_anyNodeHandler.hasAnyUnmarshaller()) {
661       _anyNodeHandler.startElement(namespaceURI, localName, qName, atts);
662       return;
663     }
664 
665     // -- Create a new namespace scope if necessary and
666     // -- make sure the flag is reset to true
667     if (_namespaceHandling.isNewNamespaceScopeNecessary()) {
668       _namespaceHandling.startNamespaceScope();
669     } else {
670       _namespaceHandling.setNewNamespaceScopeNecessary(true);
671     }
672 
673     // extract namespace information
674     extractNamespaceInformation(atts);
675 
676     // preserve parser passed arguments for any potential delegation
677     String tmpQName = null;
678 
679     if (StringUtils.isEmpty(localName)) {
680       if (StringUtils.isEmpty(qName)) {
681         String error = resourceBundle.getString("unmarshalHandler.error.localName.and.qName.null");
682         throw new SAXException(error);
683       }
684       localName = qName;
685       tmpQName = qName;
686     } else {
687       if (StringUtils.isEmpty(qName)) {
688         if (StringUtils.isEmpty(namespaceURI)) {
689           tmpQName = localName;
690         } else {
691           String prefix = _namespaceHandling.getNamespacePrefix(namespaceURI);
692           if (StringUtils.isNotEmpty(prefix)) {
693             tmpQName = prefix + ":" + localName;
694           }
695         }
696 
697       } else {
698         tmpQName = qName;
699       }
700     }
701     _anyNodeHandler.preservePassedArguments(tmpQName, atts);
702 
703     int idx = localName.indexOf(':');
704     if (idx >= 0) {
705       String prefix = localName.substring(0, idx);
706       localName = localName.substring(idx + 1);
707       if (StringUtils.isEmpty(namespaceURI)) {
708         namespaceURI = _namespaceHandling.getNamespaceURI(prefix);
709       }
710     } else {
711       // check for default namespace declaration
712       String defaultNamespace = _namespaceHandling.getDefaultNamespaceURI();
713       // TODO[WG]: remove unnecessary check as it simply is wrong
714       if (defaultNamespace != null && !defaultNamespace.equals("http://castor.exolab.org")) {
715         namespaceURI = defaultNamespace;
716       }
717       // -- adjust empty namespace
718       if (StringUtils.isEmpty(namespaceURI))
719         namespaceURI = null;
720     }
721 
722     // -- call private startElement
723     startElementProcessing(localName, namespaceURI, _attributeSetFactory.getAttributeSet(atts));
724 
725   } // -- startElement
726 
727   /**
728    * <p>
729    * DocumentHandler#startElement
730    * </p>
731    *
732    * Signals the start of element.
733    *
734    * @param name The name of the element.
735    * @param attList The AttributeList containing the associated attributes for the element.
736    * @deprecated
737    */
738   public void startElement(String name, AttributeList attList) throws org.xml.sax.SAXException {
739     if (LOG.isTraceEnabled()) {
740       String trace = MessageFormat.format(
741           resourceBundle.getString("unmarshalHandler.log.trace.startElement"), new Object[] {name});
742       LOG.trace(trace);
743     }
744 
745     // -- If we are skipping elements that have appeared in the XML but for
746     // -- which we have no mapping, increase the ignore depth counter and return
747     if (_strictElementHandler.skipStartElement()) {
748       return;
749     }
750 
751     // -- if we are in an <any> section
752     // -- we delegate the event handling
753     if (_anyNodeHandler.hasAnyUnmarshaller()) {
754       _anyNodeHandler.startElement(name, attList);
755       return;
756     }
757 
758     _anyNodeHandler.preservePassedArguments(name, attList);
759 
760     // -- The namespace of the given element
761     String namespace = null;
762 
763     // -- Begin Namespace Handling :
764     // -- XXX Note: This code will change when we update the XML event API
765 
766     _namespaceHandling.createNamespace();
767 
768     String prefix = "";
769     // String qName = name;
770     int idx = name.indexOf(':');
771     if (idx >= 0) {
772       prefix = name.substring(0, idx);
773       name = name.substring(idx + 1);
774     }
775 
776     namespace = _namespaceHandling.getNamespaceURI(prefix);
777 
778     // -- End Namespace Handling
779 
780     // -- call private startElement method
781     startElementProcessing(name, namespace, _attributeSetFactory.getAttributeSet(attList));
782 
783   } // -- startElement
784 
785   /**
786    * Signals the start of an element with the given name.
787    *
788    * @param name the NCName of the element. It is an error if the name is a QName (ie. contains a
789    *        prefix).
790    * @param namespace the namespace of the element. This may be null. Note: A null namespace is not
791    *        the same as the default namespace unless the default namespace is also null.
792    * @param atts the AttributeSet containing the attributes associated with the element.
793    */
794   void startElementProcessing(String name, String namespace, AttributeSet atts)
795       throws SAXException {
796     new StartElementProcessor(this).compute(name, namespace, atts);
797   }
798 
799   void processFirstElement(String name, String namespace, AttributeSet atts, String xmlSpace)
800       throws SAXException {
801     if (_topClass == null) {
802       if (_topObject != null) {
803         _topClass = _topObject.getClass();
804       }
805     }
806     // if (_cdResolver == null) {
807     // if (_topClass == null) {
808     // String err = "The class for the root element '" +
809     // name + "' could not be found.";
810     // throw new SAXException(err);
811     // }
812     // _cdResolver = (XMLClassDescriptorResolver)
813     // ClassDescriptorResolverFactory.createClassDescriptorResolver(BindingType.XML);
814     // _cdResolver.setClassLoader(_loader);
815     // }
816     if (getInternalContext().getXMLClassDescriptorResolver() == null) {
817       // Joachim 2007-09-04 check is new
818       String message =
819           resourceBundle.getString("unmarshalHandler.log.warn.class.descriptor.not.set");
820       LOG.warn(message);
821       throw new IllegalStateException(message);
822     }
823 
824     _topState = new UnmarshalState();
825     _topState.setElementName(name);
826     _topState.setWhitespacePreserving((xmlSpace != null) ? PRESERVE.equals(xmlSpace) : _wsPreserve);
827 
828     XMLClassDescriptor classDesc = null;
829     // -- If _topClass is null, then we need to search
830     // -- the resolver for one
831     String instanceClassname = null;
832     if (_topClass == null) {
833 
834       // -- check for xsi:type
835       instanceClassname = getInstanceType(atts, null);
836       if (instanceClassname != null) {
837         // -- first try loading class directly
838         try {
839           _topClass = loadClass(instanceClassname, null);
840         } catch (ClassNotFoundException cnfe) {
841         }
842 
843         if (_topClass == null) {
844           classDesc = getClassDescriptor(instanceClassname);
845           if (classDesc != null) {
846             _topClass = classDesc.getJavaClass();
847           }
848           if (_topClass == null) {
849             String error = MessageFormat.format(
850                 resourceBundle.getString("unmarshalHandler.error.class.not.found"),
851                 new Object[] {instanceClassname});
852             throw new SAXException(error);
853           }
854         }
855       } else {
856         classDesc = resolveByXMLName(name, namespace, null);
857         if (classDesc == null) {
858           classDesc = getClassDescriptor(name, _loader);
859           if (classDesc == null) {
860             classDesc = getClassDescriptor(getJavaNaming().toJavaClassName(name));
861           }
862         }
863         if (classDesc != null) {
864           _topClass = classDesc.getJavaClass();
865         }
866       }
867 
868       if (_topClass == null) {
869         String err = MessageFormat.format(
870             resourceBundle.getString("unmarshalHandler.error.class.root.not.found"),
871             new Object[] {name});
872         throw new SAXException(err);
873       }
874     }
875 
876     // -- create a "fake" FieldDescriptor for the root element
877     XMLFieldDescriptorImpl fieldDesc =
878         new XMLFieldDescriptorImpl(_topClass, name, name, NodeType.Element);
879 
880     _topState.setFieldDescriptor(fieldDesc);
881     // -- look for XMLClassDescriptor if null
882     // -- always check resolver first
883     if (classDesc == null) {
884       classDesc = getClassDescriptor(_topClass);
885     }
886 
887     // -- check for top-level primitives
888     if (classDesc == null) {
889       if (isPrimitive(_topClass)) {
890         classDesc = new PrimitivesClassDescriptor(_topClass);
891         fieldDesc.setIncremental(false);
892         _topState.setPrimitiveOrImmutable(true);
893       }
894     }
895 
896     fieldDesc.setClassDescriptor(classDesc);
897     if (classDesc == null) {
898       // -- report error
899       if ((!isPrimitive(_topClass)) && (!Serializable.class.isAssignableFrom(_topClass))) {
900         throw new SAXException(MarshalException.NON_SERIALIZABLE_ERR);
901       }
902 
903       String err = MessageFormat.format(
904           resourceBundle.getString("unmarshalHandler.error.create.class.descriptor"),
905           new Object[] {_topClass.getName()});
906 
907       throw new SAXException(err);
908     }
909     _topState.setClassDescriptor(classDesc);
910     _topState.setType(_topClass);
911 
912     if ((_topObject == null) && (!_topState.isPrimitiveOrImmutable())) {
913       // Retrieving the xsi:type attribute, if present
914       String topPackage = getJavaPackage(_topClass);
915 
916       if (instanceClassname == null) {
917         instanceClassname = getInstanceType(atts, topPackage);
918       } else {
919         // -- instance type already processed above, reset
920         // -- to null to prevent entering next block
921         instanceClassname = null;
922       }
923 
924       if (instanceClassname != null) {
925         Class<?> instanceClass = null;
926         try {
927 
928           XMLClassDescriptor xcd = getClassDescriptor(instanceClassname);
929 
930           boolean loadClass = true;
931           if (xcd != null) {
932             instanceClass = xcd.getJavaClass();
933             if (instanceClass != null) {
934               loadClass = (!instanceClassname.equals(instanceClass.getName()));
935             }
936           }
937 
938           if (loadClass) {
939             try {
940               instanceClass = loadClass(instanceClassname, null);
941             } catch (ClassNotFoundException cnfe) {
942               // -- revert back to ClassDescriptor's associated
943               // -- class
944               if (xcd != null) {
945                 instanceClass = xcd.getJavaClass();
946               }
947             }
948           }
949 
950           if (instanceClass == null) {
951             String error = MessageFormat.format(
952                 resourceBundle.getString("unmarshalHandler.error.class.not.found"),
953                 new Object[] {instanceClassname});
954             throw new SAXException(error);
955           }
956 
957           if (!_topClass.isAssignableFrom(instanceClass)) {
958             String err = MessageFormat.format(
959                 resourceBundle.getString("unmarshalHandler.error.not.subclass"),
960                 new Object[] {instanceClass, _topClass});
961             throw new SAXException(err);
962           }
963 
964         } catch (Exception ex) {
965           String err = MessageFormat.format(
966               resourceBundle.getString("unmarshalHandler.error.unable.instantiate"),
967               new Object[] {instanceClassname});
968           throw new SAXException(err, ex);
969         }
970 
971         // -- try to create instance of the given Class
972         Arguments args = processConstructorArgs(atts, classDesc);
973         _topState.setObject(createInstance(instanceClass, args));
974       } else {
975         // -- no xsi type information present
976         // -- try to create instance of the given Class
977         Arguments args = processConstructorArgs(atts, classDesc);
978         _topState.setObject(createInstance(_topClass, args));
979       }
980     } else {
981       // -- otherwise use _topObject
982       _topState.setObject(_topObject);
983     }
984 
985     _stateStack.pushState(_topState);
986 
987     if (!_topState.isPrimitiveOrImmutable()) {
988       // --The top object has just been initialized
989       // --notify the listener
990       Object stateObject = _topState.getObject();
991       Object parentObject =
992           (_topState.getParent() == null) ? null : _topState.getParent().getObject();
993 
994       _delegateUnmarshalListener.initialized(stateObject, parentObject);
995       processAttributes(atts, classDesc);
996       _delegateUnmarshalListener.attributesProcessed(stateObject, parentObject);
997       _namespaceHandling.processNamespaces(classDesc, _stateStack.getLastState().getObject());
998     }
999 
1000     String pkg = getJavaPackage(_topClass);
1001     if (getMappedPackage(namespace) == null) {
1002       addNamespaceToPackageMapping(namespace, pkg);
1003     }
1004   }
1005 
1006 
1007   /**
1008    * Indicates whether validation is enabled or not.
1009    * 
1010    * @return True if validation is enabled.
1011    */
1012   boolean isValidating() {
1013     return _validate;
1014   }
1015 
1016   /**
1017    * Signals to start the namespace - prefix mapping
1018    * 
1019    * @param prefix the namespace prefix to map
1020    * @param uri the namespace URI
1021    */
1022   public void startPrefixMapping(String prefix, String uri) throws SAXException {
1023 
1024     // -- Patch for Xerces 2.x bug
1025     // -- prevent attempting to declare "XML" namespace
1026     if (Namespaces.XML_NAMESPACE_PREFIX.equals(prefix) && Namespaces.XML_NAMESPACE.equals(uri)) {
1027       return;
1028     } else if (XMLNS.equals(prefix)) {
1029       return;
1030     }
1031     // -- end Xerces 2.x bug
1032 
1033     // -- Forward the call to SAX2ANY
1034     // -- or create a namespace node
1035     if (_anyNodeHandler.hasAnyUnmarshaller()) {
1036       _anyNodeHandler.startPrefixMapping(prefix, uri);
1037     } else if (_namespaceHandling.isNewNamespaceScopeNecessary()) {
1038       _namespaceHandling.stopNamespaceScope();
1039     }
1040     _namespaceHandling.addNamespace(prefix, uri);
1041 
1042     /*
1043      * //-- add namespace declarations to set of current attributes String attName = null; if
1044      * ((prefix == null) || (prefix.length() == 0)) attName = XMLNS_DECL; else attName =
1045      * XMLNS_PREFIX + prefix;
1046      * 
1047      * _currentAtts.addAttribute(attName, uri);
1048      */
1049 
1050   } // -- startPrefixMapping
1051 
1052 
1053   // ------------------------------------/
1054   // - org.xml.sax.ErrorHandler methods -/
1055   // ------------------------------------/
1056 
1057   public void error(SAXParseException exception) throws org.xml.sax.SAXException {
1058     String error = MessageFormat
1059         .format(resourceBundle.getString("unmarshalHandler.error.sax.exception"), new Object[] {
1060             exception.getMessage(), exception.getLineNumber(), exception.getColumnNumber()});
1061     throw new SAXException(error, exception);
1062   } // -- error
1063 
1064   public void fatalError(SAXParseException exception) throws org.xml.sax.SAXException {
1065     this.error(exception);
1066 
1067   } // -- fatalError
1068 
1069 
1070   public void warning(SAXParseException exception) throws org.xml.sax.SAXException {
1071     this.error(exception);
1072 
1073   } // -- warning
1074 
1075   // ---------------------/
1076   // - Protected Methods -/
1077   // ---------------------/
1078 
1079   // TODO: Joachim 2007-09-04 remove me
1080   // /**
1081   // * Sets the current Castor configuration. Currently this
1082   // * Configuration is only used during Validation (which is
1083   // * why this method is currently protected, since it has
1084   // * no effect at this point on the actual configuration of
1085   // * the unmarshaller)
1086   // *
1087   // * Currently, this method should only be called by the
1088   // * Unmarshaller.
1089   // */
1090   // protected void setConfiguration(Configuration config) {
1091   // _config = config;
1092   // } //-- setConfiguration
1093 
1094   // -------------------/
1095   // - Private Methods -/
1096   // -------------------/
1097 
1098   /**
1099    * Adds the given reference to the "queue" until the referenced object has been unmarshalled.
1100    *
1101    * @param idRef the ID being referenced
1102    * @param parent the target/parent object for the field
1103    * @param descriptor the XMLFieldDescriptor for the field
1104    */
1105   void addReference(final String idRef, final Object parent, final XMLFieldDescriptor descriptor) {
1106 
1107     ReferenceInfo refInfo = new ReferenceInfo(idRef, parent, descriptor);
1108     refInfo.setNext(_resolveTable.get(idRef));
1109     _resolveTable.put(idRef, refInfo);
1110   }
1111 
1112   /**
1113    * Creates an instance of the given class /type, using the arguments provided (if there are any).
1114    * 
1115    * @param type The class type to be used during instantiation
1116    * @param args (Optional) arguments to be used during instantiation
1117    */
1118   Object createInstance(final Class<?> type, final Arguments args) throws SAXException {
1119     Object instance = null;
1120     try {
1121       if (args == null) {
1122         instance = _objectFactory.createInstance(type);
1123       } else {
1124         instance = _objectFactory.createInstance(type, args.getTypes(), args.getValues());
1125       }
1126     } catch (Exception ex) {
1127       String error = MessageFormat.format(
1128           resourceBundle.getString("unmarshalHandler.error.unable.instantiate"),
1129           new Object[] {type.getName()});
1130       throw new SAXException(error, ex);
1131     }
1132     return instance;
1133   } // -- createInstance
1134 
1135   /**
1136    * Returns the resolved instance type attribute (xsi:type). If present the instance type attribute
1137    * is resolved into a java class name and then returned.
1138    *
1139    * @param atts the AttributeList to search for the instance type attribute.
1140    * @return the java class name corresponding to the value of the instance type attribute, or null
1141    *         if no instance type attribute exists in the given AttributeList.
1142    */
1143   String getInstanceType(AttributeSet atts, String currentPackage) throws SAXException {
1144 
1145     if (atts == null)
1146       return null;
1147 
1148     // -- find xsi:type attribute
1149     String type = atts.getValue(XSI_TYPE, XSI_NAMESPACE);
1150 
1151     if (type != null) {
1152 
1153       if (type.startsWith(JAVA_PREFIX)) {
1154         return type.substring(JAVA_PREFIX.length());
1155       }
1156 
1157       // check for namespace prefix in type
1158       int idx = type.indexOf(':');
1159       String typeNamespaceURI = null;
1160       if (idx >= 0) {
1161         // there is a namespace prefix
1162         String prefix = type.substring(0, idx);
1163         type = type.substring(idx + 1);
1164         typeNamespaceURI = _namespaceHandling.getNamespaceURI(prefix);
1165       }
1166 
1167       // -- Retrieve the type corresponding to the schema name and
1168       // -- return it.
1169       XMLClassDescriptor classDesc = null;
1170 
1171       try {
1172         classDesc = getInternalContext().getXMLClassDescriptorResolver().resolveByXMLName(type,
1173             typeNamespaceURI, _loader);
1174 
1175         if (classDesc != null)
1176           return classDesc.getJavaClass().getName();
1177 
1178 
1179         // -- if class descriptor is not found here, then no descriptors
1180         // -- existed in memory...try to load one based on name of
1181         // -- Schema type
1182         final String className = getJavaNaming().toJavaClassName(type);
1183 
1184         String adjClassName = className;
1185         String mappedPackage = getMappedPackage(typeNamespaceURI);
1186         if ((mappedPackage != null) && (mappedPackage.length() > 0)) {
1187           adjClassName = mappedPackage + "." + className;
1188         }
1189         classDesc =
1190             getInternalContext().getXMLClassDescriptorResolver().resolve(adjClassName, _loader);
1191         if (classDesc != null)
1192           return classDesc.getJavaClass().getName();
1193 
1194         // -- try to use "current Package"
1195         if (StringUtils.isNotEmpty(currentPackage)) {
1196           adjClassName = currentPackage + '.' + className;
1197         }
1198 
1199         classDesc =
1200             getInternalContext().getXMLClassDescriptorResolver().resolve(adjClassName, _loader);
1201         if (classDesc != null)
1202           return classDesc.getJavaClass().getName();
1203 
1204         // -- Still can't find type, this may be due to an
1205         // -- attempt to unmarshal an older XML instance
1206         // -- that was marshalled with a previous Castor. A
1207         // -- bug fix in the XMLMappingLoader prevents old
1208         // -- xsi:type that are missing the "java:"
1209         classDesc = getInternalContext().getXMLClassDescriptorResolver().resolve(type, _loader);
1210         if (classDesc != null)
1211           return classDesc.getJavaClass().getName();
1212       } catch (ResolverException rx) {
1213         throw new SAXException(rx);
1214       }
1215     }
1216     return null;
1217   } // -- getInstanceType
1218 
1219   /**
1220    * Looks up the package name from the given namespace URI.
1221    * 
1222    * @param namespace the namespace URI to lookup
1223    * @return the package name or null.
1224    */
1225   private String getMappedPackage(final String namespace) {
1226     return _namespaceHandling.getMappedPackage(namespace);
1227   }
1228 
1229   /**
1230    * Processes the given attribute list, and attempts to add each {@link Attributes} to the current
1231    * {@link Object} on the stack.
1232    *
1233    * @param atts the AttributeSet to process
1234    * @param classDesc the classDesc to use during processing
1235    **/
1236   void processAttributes(final AttributeSet atts, XMLClassDescriptor classDesc)
1237       throws SAXException {
1238 
1239     // -- handle empty attributes
1240     if ((atts == null) || (atts.getSize() == 0)) {
1241       if (classDesc != null) {
1242         XMLFieldDescriptor[] descriptors = classDesc.getAttributeDescriptors();
1243         for (int i = 0; i < descriptors.length; i++) {
1244           XMLFieldDescriptor descriptor = descriptors[i];
1245           if (descriptor == null) {
1246             continue;
1247           }
1248           // -- Since many attributes represent primitive
1249           // -- fields, we add an extra validation check here
1250           // -- in case the class doesn't have a "has-method".
1251           if (descriptor.isRequired() && (isValidating() || LOG.isDebugEnabled())) {
1252             String errorMsg;
1253             if (_locator != null) {
1254               errorMsg = MessageFormat.format(
1255                   resourceBundle.getString("unmarshalHandler.error.attribute.missing.location"),
1256                   classDesc.getXMLName(), descriptor.getXMLName(), _locator.getLineNumber(),
1257                   _locator.getColumnNumber());
1258             } else {
1259               errorMsg = MessageFormat.format(
1260                   resourceBundle.getString("unmarshalHandler.error.attribute.missing"),
1261                   classDesc.getXMLName(), descriptor.getXMLName());
1262             }
1263             if (isValidating()) {
1264               throw new SAXException(errorMsg);
1265             }
1266             LOG.debug(errorMsg);
1267           }
1268         }
1269       }
1270       return;
1271     }
1272 
1273 
1274     UnmarshalState state = _stateStack.getLastState();
1275     Object object = state.getObject();
1276 
1277     if (classDesc == null) {
1278       classDesc = state.getClassDescriptor();
1279       if (classDesc == null) {
1280         // -- no class desc, cannot process atts
1281         // -- except for wrapper/location atts
1282         processWrapperAttributes(atts);
1283         return;
1284       }
1285     }
1286 
1287     // -- First loop through Attribute Descriptors.
1288     // -- Then, if we have any attributes which
1289     // -- haven't been processed we can ask
1290     // -- the XMLClassDescriptor for the FieldDescriptor.
1291 
1292     boolean[] processedAtts = new boolean[atts.getSize()];
1293     XMLFieldDescriptor[] descriptors = classDesc.getAttributeDescriptors();
1294     for (XMLFieldDescriptor descriptor : descriptors) {
1295 
1296       String name = descriptor.getXMLName();
1297       String namespace = descriptor.getNameSpaceURI();
1298       String path = descriptor.getLocationPath();
1299       StringBuilder fullAttributePath = new StringBuilder();
1300 
1301       if (StringUtils.isNotEmpty(path)) {
1302         fullAttributePath.append(path).append('/');
1303       }
1304 
1305       fullAttributePath.append(name);
1306 
1307       if (!name.equals(fullAttributePath.toString())) {
1308         int index = atts.getIndex(name, namespace);
1309         if (index >= 0) {
1310           processedAtts[index] = true;
1311         }
1312         continue;
1313       }
1314 
1315       int index = atts.getIndex(name, namespace);
1316 
1317       String attValue = null;
1318       if (index >= 0) {
1319         attValue = atts.getValue(index);
1320         processedAtts[index] = true;
1321       }
1322 
1323       try {
1324         processAttribute(name, namespace, attValue, descriptor, classDesc, object);
1325       } catch (IllegalStateException ise) {
1326         String error = MessageFormat.format(
1327             resourceBundle.getString("unmarshalHandler.error.unable.add.attribute"),
1328             new Object[] {name, state.getClassDescriptor().getJavaClass().getName(), ise});
1329         throw new SAXException(error, ise);
1330       }
1331     }
1332 
1333     // -- Handle any non processed attributes...
1334     // -- This is useful for descriptors that might use
1335     // -- wild-cards or other types of matching...as well
1336     // -- as backward compatibility...attribute descriptors
1337     // -- were erronously getting set with the default
1338     // -- namespace by the source generator...this is
1339     // -- also true of the generated classes for the
1340     // -- Mapping Framework...we need to clean this up
1341     // -- at some point in the future.
1342     for (int i = 0; i < processedAtts.length; i++) {
1343       if (processedAtts[i]) {
1344         continue;
1345       }
1346 
1347       String namespace = atts.getNamespace(i);
1348       String name = atts.getName(i);
1349 
1350       // -- skip XSI attributes
1351       if (XSI_NAMESPACE.equals(namespace)) {
1352         if (NIL_ATTR.equals(name)) {
1353           String value = atts.getValue(i);
1354           state.setNil(("true".equals(value)));
1355         }
1356         continue;
1357       }
1358 
1359 
1360       if (name.startsWith(XML_PREFIX + ':')) {
1361 
1362         // -- XML specification specific attribute
1363         // -- It should be safe to ignore these...but
1364         // -- if you think otherwise...let use know!
1365         if (LOG.isDebugEnabled()) {
1366           String debugMsg = MessageFormat.format(
1367               resourceBundle.getString("unmarshalHandler.log.debug.ignore.extra.attribute"),
1368               new Object[] {name, state.getClassDescriptor().getJavaClass().getName()});
1369           LOG.debug(debugMsg);
1370         }
1371         continue;
1372       }
1373 
1374       // -- This really should handle namespace...but it currently
1375       // -- doesn't. Ignoring namespaces also helps with the
1376       // -- backward compatibility issue mentioned above.
1377       XMLFieldDescriptor descriptor =
1378           classDesc.getFieldDescriptor(name, namespace, NodeType.Attribute);
1379 
1380       if (descriptor == null) {
1381         // -- check for nested attribute...loop through
1382         // -- stack and find correct descriptor
1383         String path = state.getElementName();
1384         Integer parentStateIndex = _stateStack.getFirstParentStateIndex();
1385         while (parentStateIndex >= 0) {
1386           UnmarshalState targetState = _stateStack.peekAtState(parentStateIndex--);
1387           if (targetState.isWrapper()) {
1388             path = targetState.getElementName() + '/' + path;
1389             continue;
1390           }
1391           classDesc = targetState.getClassDescriptor();
1392           descriptor = classDesc.getFieldDescriptor(name, namespace, NodeType.Attribute);
1393 
1394           if (descriptor != null) {
1395             String tmpPath = descriptor.getLocationPath();
1396             if (path.equals(StringUtils.defaultString(tmpPath))) {
1397               _stateStack.resetParentState();
1398               break; // -- found
1399             }
1400           }
1401 
1402           path = targetState.getElementName() + '/' + path;
1403 
1404           // -- reset descriptor to make sure we don't
1405           // -- exit the loop with a reference to a
1406           // -- potentially incorrect one.
1407           descriptor = null;
1408         }
1409       }
1410       if (descriptor == null) {
1411         if (_strictAttributes) {
1412           // -- handle error
1413           String errorMsg = MessageFormat.format(
1414               resourceBundle.getString("unmarshalHandler.error.strict.attribute.error"),
1415               new Object[] {name, state.getElementName()});
1416           throw new SAXException(errorMsg);
1417         }
1418         continue;
1419       }
1420 
1421       try {
1422         processAttribute(name, namespace, atts.getValue(i), descriptor, classDesc, object);
1423       } catch (IllegalStateException ise) {
1424         String errorMsg = MessageFormat.format(
1425             resourceBundle.getString("unmarshalHandler.error.unable.add.attribute"),
1426             new Object[] {name, state.getClassDescriptor().getJavaClass().getName(), ise});
1427         throw new SAXException(errorMsg, ise);
1428       }
1429     }
1430 
1431   }
1432 
1433   /**
1434    * Processes the given AttributeSet for wrapper elements.
1435    * 
1436    * @param atts the AttributeSet to process
1437    * @throws SAXException If the AttributeSet cannot be processed
1438    */
1439   void processWrapperAttributes(final AttributeSet atts) throws SAXException {
1440 
1441     UnmarshalState state = _stateStack.getLastState();
1442 
1443     // -- loop through attributes and look for the
1444     // -- ancestor objects that they may belong to
1445     for (int i = 0; i < atts.getSize(); i++) {
1446       String name = atts.getName(i);
1447       String namespace = atts.getNamespace(i);
1448 
1449       // -- skip XSI attributes
1450       if (XSI_NAMESPACE.equals(namespace)) {
1451         continue;
1452       }
1453 
1454       XMLFieldDescriptor descriptor = null;
1455       XMLClassDescriptor classDesc = null;
1456       // -- check for nested attribute...loop through
1457       // -- stack and find correct descriptor
1458       String path = state.getElementName();
1459       UnmarshalState targetState = null;
1460       while (_stateStack.hasAnotherParentState()) {
1461         targetState = _stateStack.removeParentState();
1462         if (targetState.isWrapper()) {
1463           path = targetState.getElementName() + '/' + path;
1464           continue;
1465         }
1466         classDesc = targetState.getClassDescriptor();
1467 
1468         XMLFieldDescriptor[] descriptors = classDesc.getAttributeDescriptors();
1469         boolean found = false;
1470         for (int a = 0; a < descriptors.length; a++) {
1471           descriptor = descriptors[a];
1472           if (descriptor == null) {
1473             continue;
1474           }
1475           if (descriptor.matches(name)) {
1476             String tmpPath = descriptor.getLocationPath();
1477             if (path.equals(StringUtils.defaultString(tmpPath))) {
1478               found = true;
1479               break;
1480             }
1481           }
1482         }
1483         if (found) {
1484           _stateStack.resetParentState();
1485           break;
1486         }
1487 
1488         path = targetState.getElementName() + '/' + path;
1489 
1490         // -- reset descriptor to make sure we don't
1491         // -- exit the loop with a reference to a
1492         // -- potentially incorrect one.
1493         descriptor = null;
1494       }
1495       if (descriptor != null) {
1496         try {
1497           processAttribute(name, namespace, atts.getValue(i), descriptor, classDesc,
1498               targetState.getObject());
1499         } catch (IllegalStateException ise) {
1500           String errorMsg = MessageFormat.format(
1501               resourceBundle.getString("unmarshalHandler.error.unable.add.attribute"),
1502               new Object[] {name, state.getClassDescriptor().getJavaClass().getName(), ise});
1503           throw new SAXException(errorMsg, ise);
1504         }
1505       }
1506     }
1507 
1508   }
1509 
1510   /**
1511    * Processes the given Attribute.
1512    **/
1513   private void processAttribute(final String attName, final String attNamespace, String attValue,
1514       XMLFieldDescriptor descriptor, final XMLClassDescriptor classDesc, Object parent)
1515       throws SAXException {
1516 
1517     // Object value = attValue;
1518     while (descriptor.isContainer()) {
1519       FieldHandler handler = descriptor.getHandler();
1520       Object containerObject = handler.getValue(parent);
1521 
1522       if (containerObject == null) {
1523         containerObject = handler.newInstance(parent);
1524         handler.setValue(parent, containerObject);
1525       }
1526 
1527       ClassDescriptor containerClassDesc =
1528           ((XMLFieldDescriptorImpl) descriptor).getClassDescriptor();
1529       descriptor = ((XMLClassDescriptor) containerClassDesc).getFieldDescriptor(attName,
1530           attNamespace, NodeType.Attribute);
1531       parent = containerObject;
1532     }
1533 
1534     if (attValue == null) {
1535       // -- Since many attributes represent primitive
1536       // -- fields, we add an extra validation check here
1537       // -- in case the class doesn't have a "has-method".
1538       if (descriptor.isRequired() && isValidating()) {
1539         String errorMsg;
1540         if (_locator != null) {
1541           errorMsg = MessageFormat.format(
1542               resourceBundle.getString("unmarshalHandler.error.attribute.missing.location"),
1543               new Object[] {classDesc.getXMLName(), attName, _locator.getLineNumber(),
1544                   _locator.getColumnNumber()});
1545         } else {
1546           errorMsg = MessageFormat.format(
1547               resourceBundle.getString("unmarshalHandler.error.attribute.missing"),
1548               new Object[] {classDesc.getXMLName(), attName});
1549         }
1550         throw new SAXException(errorMsg);
1551       }
1552       return;
1553     }
1554 
1555     // -- if this is the identity then save id
1556     if (classDesc.getIdentity() == descriptor) {
1557 
1558       try {
1559         ((IDResolverImpl) _idResolver).bind(attValue, parent,
1560             isValidating() && !getInternalContext().getLenientIdValidation());
1561       } catch (ValidationException e) {
1562         String errorMsg =
1563             MessageFormat.format(resourceBundle.getString("unmarshalHandler.error.duplicated.id"),
1564                 new Object[] {attValue});
1565         throw new SAXException(errorMsg, e);
1566       }
1567 
1568       // -- save key in current state
1569       UnmarshalState state = _stateStack.getLastState();
1570       state.setKey(attValue);
1571 
1572       // -- resolve waiting references
1573       resolveReferences(attValue, parent);
1574     } else if (descriptor.isReference()) {
1575       // -- if this is an IDREF(S) then resolve reference(s)
1576       if (descriptor.isMultivalued()) {
1577         StringTokenizer st = new StringTokenizer(attValue);
1578         while (st.hasMoreTokens()) {
1579           processIDREF(st.nextToken(), descriptor, parent);
1580         }
1581       } else {
1582         processIDREF(attValue, descriptor, parent);
1583       }
1584       // -- object values have been set by processIDREF
1585       // -- simply return
1586       return;
1587     }
1588 
1589     // -- if it's a constructor argument, we can exit at this point
1590     // -- since constructor arguments have already been set
1591     if (descriptor.isConstructorArgument()) {
1592       return;
1593     }
1594 
1595     // -- attribute handler
1596     FieldHandler handler = descriptor.getHandler();
1597     if (handler == null) {
1598       return;
1599     }
1600 
1601     // -- attribute field type
1602     Class<?> type = descriptor.getFieldType();
1603     String valueType = descriptor.getSchemaType();
1604     boolean isPrimative = isPrimitive(type);
1605     boolean isQName = StringUtils.equals(valueType, QNAME_NAME);
1606 
1607     boolean isByteArray = false;
1608     if (type.isArray()) {
1609       isByteArray = (type.getComponentType() == Byte.TYPE);
1610     }
1611 
1612     // -- if this is an multi-value attribute
1613     if (descriptor.isMultivalued()) {
1614       StringTokenizer attrValueTokenizer = new StringTokenizer(attValue);
1615       while (attrValueTokenizer.hasMoreTokens()) {
1616         attValue = attrValueTokenizer.nextToken();
1617         setAttributeValueOnObject(attValue, descriptor, parent, handler, type, isPrimative, isQName,
1618             isByteArray);
1619       }
1620     } else {
1621       setAttributeValueOnObject(attValue, descriptor, parent, handler, type, isPrimative, isQName,
1622           isByteArray);
1623     }
1624 
1625   }
1626 
1627   /**
1628    * Sets the value of an attribute on the target object, using the {@link FieldHandler} provided.
1629    * 
1630    * @param attValue The attribute value.
1631    * @param descriptor Corresponding {@link XMLFieldDescriptor} instance for the attribute
1632    *        processed.
1633    * @param parent Parent object into which attribute value needs to be 'injected'.
1634    * @param handler {@link FieldHandler} used for 'value injection'.
1635    * @param type {@link Class} type.
1636    * @param isPrimitive Indicates whether the attribute value represents a primitive value.
1637    * @param isQName Indicates whether the attribute value represents a QName value.
1638    * @param isByteArray Indicates whether the attribute value represents a byte array.
1639    * @throws SAXException If there's a problem 'injecting' the attribute value into the target
1640    *         field.
1641    */
1642   private void setAttributeValueOnObject(final String attValue, final XMLFieldDescriptor descriptor,
1643       final Object parent, final FieldHandler handler, final Class<?> type,
1644       final boolean isPrimitive, final boolean isQName, final boolean isByteArray)
1645       throws SAXException {
1646     // -- value to set
1647     Object value = attValue;
1648     // -- special type conversion for primitives
1649     if (isPrimitive) {
1650       value = toPrimitiveObject(type, attValue, descriptor);
1651     }
1652 
1653     // -- special byte[]s provessing, if required
1654     if (isByteArray) {
1655       if (attValue == null) {
1656         value = new byte[0];
1657       } else {
1658         // -- Base64/hexbinary decoding
1659         if (HexDecoder.DATA_TYPE.equals(descriptor.getComponentType())) {
1660           value = HexDecoder.decode(attValue);
1661         } else {
1662           value = Base64Decoder.decode(attValue);
1663         }
1664       }
1665     }
1666 
1667     // -- QName resolution (ns:value -> {URI}value), if required
1668     if (isQName) {
1669       value = _namespaceHandling.resolveNamespace(value);
1670     }
1671     // -- set value
1672     handler.setValue(parent, value);
1673   }
1674 
1675   /**
1676    * Processes the given attribute set, and creates the constructor arguments.
1677    *
1678    * @param atts the AttributeSet to process
1679    * @param classDesc the XMLClassDescriptor of the objec
1680    * @return the array of constructor argument values.
1681    * @throws SAXException If there's a problem creating the constructor argument set.
1682    */
1683   Arguments processConstructorArgs(final AttributeSet atts, final XMLClassDescriptor classDesc)
1684       throws SAXException {
1685 
1686     if (classDesc == null) {
1687       return new Arguments();
1688     }
1689 
1690     // -- Loop through Attribute Descriptors and build
1691     // -- the argument array
1692 
1693     // -- NOTE: Due to IDREF being able to reference an
1694     // -- un-yet unmarshalled object, we cannot handle
1695     // -- references as constructor arguments.
1696     // -- kvisco - 20030421
1697     int count = 0;
1698     XMLFieldDescriptor[] descriptors = classDesc.getAttributeDescriptors();
1699     for (XMLFieldDescriptor fieldDescriptor : descriptors) {
1700       if (fieldDescriptor == null) {
1701         continue;
1702       }
1703       if (fieldDescriptor.isConstructorArgument()) {
1704         ++count;
1705       }
1706     }
1707 
1708     Arguments args = new Arguments();
1709 
1710     if (count == 0) {
1711       return args;
1712     }
1713 
1714     args.setValues(new Object[count]);
1715     args.setTypes(new Class[count]);
1716 
1717     for (XMLFieldDescriptor descriptor : descriptors) {
1718 
1719       if (descriptor == null) {
1720         continue;
1721       }
1722       if (!descriptor.isConstructorArgument()) {
1723         continue;
1724       }
1725 
1726       int argIndex = descriptor.getConstructorArgumentIndex();
1727       if (argIndex >= count) {
1728         String errorMsg = MessageFormat.format(
1729             resourceBundle.getString("unmarshalHandler.error.index.out.of.bound"),
1730             new Object[] {argIndex});
1731         throw new SAXException(errorMsg);
1732       }
1733 
1734       args.setType(argIndex, descriptor.getFieldType());
1735       String name = descriptor.getXMLName();
1736       String namespace = descriptor.getNameSpaceURI();
1737 
1738       int index = atts.getIndex(name, namespace);
1739 
1740       if (index >= 0) {
1741         Object value = atts.getValue(index);
1742         // -- check for proper type and do type
1743         // -- conversion
1744         if (isPrimitive(args.getType(argIndex))) {
1745           value = toPrimitiveObject(args.getType(argIndex), (String) value, descriptor);
1746         } else {
1747           // check whether we are looking at an enum-style object, and if so,
1748           // convert the (string) value
1749           value = convertToEnumObject(descriptor, value);
1750         }
1751 
1752         // check if the value is a QName that needs to
1753         // be resolved (ns:value -> {URI}value)
1754         String valueType = descriptor.getSchemaType();
1755         if (StringUtils.equals(valueType, QNAME_NAME)) {
1756           value = _namespaceHandling.resolveNamespace(value);
1757         }
1758         args.setValue(argIndex, value);
1759       } else {
1760         if (isPrimitive(args.getType(argIndex))) {
1761           args.setValue(argIndex, toPrimitiveObject(args.getType(argIndex), null, descriptor));
1762         } else {
1763           args.setValue(argIndex, null);
1764         }
1765       }
1766     }
1767     return args;
1768   }
1769 
1770   /**
1771    * Checks whether the actual value passed in should be converted to an enum-style class instance.
1772    * 
1773    * @param descriptor The {@link XMLFieldDescriptor} instance in question.
1774    * @param value The actual value (which might need conversion).
1775    * @return The value, potentially converted to an enum-style class.
1776    */
1777   private Object convertToEnumObject(final XMLFieldDescriptor descriptor, Object value) {
1778     Class<?> fieldType = descriptor.getFieldType();
1779     Method valueOfMethod;
1780     try {
1781       valueOfMethod = fieldType.getMethod("valueOf", new Class[] {String.class});
1782       if (valueOfMethod != null && Modifier.isStatic(valueOfMethod.getModifiers())) {
1783         Class<?> returnType = valueOfMethod.getReturnType();
1784         if (returnType.isAssignableFrom(fieldType)) {
1785           Object enumObject = valueOfMethod.invoke(null, new Object[] {value});
1786           value = enumObject;
1787         }
1788       }
1789     } catch (SecurityException e) {
1790       // TODO: well, cannot do anything about it
1791     } catch (NoSuchMethodException e) {
1792       // TODO: nothing to do, as it simply isn't an enum-style class
1793     } catch (IllegalArgumentException e) {
1794       // TODO: cannot really happen
1795     } catch (IllegalAccessException e) {
1796       // TODO: indicates that the valueOf() method isn't public
1797     } catch (InvocationTargetException e) {
1798       // TODO: hmm .. what else
1799     }
1800     return value;
1801   }
1802 
1803   /**
1804    * Processes the given IDREF.
1805    *
1806    * @param idRef the ID of the object in which to reference
1807    * @param descriptor the current FieldDescriptor
1808    * @param parent the current parent object
1809    * @return true if the ID was found and resolved properly
1810    */
1811   boolean processIDREF(final String idRef, final XMLFieldDescriptor descriptor,
1812       final Object parent) {
1813     Object value = _idResolver.resolve(idRef);
1814     if (value == null) {
1815       // -- save state to resolve later
1816       addReference(idRef, parent, descriptor);
1817     } else {
1818       FieldHandler handler = descriptor.getHandler();
1819       if (handler != null) {
1820         handler.setValue(parent, value);
1821       }
1822     }
1823     return (value != null);
1824   }
1825 
1826   /**
1827    * Finds and returns an XMLClassDescriptor for the given class name. If a ClassDescriptor could
1828    * not be found one will attempt to be generated.
1829    * 
1830    * @param className the name of the class to find the descriptor for
1831    **/
1832   private XMLClassDescriptor getClassDescriptor(String className) throws SAXException {
1833     Class<?> type = null;
1834     try {
1835       // -- use specified ClassLoader if necessary
1836       if (_loader != null) {
1837         type = _loader.loadClass(className);
1838       }
1839       // -- no loader available use Class.forName
1840       else
1841         type = Class.forName(className);
1842     } catch (ClassNotFoundException cnfe) {
1843       return null;
1844     }
1845     return getClassDescriptor(type);
1846 
1847   } // -- getClassDescriptor
1848 
1849   /**
1850    * Finds and returns an XMLClassDescriptor for the given class. If a ClassDescriptor could not be
1851    * found one will attempt to be generated.
1852    * 
1853    * @param cls the Class to get the ClassDescriptor for
1854    **/
1855   XMLClassDescriptor getClassDescriptor(final Class<?> cls) throws SAXException {
1856     if (cls == null) {
1857       return null;
1858     }
1859 
1860 
1861     // -- special case for strings
1862     if (cls == String.class) {
1863       return STRING_DESCRIPTOR;
1864     }
1865 
1866     if (cls.isArray()) {
1867       return null;
1868     }
1869     if (isPrimitive(cls)) {
1870       return null;
1871     }
1872 
1873     // TODO Joachim
1874     // if (_cdResolver == null)
1875     // _cdResolver = (XMLClassDescriptorResolver)
1876     // ClassDescriptorResolverFactory.createClassDescriptorResolver(BindingType.XML);
1877 
1878     XMLClassDescriptor classDesc = null;
1879 
1880     try {
1881       InternalContext ctx = getInternalContext();
1882       classDesc = (XMLClassDescriptor) ctx.getXMLClassDescriptorResolver().resolve(cls);
1883     } catch (ResolverException rx) {
1884       // TODO
1885     }
1886 
1887     if (classDesc != null) {
1888       return new InternalXMLClassDescriptor(classDesc);
1889     }
1890 
1891     if (LOG.isDebugEnabled()) {
1892       LOG.debug(ERROR_DID_NOT_FIND_CLASSDESCRIPTOR + cls.getName());
1893     }
1894 
1895     return classDesc;
1896   }
1897 
1898 
1899   /**
1900    * Finds and returns a ClassDescriptor for the given class. If a ClassDescriptor could not be
1901    * found one will attempt to be generated.
1902    * 
1903    * @param className the name of the class to get the Descriptor for
1904    **/
1905   XMLClassDescriptor getClassDescriptor(String className, ClassLoader loader) throws SAXException {
1906     // TODO: Joachim
1907     // if (_cdResolver == null)
1908     // _cdResolver = (XMLClassDescriptorResolver)
1909     // ClassDescriptorResolverFactory.createClassDescriptorResolver(BindingType.XML);
1910 
1911 
1912     XMLClassDescriptor classDesc = null;
1913     try {
1914       classDesc = getInternalContext().getXMLClassDescriptorResolver().resolve(className, loader);
1915     } catch (ResolverException rx) {
1916       throw new SAXException(rx);
1917     }
1918 
1919 
1920     if (classDesc != null) {
1921       return new InternalXMLClassDescriptor(classDesc);
1922     }
1923 
1924     if (LOG.isDebugEnabled()) {
1925       LOG.debug(ERROR_DID_NOT_FIND_CLASSDESCRIPTOR + className);
1926     }
1927 
1928     return classDesc;
1929   } // -- getClassDescriptor
1930 
1931   /**
1932    * Returns the XMLClassLoader
1933    */
1934   XMLClassDescriptor resolveByXMLName(String name, String namespace, ClassLoader loader)
1935       throws SAXException {
1936 
1937     try {
1938       return getInternalContext().getXMLClassDescriptorResolver().resolveByXMLName(name, namespace,
1939           loader);
1940     } catch (ResolverException rx) {
1941       throw new SAXException(rx);
1942     }
1943 
1944   }
1945 
1946   /**
1947    * Returns the package for the given Class
1948    *
1949    * @param type the Class to return the package of
1950    * @return the package for the given Class
1951    **/
1952   String getJavaPackage(Class<?> type) {
1953     if (type == null)
1954       return null;
1955     String pkg = _javaPackages.get(type);
1956     if (pkg == null) {
1957       pkg = type.getName();
1958       int idx = pkg.lastIndexOf('.');
1959       if (idx > 0)
1960         pkg = pkg.substring(0, idx);
1961       else
1962         pkg = "";
1963       _javaPackages.put(type, pkg);
1964     }
1965     return pkg;
1966   } // -- getJavaPackage
1967 
1968   /**
1969    * Returns the name of a class, handles array types
1970    * 
1971    * @return the name of a class, handles array types
1972    **/
1973   String className(Class<?> type) {
1974     if (type.isArray()) {
1975       return className(type.getComponentType()) + "[]";
1976     }
1977     return type.getName();
1978   } // -- className
1979 
1980 
1981   /**
1982    * Checks the given StringBuffer to determine if it only contains whitespace.
1983    *
1984    * @param sb the StringBuffer to check
1985    * @return true if the only whitespace characters were found in the given StringBuffer
1986    **/
1987   static boolean isWhitespace(CharSequence sb) {
1988     for (int i = 0; i < sb.length(); i++) {
1989       char ch = sb.charAt(i);
1990       switch (ch) {
1991         case ' ':
1992         case '\n':
1993         case '\t':
1994         case '\r':
1995           break;
1996         default:
1997           return false;
1998       }
1999     }
2000     return true;
2001   } // -- isWhitespace
2002 
2003   /**
2004    * Loads and returns the class with the given class name using the given loader.
2005    * 
2006    * @param className the name of the class to load
2007    * @param loader the ClassLoader to use, this may be null.
2008    **/
2009   Class<?> loadClass(String className, ClassLoader loader) throws ClassNotFoundException {
2010     // -- use passed in loader
2011     if (loader != null)
2012       return loader.loadClass(className);
2013     // -- use internal loader
2014     else if (_loader != null)
2015       return _loader.loadClass(className);
2016     // -- no loader available use Class.forName
2017     return Class.forName(className);
2018   } // -- loadClass
2019 
2020   /**
2021    * Resolves the current set of waiting references for the given Id.
2022    * 
2023    * @param id the id that references are waiting for.
2024    * @param value the value of the resolved id.
2025    * @throws SAXException Indicates a problem resolving an IDREF
2026    **/
2027   private void resolveReferences(final String id, final Object value)
2028       throws org.xml.sax.SAXException {
2029     if ((id == null) || (value == null)) {
2030       return;
2031     }
2032     if (_resolveTable == null) {
2033       return;
2034     }
2035 
2036     ReferenceInfo refInfo = _resolveTable.remove(id);
2037     while (refInfo != null) {
2038       try {
2039         FieldHandler handler = refInfo.getDescriptor().getHandler();
2040         if (handler != null) {
2041           handler.setValue(refInfo.getTarget(), value);
2042         }
2043 
2044         // -- special handling for MapItems
2045         if (refInfo.getTarget() instanceof MapItem) {
2046           resolveReferences(refInfo.getTarget().toString(), refInfo.getTarget());
2047         }
2048       } catch (java.lang.IllegalStateException ise) {
2049         String errorMsg =
2050             MessageFormat.format(resourceBundle.getString("unmarshalHandler.error.resolving.idRef"),
2051                 new Object[] {id, ise.toString()});
2052         throw new SAXException(errorMsg, ise);
2053       }
2054       refInfo = refInfo.getNext();
2055     }
2056   }
2057 
2058   /**
2059    * Converts a String to the given primitive object type.
2060    *
2061    * @param type the class type of the primitive in which to convert the String to
2062    * @param value the String to convert to a primitive
2063    * @param fieldDesc Descriptor for the given field (value)
2064    * @return the new primitive Object
2065    * @exception SAXException If the String cannot be converted to a primitive object type
2066    */
2067   Object toPrimitiveObject(final Class<?> type, final String value,
2068       final XMLFieldDescriptor fieldDesc) throws SAXException {
2069     try {
2070       return toPrimitiveObject(type, value);
2071     } catch (Exception ex) {
2072       UnmarshalState state = _stateStack.getLastState();
2073       if (state != null) {
2074         if (state.getObject() != null) {
2075           String errorMsg = MessageFormat.format(
2076               resourceBundle.getString("unmarshalHandler.error.unmarshal.field.of.class"),
2077               new Object[] {fieldDesc.getFieldName(), state.getObject().getClass().getName()});
2078           throw new SAXException(errorMsg, ex);
2079         }
2080       }
2081       String errorMsg =
2082           MessageFormat.format(resourceBundle.getString("unmarshalHandler.error.unmarshal.field"),
2083               new Object[] {fieldDesc.getFieldName()});
2084       throw new SAXException(errorMsg, ex);
2085     }
2086   }
2087 
2088 
2089   /**
2090    * Converts a {@link String} to the given primitive object type.
2091    *
2092    * @param type the class type of the primitive in which to convert the String to
2093    * @param value the {@link String} to convert to a primitive
2094    * @return the new primitive {@link Object}
2095    */
2096   public static Object toPrimitiveObject(final Class<?> type, String value) {
2097     return PrimitiveObjectFactory.getInstance().getObject(type, value);
2098   }
2099 
2100   /**
2101    * Internal class used for passing constructor argument information.
2102    */
2103   class Arguments {
2104     /**
2105      * Constructor argument values.
2106      */
2107     private Object[] _values = null;
2108     /**
2109      * Constructor argument types.
2110      */
2111     private Class<?>[] _types = null;
2112 
2113     /**
2114      * Returns the number of constructor arguments.
2115      * 
2116      * @return The number of constructor arguments.
2117      */
2118     public int size() {
2119       if (_values == null) {
2120         return 0;
2121       }
2122       return _values.length;
2123     }
2124 
2125     public Class<?>[] getTypes() {
2126       return _types;
2127     }
2128 
2129     public Object[] getValues() {
2130       return _values;
2131     }
2132 
2133     public Class<?> getType(int index) {
2134       return _types[index];
2135     }
2136 
2137     public void setValues(Object[] values) {
2138       _values = values;
2139     }
2140 
2141     public void setValue(int index, Object value) {
2142       _values[index] = value;
2143     }
2144 
2145     public void setTypes(Class<?>[] types) {
2146       _types = types;
2147     }
2148 
2149     public void setType(int index, Class<?> type) {
2150       _types[index] = type;
2151     }
2152 
2153   }
2154 
2155   /**
2156    * A class for handling Arrays during unmarshalling.
2157    *
2158    * @author <a href="mailto:kvisco@intalio.com">kvisco@intalio.com</a>
2159    */
2160   public static class ArrayHandler {
2161 
2162     Class<?> _componentType = null;
2163 
2164     ArrayList<Object> _items = null;
2165 
2166     /**
2167      * Creates a new ArrayHandler
2168      *
2169      * @param componentType the ComponentType for the array.
2170      */
2171     ArrayHandler(final Class<?> componentType) {
2172       if (componentType == null) {
2173         String errMsg = resourceBundle.getString("unmarshalHandler.error.componentType.null");
2174         throw new IllegalArgumentException(errMsg);
2175       }
2176       _componentType = componentType;
2177       _items = new ArrayList<Object>();
2178     } // -- ArrayHandler
2179 
2180     /**
2181      * Adds the given object to the underlying array.
2182      * 
2183      * @param obj The object to be added to the underlying array.
2184      */
2185     public void addObject(final Object obj) {
2186       if (obj == null) {
2187         return;
2188       }
2189       /*
2190        * disable check for now until we write a small function to handle primitive and their
2191        * associated wrapper classes if (!_componentType.isAssignableFrom(obj.getClass())) { String
2192        * err = obj.getClass().getName() + " is not an instanceof " + _componentType.getName(); throw
2193        * new IllegalArgumentException(err); }
2194        */
2195       _items.add(obj);
2196     }
2197 
2198     /**
2199      * Returns the data handled by this class as an array.
2200      * 
2201      * @return The data handled internally in the form of an array.
2202      */
2203     public Object getObject() {
2204       int size = _items.size();
2205       Object array = Array.newInstance(_componentType, size);
2206       for (int i = 0; i < size; i++) {
2207         Array.set(array, i, _items.get(i));
2208       }
2209       return array;
2210     }
2211 
2212     /**
2213      * Returns the component type handled by this class.
2214      * 
2215      * @return The component type handled by this class.
2216      */
2217     public Class<?> componentType() {
2218       return _componentType;
2219     }
2220 
2221   } // -- ArrayHandler
2222 
2223   /**
2224    * Returns the ObjectFactory instance in use.
2225    * 
2226    * @return the ObjectFactory instance in use.
2227    */
2228   public ObjectFactory getObjectFactory() {
2229     return _objectFactory;
2230   }
2231 
2232   /**
2233    * Sets a (custom) ObjectFactory instance.
2234    * 
2235    * @param objectFactory A (custom) ObjectFactory instance
2236    */
2237   public void setObjectFactory(ObjectFactory objectFactory) {
2238     _objectFactory = objectFactory;
2239   }
2240 
2241   /**
2242    * Returnss a refrence to the {@link UnmarshalStateStack} instance currently in use.
2243    * 
2244    * @return The {@link UnmarshalStateStack} in use.
2245    */
2246   public UnmarshalStateStack getStateStack() {
2247     return _stateStack;
2248   }
2249 
2250   /**
2251    * Returns the top {@link UnmarshalState} instance from the {@link UnmarshalStateStack}.
2252    * 
2253    * @return The top {@link UnmarshalState} instance.
2254    */
2255   public UnmarshalState getTopState() {
2256     return _topState;
2257   }
2258 
2259   /**
2260    * Returns the {@link StrictElementHandler} in use.
2261    * 
2262    * @return The {@link StrictElementHandler} in use.
2263    */
2264   public StrictElementHandler getStrictElementHandler() {
2265     return _strictElementHandler;
2266   }
2267 
2268   /**
2269    * Returns the {@link NamespaceHandling} in use.
2270    * 
2271    * @return The currently active {@link NamespaceHandling} instance.
2272    */
2273   public NamespaceHandling getNamespaceHandling() {
2274     return _namespaceHandling;
2275   }
2276 
2277   /**
2278    * Returns the current {@link ClassLoader} in use.
2279    * 
2280    * @return The {@link ClassLoader} in use.
2281    */
2282   public ClassLoader getClassLoader() {
2283     return _loader;
2284   }
2285 
2286   /**
2287    * Returns the currently used {@link AnyNodeUnmarshalHandler} instance.
2288    * 
2289    * @return The {@link AnyNodeUnmarshalHandler} in use.
2290    */
2291   public AnyNodeUnmarshalHandler getAnyNodeHandler() {
2292     return _anyNodeHandler;
2293   }
2294 
2295   /**
2296    * Returns the currently active {@link UnmarshalListenerDelegate} instance
2297    * 
2298    * @return The active {@link UnmarshalListenerDelegate} in use.
2299    */
2300   public UnmarshalListenerDelegate getDelegateUnmarshalListener() {
2301     return _delegateUnmarshalListener;
2302   }
2303 
2304   /**
2305    * Indicats whether Object instances should be re-used.
2306    * 
2307    * @return True if object instances should be re-used.
2308    */
2309   public boolean isReuseObjects() {
2310     return _reuseObjects;
2311   }
2312 
2313   /**
2314    * Hashtable to store idReference and ReferenceInfo
2315    * 
2316    * @return Hashtable
2317    */
2318   public Hashtable<String, ReferenceInfo> getResolveTable() {
2319     return _resolveTable;
2320   }
2321 
2322   /**
2323    * returns the AnyNode (if any).
2324    * 
2325    * @return AnyNode, could be null
2326    */
2327   public org.exolab.castor.types.AnyNode getAnyNode() {
2328     return _node;
2329   }
2330 
2331   /**
2332    * sets the AnyNode
2333    * 
2334    * @param node AnyNode
2335    */
2336   public void setAnyNode(org.exolab.castor.types.AnyNode node) {
2337     _node = node;
2338   }
2339 
2340   /**
2341    * Indicates whether it's necessary to clear any collection or not.
2342    * 
2343    * @return True if it's necessary to clear any collection.
2344    */
2345   public boolean isClearCollections() {
2346     return _clearCollections;
2347   }
2348 
2349 
2350 
2351 }
2352