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