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   * This file was originally developed by Keith Visco during the
44   * course of employment at Intalio Inc.
45   * All portions of this file developed by Keith Visco after Jan 19 2005 are
46   * Copyright (C) 2005 Keith Visco. All Rights Reserved.
47   *
48   * $Id$
49   */
50  package org.exolab.castor.xml;
51  
52  import java.io.IOException;
53  import java.io.PrintWriter;
54  import java.io.Writer;
55  import java.lang.reflect.Array;
56  import java.lang.reflect.Method;
57  import java.math.BigDecimal;
58  import java.util.ArrayList;
59  import java.util.Enumeration;
60  import java.util.HashSet;
61  import java.util.Iterator;
62  import java.util.List;
63  import java.util.Set;
64  import java.util.Stack;
65  import java.util.StringTokenizer;
66  
67  import org.apache.commons.lang.StringUtils;
68  import org.apache.commons.logging.Log;
69  import org.apache.commons.logging.LogFactory;
70  import org.castor.core.util.Base64Encoder;
71  import org.castor.core.util.HexDecoder;
72  import org.castor.core.util.Messages;
73  import org.castor.mapping.BindingType;
74  import org.castor.mapping.MappingUnmarshaller;
75  import org.castor.xml.InternalContext;
76  import org.castor.xml.XMLProperties;
77  import org.exolab.castor.mapping.ClassDescriptor;
78  import org.exolab.castor.mapping.CollectionHandler;
79  import org.exolab.castor.mapping.FieldHandler;
80  import org.exolab.castor.mapping.MapHandler;
81  import org.exolab.castor.mapping.MapItem;
82  import org.exolab.castor.mapping.Mapping;
83  import org.exolab.castor.mapping.MappingException;
84  import org.exolab.castor.mapping.MappingLoader;
85  import org.exolab.castor.mapping.handlers.MapHandlers;
86  import org.exolab.castor.mapping.loader.CollectionHandlers;
87  import org.exolab.castor.types.AnyNode;
88  import org.exolab.castor.util.SafeStack;
89  import org.exolab.castor.xml.descriptors.RootArrayDescriptor;
90  import org.exolab.castor.xml.descriptors.StringClassDescriptor;
91  import org.exolab.castor.xml.handlers.DateFieldHandler;
92  import org.exolab.castor.xml.handlers.EnumFieldHandler;
93  import org.exolab.castor.xml.util.AnyNode2SAX2;
94  import org.exolab.castor.xml.util.AttributeSetImpl;
95  import org.exolab.castor.xml.util.DocumentHandlerAdapter;
96  import org.exolab.castor.xml.util.SAX2DOMHandler;
97  import org.exolab.castor.xml.util.StaxEventHandler;
98  import org.exolab.castor.xml.util.StaxStreamHandler;
99  import org.exolab.castor.xml.util.XMLClassDescriptorAdapter;
100 import org.exolab.castor.xml.util.XMLClassDescriptorImpl;
101 import org.exolab.castor.xml.util.XMLFieldDescriptorImpl;
102 import org.w3c.dom.Node;
103 import org.xml.sax.ContentHandler;
104 import org.xml.sax.DocumentHandler;
105 import org.xml.sax.SAXException;
106 import org.xml.sax.helpers.AttributesImpl;
107 
108 import javax.xml.stream.XMLEventWriter;
109 import javax.xml.stream.XMLStreamWriter;
110 
111 import javax.xml.transform.Result;
112 import javax.xml.transform.dom.DOMResult;
113 import javax.xml.transform.sax.SAXResult;
114 import javax.xml.transform.stream.StreamResult;
115 
116 /**
117  * A Marshaller that serializes Java Object's to XML
118  *
119  * Note: This class is not thread safe, and not intended to be,
120  * so please create a new Marshaller for each thread if it
121  * is to be used in a multithreaded environment.
122  *
123  * @author <a href="mailto:keith AT kvisco DOT com">Keith Visco</a>
124  * @version $Revision$ $Date: 2006-04-13 06:47:36 -0600 (Thu, 13 Apr 2006) $
125  */
126 public class Marshaller extends MarshalFramework {
127 
128 
129     //---------------------------/
130     //- Private Class variables -/
131     //---------------------------/
132 
133     /**
134      * Logger from commons-logging.
135      */
136     private static final Log LOG = LogFactory.getLog(Marshaller.class);
137 
138     /**
139      * The CDATA type..uses for SAX attributes.
140     **/
141     private static final String CDATA = "CDATA";
142 
143     /**
144      * Default prefix for use when creating
145      * namespace prefixes.
146     **/
147     private static final String DEFAULT_PREFIX = "ns";
148 
149 
150     /**
151      * Message name for a non sax capable serializer error.
152     **/
153     private static final String SERIALIZER_NOT_SAX_CAPABLE
154         = "conf.serializerNotSaxCapable";
155 
156     /**
157      * Namespace declaration for xml schema instance.
158      */
159     private static final String XSI_PREFIX = "xsi";
160 
161     /**
162      * The xsi:type attribute.
163      */
164     private static final String XSI_TYPE = "xsi:type";
165 
166     /**
167      * Namespace prefix counter.
168      */
169     private int _namespaceCounter = 0;
170 
171     /**
172      * An instance of StringClassDescriptor.
173      */
174     private static final StringClassDescriptor STRING_CLASS_DESCRIPTOR
175         = new StringClassDescriptor();
176 
177     //----------------------------/
178     //- Private member variables -/
179     //----------------------------/
180 
181     /**
182      * A boolean to indicate whether or not we are
183      * marshalling as a complete document or not.
184     **/
185     private boolean _asDocument = true;
186 
187     /**
188      * The depth of the sub tree, 0 denotes document level.
189      */
190     private int _depth = 0;
191 
192     /**
193      * The output format to use with the serializer.
194      * This will be null if the user passed in their
195      * own DocumentHandler.
196     **/
197     private OutputFormat _format = null;
198 
199     /**
200      * The ContentHandler we are marshaling to.
201     **/
202     private ContentHandler  _handler      = null;
203 
204     /**
205      * Indicates whether whether or not to use xsi:type declarations in the output.
206     **/
207     private boolean _marshalExtendedType = true;
208 
209     /**
210      * The registered {@link MarshalListener} to receive notifications of pre- and post marshal for
211      * each object in the tree being marshaled.
212     **/
213     private MarshalListener _marshalListener = null;
214 
215     /**
216      * The name space stack.
217     **/
218     private NamespacesStack namespacesStack = new NamespacesStack();
219 
220     /**
221      * Records Java packages being used during marshaling.
222     **/
223     private List<String> _packages = new ArrayList<String>();
224 
225     /**
226      * A stack of parent objects...to prevent circular
227      * references from being marshaled.
228     **/
229     private Stack _parents  = new SafeStack();
230     
231     /**
232      * A list of ProcessingInstructions to output
233      * upon marshalling of the document.
234     **/
235     private List<ProcessingInstruction> _processingInstructions = new ArrayList<ProcessingInstruction>();
236 
237     /**
238      * Name of the root element to use.
239      */
240     private String _rootElement  = null;
241 
242     /**
243      * A boolean to indicate keys from a map
244      * should be saved when necessary.
245      */
246     private boolean _saveMapKeys = true;
247 
248     /**
249      * The serializer that is being used for marshalling.
250      * This may be null if the user passed in a DocumentHandler.
251     **/
252     private Serializer       _serializer   = null;
253 
254     /**
255      * A flag to allow suppressing namespaces.
256      */
257     private boolean _suppressNamespaces = false;
258 
259     /**
260      * A flag to allow suppressing the xsi:type attribute.
261      */
262     private boolean _suppressXSIType = false;
263     
264     private boolean _useXSITypeAtRoot = false;
265 
266     /**
267      * The set of optional top-level attributes
268      * set by the user.
269     **/
270     private AttributeSetImpl _topLevelAtts = new AttributeSetImpl();
271 
272     /**
273      * The AttributeList which is to be used during marshalling,
274      * instead of creating a bunch of new ones.
275      */
276     private AttributesImpl _attributes = new AttributesImpl();
277 
278     /**
279      * The validation flag.
280      */
281     private boolean _validate = false;
282 
283     /** Set of full class names of proxy interfaces. If the class to be marshalled implements
284      *  one of them the superclass will be marshalled instead of the class itself. */
285     private final Set<String> _proxyInterfaces = new HashSet<String>();
286 
287     /**
288      * Creates a new {@link Marshaller} with the given SAX {@link DocumentHandler}.
289      *
290      * @param handler the SAX {@link DocumentHandler} to "marshal" to.
291      *
292      * @throws IllegalArgumentException if the given {@link DocumentHandler} is null
293      * @deprecate Please use {@link XMLContext#createMarshaller()} and 
294      *    {@link Marshaller#setDocumentHandler(DocumentHandler)} instead
295      * 
296      * @see {@link XMLContext#createMarshaller()}
297      * @see {@link Marshaller#setDocumentHandler(DocumentHandler)}
298      * @see XMLContext
299      * 
300     **/
301     public Marshaller(final DocumentHandler handler) {
302         super(null);
303         checkNotNull(handler, "The given 'org.sax.DocumentHandler' instance is null.");
304 
305         setContentHandler(new DocumentHandlerAdapter(handler));
306     }
307 
308     /**
309      * Sets the given SAX {@link DocumentHandler} to 'marshal' into.
310      *
311      * @param handler the SAX {@link DocumentHandler} to "marshal" to.
312      *
313      * @throws IllegalArgumentException if the given {@link DocumentHandler} is null
314     **/
315     public void setDocumentHandler(final DocumentHandler handler) {
316         checkNotNull(handler, "The given 'org.sax.DocumentHandler' instance is null.");
317 
318         setContentHandler(new DocumentHandlerAdapter(handler));
319     }
320 
321     /**
322      * Creates a new {@link Marshaller} with the given SAX {@link ContentHandler}.
323      *
324      * @param contentHandler the {@link ContentHandler} to "marshal" to.
325      * @throws IllegalArgumentException if the gievn {@link ContentHandler} is null
326      * @deprecate Please use {@link XMLContext#createMarshaller()} and 
327      *    {@link Marshaller#setContentHandler(ContentHandler)} instead
328      * 
329      * @see {@link XMLContext#createMarshaller()}
330      * @see {@link Marshaller#setContentHandler(ContentHandler)}
331      * @see XMLContext
332      * 
333     **/
334     public Marshaller(final ContentHandler contentHandler) {
335         super(null);
336         checkNotNull(contentHandler, "The given 'org.sax.ContentHandler' is null.");
337 
338         setContentHandler(contentHandler);
339     }
340     
341     /**
342      * The one {@link Marshaller} constructor that is used by {@link XMLContext} which
343      * sets an {@link InternalContext} that comes from outside. Writer or {@link ContentHandler}
344      * have to be set in a second step.
345      * @param internalContext the {@link InternalContext} to initialize the {@link Marshaller}
346      * instance with
347      */
348     public Marshaller(final InternalContext internalContext) {
349         super(internalContext);
350     }
351 
352     /**
353      * Creates a default instance of Marshaller, where the sink needs to be set
354      * separately.
355      */
356     public Marshaller () {
357         super(null);
358     }
359 
360     /**
361      * Creates a new {@link Marshaller} with the given writer.
362      * @param out the {@link Writer} to serialise to.
363      * @throws IllegalArgumentException if the given {@link Writer} is null
364      * @throws IOException If the given {@link Writer} instance cannot be opened.
365      * @deprecate Please use {@link XMLContext#createMarshaller()} and 
366      *    {@link Marshaller#setWriter(Writer)} instead
367      * 
368      * @see {@link XMLContext#createMarshaller()}
369      * @see {@link Marshaller#setWriter(Writer)}
370      * @see XMLContext
371      * 
372     **/
373     public Marshaller(final Writer out) throws IOException {
374         super(null);
375         setWriter(out);
376     }
377 
378     /**
379      * Creates a new {@link Marshaller} with the given {@link XMLStreamWriter}.
380      *
381      * @param xmlStreamWriter the {@link XMLStreamWriter}
382      * @throws IllegalArgumentException if the given {@link XMLStreamWriter} is null
383      * @see {@link XMLContext#createMarshaller()}
384      * @see {@link Marshaller#setXmlStreamWriter(javax.xml.stream.XMLStreamWriter)}
385      * @see XMLContext
386      *
387      * @since 1.3.3
388      */
389     public Marshaller(XMLStreamWriter xmlStreamWriter) {
390         super(null);
391         setXmlStreamWriter(xmlStreamWriter);
392     }
393 
394     /**
395      * Creates a new {@link Marshaller} with the given {@link XMLEventWriter}.
396      *
397      * @param xmlEventWriter the {@link XMLEventWriter}
398      * @throws IllegalArgumentException if the given {@link XMLEventWriter} is null
399      * @see {@link XMLContext#createMarshaller()}
400      * @see {@link Marshaller#setXmlEventWriter(javax.xml.stream.XMLEventWriter)}
401      * @see XMLContext
402      *
403      * @since 1.3.3
404      */
405     public Marshaller(XMLEventWriter xmlEventWriter) {
406         super(null);
407         setXmlEventWriter(xmlEventWriter);
408     }
409 
410     /**
411      * Sets the java.io.Writer to be used during marshalling.
412      * 
413      * @param out
414      *            The writer to use for marshalling
415      *
416      * @throws IllegalArgumentException if out is null
417      * @throws IOException
418      *             If there's a problem accessing the java.io.Writer provided
419      */
420     public void setWriter (final Writer out) throws IOException {
421         checkNotNull(out, "The given 'java.io.Writer' instance is null.");
422 
423         configureSerializer(out);
424     }
425 
426     /**
427      * Sets the {@link Result} into which the output xml will be written. Currently this method supports
428      * {@link DOMResult}, {@link SAXResult} and {@link StreamResult}.
429      *
430      * @param result the {@link Result} instance to set
431      *
432      * @throws IllegalArgumentException if the result is null or it is not supported
433      *
434      * @since 1.3.3
435      */
436     public void setResult(Result result) throws IOException {
437         checkNotNull(result, "The given 'javax.xml.transform.Result' instance is null.");
438 
439         if(result instanceof DOMResult) {
440             DOMResult domResult = (DOMResult) result;
441 
442             if(domResult.getNode() != null) {
443                 // sets the dom node
444                 setNode(domResult.getNode());
445                 return;
446             }
447         } else if(result instanceof SAXResult) {
448             SAXResult saxResult = (SAXResult) result;
449 
450             if(saxResult.getHandler() != null) {
451                 // sets the content handler
452                 setContentHandler(saxResult.getHandler());
453                 return;
454                 // TODO what to do with lexical handler ?
455             }
456         } else if (result instanceof StreamResult) {
457             StreamResult streamResult = (StreamResult) result;
458 
459             if(streamResult.getWriter() != null) {
460                 // sets the writer
461                 setWriter(streamResult.getWriter());
462                 return;
463             } else if(streamResult.getOutputStream() != null) {
464                 // sets the output stream, wrapping it into a print writer instance
465                 setWriter(new PrintWriter(streamResult.getOutputStream()));
466                 return;
467             }
468         }
469 
470         throw new IllegalArgumentException(
471                 "The given 'javax.transofrm.xml.Result' is not supported, or were incorrectly instantiated.");
472     }
473 
474 
475     private void configureSerializer(Writer out) throws IOException
476     {
477         _serializer = getInternalContext().getSerializer();
478 
479         if (_serializer == null)
480             throw new RuntimeException("Unable to obtain serializer");
481 
482         _serializer.setOutputCharStream( out );
483 
484         //-- Due to a Xerces Serializer bug that doesn't allow declaring
485         //-- multiple prefixes to the same namespace, we use the old
486         //-- DocumentHandler format and process namespaces ourselves
487         _handler = new DocumentHandlerAdapter(_serializer.asDocumentHandler());
488 
489         if ( _handler == null ) {
490             String err = Messages.format( SERIALIZER_NOT_SAX_CAPABLE,
491                                           _serializer.getClass().getName() );
492             throw new RuntimeException( err );
493         }
494     }
495 
496     /**
497      * Creates a new {@link Marshaller} for the given DOM {@link Node}.
498      *
499      * @param node the DOM {@link Node} to marshal into.
500      *
501      * @throws IllegalArgumentException if node is null
502      *
503      * @deprecate Please use {@link XMLContext#createMarshaller()} and 
504      *    {@link Marshaller#setNode(Node)} instead
505      * 
506      * @see {@link XMLContext#createMarshaller()}
507      * @see {@link Marshaller#setNode(Node)}
508      * @see XMLContext
509     **/
510     public Marshaller(final Node node) {
511         super(null);
512         checkNotNull(node, "The given 'org.w3c.dom.Node' instance is null.");
513 
514         setContentHandler(new DocumentHandlerAdapter(new SAX2DOMHandler(node)));
515     }
516     
517     /**
518      * Sets the W3C {@link Node} instance to marshal to.
519      *
520      * @param node the DOM {@link Node} to marshal into.
521      *
522      * @throws IllegalArgumentException if node is null
523     **/
524     public void setNode(final Node node) {
525         checkNotNull(node, "The given 'org.w3c.dom.Node' instance is null.");
526 
527         setContentHandler(new DocumentHandlerAdapter(new SAX2DOMHandler(node)));
528     }
529 
530     /**
531      * Sets the {@link XMLStreamWriter} to use.
532      *
533      * @param xmlStreamWriter the {@link XMLStreamWriter} instance to use
534      *
535      * @throws IllegalArgumentException if the xmlStreamWriter is null
536      *
537      * @since 1.3.3
538      */
539     public void setXmlStreamWriter(XMLStreamWriter xmlStreamWriter) {
540         checkNotNull(xmlStreamWriter, "The given 'java.xml.stream.XMLStreamWriter' instance is null.");
541 
542         setContentHandler(new StaxStreamHandler(xmlStreamWriter));
543     }
544 
545     /**
546      * Sets the {@link XMLEventWriter} to use.
547      *
548      * @param xmlEventWriter the {@link XMLEventWriter} instance to use
549      *
550      * @throws IllegalArgumentException if the xmlEventReader is null
551      *
552      * @since 1.3.3
553      */
554     public void setXmlEventWriter(XMLEventWriter xmlEventWriter) {
555         checkNotNull(xmlEventWriter, "The given 'java.xml.stream.XMLEventWriter' instance is null.");
556 
557         setContentHandler(new StaxEventHandler(xmlEventWriter));
558     }
559     
560   /**
561    * To set the {@link InternalContext} to use, and to 
562    * initialize {@link Marshaller} properties linked to it.
563    * @param internalContext the {@link InternalContext} to use
564    */
565   public void setInternalContext(final InternalContext internalContext) {
566       super.setInternalContext(internalContext);
567       deriveProperties();
568   }
569 
570 
571   /**
572      * Derive class-level properties from {@link XMLProperties} as defined
573      * {@link InternalContext}. This method will be called after a new
574      * {@link InternalContext} or a property has been set.
575      * 
576      * @link #setInternalContext(InternalContext)
577      */
578     private void deriveProperties() {
579         _validate = getInternalContext().marshallingValidation();
580         _saveMapKeys = getInternalContext().getBooleanProperty(
581                 XMLProperties.SAVE_MAP_KEYS).booleanValue();
582 
583         String prop = getInternalContext().getStringProperty(
584                 XMLProperties.PROXY_INTERFACES);
585         if (prop != null) {
586             StringTokenizer tokenizer = new StringTokenizer(prop, ", ");
587             while (tokenizer.hasMoreTokens()) {
588                 _proxyInterfaces.add(tokenizer.nextToken());
589             }
590         }
591     }
592     
593    /**
594      * Adds the given processing instruction data to the set of
595      * processing instructions to output during marshalling.
596      *
597      * @param target the processing instruction target
598      * @param data the processing instruction data
599     *
600     * @throws IllegalArgumentException if target is null or empty string or data is null
601     **/
602     public void addProcessingInstruction(String target, String data) {
603 
604         checkNotEmpty(target, "The argument 'target' must not be null or empty.");
605         checkNotNull(data, "The argument 'data' must not be null.");
606 
607         _processingInstructions.add(new ProcessingInstruction(target, data));
608     } //-- addProcessingInstruction
609 
610    /**
611     * Sets the document type definition for the serializer. Note that this method
612     * cannot be called if you've passed in your own DocumentHandler.
613     *
614     * @param publicId the public identifier
615     * @param systemId the system identifier
616     */
617     public void setDoctype(String publicId, String systemId) {
618 
619         if (_serializer != null) {
620             if (_format == null) {
621                 _format = getInternalContext().getOutputFormat();
622             }
623             _format.setDoctype(publicId, systemId);
624             //-- reset output format, this needs to be done
625             //-- any time a change occurs to the format.
626             _serializer.setOutputFormat( _format );
627 
628             setDocumentHandler();
629         }
630         else {
631             String error = "doctype cannot be set if you've passed in "+
632             "your own DocumentHandler";
633             throw new IllegalStateException(error);
634         }
635     } //-- setDoctype
636 
637     /**
638      * Sets whether or not to marshal as a document which includes
639      * the XML declaration, and if necessary the DOCTYPE declaration.
640      * By default the Marshaller will marshal as a well formed
641      * XML document including the XML Declaration.
642      *
643      * If the given boolean is true, the Marshaller will marshal
644      * as a well formed XML fragment (no XML declaration or DOCTYPE).
645      *
646      * This method is basically the same as calling
647      * #setMarshalAsDocument(false);
648      *
649      * @param supressXMLDeclaration a boolean that when true
650      * includes that generated XML should not contain
651      * the XML declaration.
652      * @see #setMarshalAsDocument
653      */
654     public void setSupressXMLDeclaration(boolean supressXMLDeclaration) {
655         setMarshalAsDocument(!supressXMLDeclaration);
656     } //-- setSupressXMLDeclaration
657 
658     /**
659      * Sets whether or not to marshal as a document which includes
660      * the XML declaration, and if necessary the DOCTYPE declaration.
661      * By default the Marshaller will marshal as a well formed
662      * XML document including the XML Declaration.
663      *
664      * If the given boolean is false, the Marshaller will marshal
665      * as a well formed XML fragment (no XML declaration or DOCTYPE).
666      *
667      * This method is basically the same as calling
668      * #setSupressXMLDeclaration(true);
669      *
670      * @param asDocument a boolean, when true, indicating to marshal
671      * as a complete XML document.
672      * @see #setSupressXMLDeclaration
673      */
674     public void setMarshalAsDocument(boolean asDocument) {
675 
676         _asDocument = asDocument;
677 
678         if (_serializer != null) {
679 
680             if (_format == null) {
681                 _format = getInternalContext().getOutputFormat();
682             }
683             _format.setOmitXMLDeclaration( ! asDocument );
684             _format.setOmitDocumentType( ! asDocument );
685 
686             //-- reset output format, this needs to be done
687             //-- any time a change occurs to the format.
688             _serializer.setOutputFormat( _format );
689 
690             setDocumentHandler();
691         }
692 
693     } //-- setMarshalAsDocument
694 
695     /**
696      * Sets the given mapping to be used by the marshalling Framework. If a resolver
697      * exists this mapping will be added to the existing ClassDescriptorResolver.
698      * Otherwise a new ClassDescriptorResolver will be created.
699      *
700      * @param mapping Mapping to using during marshalling.
701      */
702     public void setMapping(final Mapping mapping) throws MappingException {
703 //        if (_cdResolver == null) {
704 //            _cdResolver = (XMLClassDescriptorResolver) ClassDescriptorResolverFactory
705 //                .createClassDescriptorResolver(BindingType.XML);
706 //        }
707         if ((getInternalContext() == null) 
708                 || (getInternalContext().getXMLClassDescriptorResolver() == null)) {
709             String message = "No internal context or no class descriptor in context.";
710             LOG.warn(message);
711             throw new IllegalStateException(message);
712         }
713 
714         MappingUnmarshaller mum = new MappingUnmarshaller();
715         MappingLoader resolver = mum.getMappingLoader(mapping, BindingType.XML);
716         getInternalContext().getXMLClassDescriptorResolver().setMappingLoader(resolver);
717     }
718 
719     /**
720      * Sets an optional MarshalListener to recieve pre and post
721      * marshal notification for each Object in the tree.
722      * MarshalListener is only for complex objects that map
723      * into elements, simpleTypes and types that map into
724      * attributes do not cause any pre and post event notifications.
725      * Current only one (1) listener is allowed. If you need
726      * register multiple listeners, you will have to create
727      * your own master listener that will forward the
728      * event notifications and manage the multiple
729      * listeners.
730      *
731      * @param listener the MarshalListener to set.
732     **/
733     public void setMarshalListener(MarshalListener listener) {
734         _marshalListener = listener;
735     }
736 
737     /**
738      * Sets the mapping for the given Namespace prefix.
739      * 
740      * @param nsPrefix the namespace prefix
741      * @param nsURI the namespace that the prefix resolves to
742      *
743      * @throws IllegalArgumentException if nsURI is null or empty string
744     **/
745     public void setNamespaceMapping(final String nsPrefix, final String nsURI) {
746         checkNotEmpty(nsURI, "Namespace URI must be not null.");
747         namespacesStack.addNamespace(nsPrefix, nsURI);
748     }
749 
750     /**
751      * Sets the name of the root element to use.
752      *
753      * @param rootElement The name of the root element to use.
754      */
755     public void setRootElement(final String rootElement) {
756         _rootElement = rootElement;
757     }
758 
759     /**
760      * Returns the name of the root element to use
761      * @return Returns the name of the root element to use
762      */
763     public String getRootElement()
764     {
765         return _rootElement;
766     } //-- getRootElement
767 
768     /**
769      * Set to True to declare the given namespace mappings at the root node. Default is False.
770      * @param nsPrefixAtRoot
771      * @deprecated
772      */
773     public void setNSPrefixAtRoot(boolean nsPrefixAtRoot)
774     {
775         // leaving for now...backward compatability
776         //_nsPrefixAtRoot = nsPrefixAtRoot;
777     }
778 
779     /**
780      * Returns True if the given namespace mappings will be declared at the root node.
781      * @return Returns True if the given namespace mappings will be declared at the root node.
782      * @deprecated
783      */
784     public boolean getNSPrefixAtRoot()
785     {
786         return true;
787     }
788 
789     /**
790      * Returns the ClassDescriptorResolver for use during marshalling
791      *
792      * @return the ClassDescriptorResolver
793      * @see #setResolver
794      */
795     public XMLClassDescriptorResolver getResolver() {
796 
797 //        if (_cdResolver == null) {
798 //            _cdResolver = (XMLClassDescriptorResolver)
799 //            ClassDescriptorResolverFactory.createClassDescriptorResolver(BindingType.XML);
800 //        }
801         if ((getInternalContext() == null) 
802                 || (getInternalContext().getXMLClassDescriptorResolver() == null)) {
803             String message = "No internal context or no class descriptor in context.";
804             LOG.warn(message);
805             throw new IllegalStateException(message);
806         }
807         return getInternalContext().getXMLClassDescriptorResolver();
808 
809     } //-- getResolver
810 
811     /**
812      * Sets the ClassDescriptorResolver to use during marshalling.
813      *
814      * <BR />
815      * <B>Note:</B> This method will nullify any Mapping
816      * currently being used by this Marshaller
817      *
818      * @param cdr the ClassDescriptorResolver to use
819      * @see #setMapping
820      * @see #getResolver
821      */
822     public void setResolver(final XMLClassDescriptorResolver cdr) {
823 
824         if (cdr != null) {
825             getInternalContext().setXMLClassDescriptorResolver(cdr);
826 //            _cdResolver = cdr;
827         }
828 
829     } //-- setResolver
830 
831     /**
832      * Sets whether or not to validate the object model
833      * before marshalling. By default validation is enabled.
834      * This method is really for debugging.
835      * I do not recommend turning off validation, since
836      * you could marshal a document, which you can then
837      * not unmarshal. If you know the object model
838      * is guaranteed to be valid, disabling validation will
839      * improve performace.
840      *
841      * @param validate the boolean indicating whether or not to
842      * validate the object model before marshalling.
843     **/
844     public void setValidation(boolean validate) {
845         _validate = validate;
846     } //-- setValidation
847     
848     public boolean getValidation() {
849         return _validate;
850     }
851 
852     /**
853      * If True the marshaller will use the 'xsi:type' attribute
854      * to marshall a field value that extended the defined field type.
855      * Default is True.
856      */
857     public void setMarshalExtendedType(boolean marshalExtendedType)
858     {
859         _marshalExtendedType = marshalExtendedType;
860     } //-- setMarshalExtendedType
861 
862 
863     /**
864      * If True the marshaller will use the 'xsi:type' attribute
865      * to marshall a field value that extended the defined field type.
866      * Default is True.
867      * @return If True the marshaller will use the 'xsi:type' attribute
868      * to marshall a field value that extended the defined field type.
869      * Default is True.
870      */
871     public boolean getMarshalExtendedType()
872     {
873         return _marshalExtendedType;
874     } //-- setMarshallExtendedType
875 
876     /**
877      * Marshals the given Object as XML using the given writer.
878      *
879      * @param object The Object to marshal.
880      * @param out The writer to marshal to.
881      * @exception org.exolab.castor.xml.MarshalException
882      * @exception org.exolab.castor.xml.ValidationException
883      */
884     public static void marshal(Object object, Writer out)
885     throws MarshalException, ValidationException {
886         try {
887             staticMarshal(object, new Marshaller(out));
888         } catch (IOException e) {
889             throw new MarshalException(e);
890         }
891     } //-- marshal
892 
893     /**
894      * Marshals the given Object as XML using the given DocumentHandler
895      * to send events to.
896      *
897      * @param object The Object to marshal.
898      * @param handler The DocumentHandler to marshal to.
899      * @exception org.exolab.castor.xml.MarshalException
900      * @exception org.exolab.castor.xml.ValidationException
901      */
902     public static void marshal(Object object, DocumentHandler handler)
903     throws MarshalException, ValidationException {
904         staticMarshal(object, new Marshaller(handler));
905     } //-- marshal
906 
907     /**
908      * Marshals the given Object as XML using the given ContentHandler
909      * to send events to.
910      *
911      * @param object The Object to marshal.
912      * @param handler The ContentHandler to marshal to.
913      * @exception org.exolab.castor.xml.MarshalException
914      * @exception org.exolab.castor.xml.ValidationException
915      */
916     public static void marshal(Object object, ContentHandler handler)
917     throws MarshalException, ValidationException {
918         staticMarshal(object, new Marshaller(handler));
919     } //-- marshal
920 
921     /**
922      * Marshals the given Object as XML using the given DOM Node
923      * to send events to.
924      *
925      * @param object The Object to marshal.
926      * @param node The DOM Node to marshal to.
927      * @exception org.exolab.castor.xml.MarshalException
928      * @exception org.exolab.castor.xml.ValidationException
929      */
930     public static void marshal(Object object, Node node)
931     throws MarshalException, ValidationException {
932         staticMarshal(object, new Marshaller(node));
933     } //-- marshal
934 
935     /**
936      * Static helper method to marshal the given object using the
937      * Marshaller instance provided.
938      *
939      * @param object The Object to marshal.
940      * @param marshaller The {@link Marshaller} to use for marshalling.
941      * @throws MarshalException as thrown by marshal(Object)
942      * @throws ValidationException as thrown by marshal(Object)
943      */
944     private static void staticMarshal(final Object object, final Marshaller marshaller)
945     throws MarshalException, ValidationException {
946         if (object == null) {
947             throw new MarshalException("object must not be null");
948         }
949 
950         if (LOG.isInfoEnabled()) {
951             LOG.info("Marshaller called using one of the *static* " + 
952                     " marshal(Object, *) methods. This will ignore any " +
953                     " mapping files as specified. Please consider switching to " +
954                     " using Marshaller instances and calling one of the" +
955                     " marshal(*) methods.");
956         }
957 
958         marshaller.marshal(object);
959     } //-- staticMarshal
960 
961     /**
962      * Marshals the given Object as XML using the DocumentHandler
963      * for this Marshaller.
964      *
965      * @param object The Object to marshal.
966      * @exception org.exolab.castor.xml.MarshalException
967      * @exception org.exolab.castor.xml.ValidationException
968      */
969     public void marshal(Object object)
970     throws MarshalException, ValidationException {
971         if (object == null)
972             throw new MarshalException("object must not be null");
973 
974         if (LOG.isDebugEnabled()) {
975             LOG.debug("Marshalling " + object.getClass().getName());
976         }
977 
978         if (object instanceof AnyNode) {
979            try{
980               AnyNode2SAX2.fireEvents((AnyNode)object, _handler, namespacesStack);
981            } catch(SAXException e) {
982                 throw new MarshalException(e);
983            }
984         }
985         else {
986              validate(object);
987              MarshalState mstate = new MarshalState(object, "root");
988              if (_asDocument) {
989                 try {
990                     _handler.startDocument();
991                     //-- handle processing instructions
992                     for (int i = 0; i < _processingInstructions.size(); i++) {
993                         ProcessingInstruction pi = _processingInstructions.get(i);
994                         _handler.processingInstruction(pi.getTarget(),
995                             pi.getData());
996                     }
997                     marshal(object, null, _handler, mstate);
998                     _handler.endDocument();
999                 } catch (SAXException sx) {
1000                     throw new MarshalException(sx);
1001                 }
1002              }
1003              else {
1004                 marshal(object, null, _handler, mstate);
1005              }
1006         }
1007 
1008     } //-- marshal
1009 
1010     /**
1011      * Marshals the given object, using the given descriptor
1012      * and document handler.
1013      *
1014      * <BR/>
1015      * <B>Note:</B>
1016      * <I>
1017      *   It is an error if this method is called with an
1018      *   AttributeDescriptor.
1019      * </I>
1020      * @param descriptor the XMLFieldDescriptor for the given object
1021      * @param handler the DocumentHandler to marshal to
1022      * @exception org.exolab.castor.xml.MarshalException
1023      * @exception org.exolab.castor.xml.ValidationException
1024      * during marshaling
1025     **/
1026     private void marshal
1027         (Object object,
1028          XMLFieldDescriptor descriptor,
1029          ContentHandler handler,
1030          final MarshalState mstate)
1031         throws MarshalException, ValidationException
1032     {
1033 
1034 
1035         if (object == null) {
1036             String err = "Marshaller#marshal: null parameter: 'object'";
1037             throw new MarshalException(err);
1038         }
1039 
1040         if (descriptor != null && descriptor.isTransient())
1041             return;
1042 
1043         //-- notify listener
1044         if (_marshalListener != null) {
1045             boolean toBeMarshalled = true;
1046             try {
1047                 toBeMarshalled = _marshalListener.preMarshal(object);
1048             } catch (RuntimeException e) {
1049                 LOG.error("Invoking #preMarshal() on your custom MarshalListener instance caused the following problem:", e);
1050             }
1051             if (!toBeMarshalled) {
1052                 return;
1053             }
1054         }
1055 
1056         //-- handle AnyNode
1057         if (object instanceof AnyNode) {
1058            try {
1059                AnyNode2SAX2.fireEvents((AnyNode) object, handler, namespacesStack);
1060            }catch (SAXException e) {
1061                throw new MarshalException(e);
1062            }
1063            return;
1064         }
1065 
1066         boolean containerField = false;
1067 
1068         if (descriptor != null && descriptor.isContainer()) {
1069             containerField = true;
1070         }
1071 
1072         //-- add object to stack so we don't potentially get into
1073         //-- an endlessloop
1074         if (_parents.search(object) >= 0) {
1075             return;
1076         }
1077         
1078         _parents.push(object);
1079 
1080         final boolean isNil = (object instanceof NilObject);
1081 
1082         Class<?> cls = null;
1083 
1084         if (!isNil) {
1085             cls = object.getClass();
1086             
1087             if (_proxyInterfaces.size() > 0) {
1088                 boolean isProxy = false;
1089                 
1090                 Class<?>[] interfaces = cls.getInterfaces();
1091                 for (int i = 0; i < interfaces.length; i++) {
1092                     if (_proxyInterfaces.contains(interfaces[i].getName())) { 
1093                         isProxy = true; 
1094                     }
1095                 }
1096                 
1097                 if (isProxy) { cls = cls.getSuperclass(); }
1098             }
1099         } else {
1100             cls = ((NilObject) object).getClassDescriptor().getJavaClass();
1101         }
1102 
1103         boolean byteArray = false;
1104         if (cls.isArray())
1105             byteArray = (cls.getComponentType() == Byte.TYPE);
1106 
1107         boolean atRoot = false;
1108         if (descriptor == null) {
1109             descriptor = new XMLFieldDescriptorImpl(cls, "root", null, null);
1110             atRoot = true;
1111         }
1112 
1113 
1114         //-- calculate Object's name
1115         String name = descriptor.getXMLName();
1116         if (atRoot && _rootElement!=null)
1117             name = _rootElement;
1118 
1119         boolean autoNameByClass = false;
1120         if (name == null) {
1121             autoNameByClass = true;
1122             name = cls.getName();
1123             //-- remove package information from name
1124             int idx = name.lastIndexOf('.');
1125             if (idx >= 0) {
1126                 name = name.substring(idx+1);
1127             }
1128             //-- remove capitalization
1129             name = getInternalContext().getXMLNaming().toXMLName(name);
1130         }
1131 
1132         //-- obtain the class descriptor
1133         XMLClassDescriptor classDesc = null;
1134         boolean saveType = false; /* flag for xsi:type */
1135 
1136         if (object instanceof NilObject) {
1137             classDesc = ((NilObject)object).getClassDescriptor();
1138         } else if (cls == descriptor.getFieldType()) {
1139             classDesc = (XMLClassDescriptor)descriptor.getClassDescriptor();
1140         }
1141 
1142         if (classDesc == null) {
1143 
1144             //-- check for primitive or String, we need to use
1145             //-- the special #isPrimitive method of this class
1146             //-- so that we can check for the primitive wrapper
1147             //-- classes
1148             if (isPrimitive(cls) || byteArray) {
1149                 classDesc = STRING_CLASS_DESCRIPTOR;
1150                 //-- check to see if we need to save the xsi:type
1151                 //-- for this class
1152                 Class<?> fieldType = descriptor.getFieldType();
1153                 if (cls != fieldType) {
1154                     while (fieldType.isArray()) {
1155                         fieldType = fieldType.getComponentType();
1156                     }
1157                     saveType = (!primitiveOrWrapperEquals(cls, fieldType));
1158                 }
1159             }
1160             else {
1161                 saveType = cls.isArray();
1162                 //-- save package information for use when searching
1163                 //-- for MarshalInfo classes
1164                 String className = cls.getName();
1165                 int idx = className.lastIndexOf(".");
1166                 String pkgName = null;
1167                 if (idx > 0) {
1168                     pkgName = className.substring(0,idx+1);
1169                     if (!_packages.contains(pkgName))
1170                         _packages.add(pkgName);
1171                 }
1172 
1173                 if (_marshalExtendedType) {
1174                     //-- Check to see if we can determine the class or
1175                     //-- ClassDescriptor from the type specified in the
1176                     //-- FieldHandler or from the current CDR state
1177 
1178                     if ((cls != descriptor.getFieldType()) || atRoot) {
1179 
1180                         saveType = true;
1181 
1182                         boolean containsDesc = false;
1183 
1184                         //-- if we're not at the root, check to see if we can resolve name.
1185                         //-- if we're at the root, the name will most likely be resolvable
1186                         //-- due to the validation step, so in most cases, if we are not
1187                         //-- using a mapping we need the xsi:type at the root
1188                         if (!atRoot) {
1189                             String nsURI = descriptor.getNameSpaceURI();
1190                             XMLClassDescriptor tmpDesc = null;
1191                             try {
1192                                 tmpDesc = getResolver().resolveByXMLName(name, nsURI, null);
1193                             }
1194                             catch(ResolverException rx) {
1195                                 //-- exception not important as we're simply
1196                                 //-- testing to see if we can resolve during
1197                                 //-- unmarshalling
1198                                 if (LOG.isDebugEnabled()) {
1199                                     LOG.debug("Error resolving", rx);
1200                                 }
1201                             }
1202 
1203                             if (tmpDesc != null) {
1204                                 Class<?> tmpType = tmpDesc.getJavaClass();
1205                                 if (tmpType == cls) {
1206                                     containsDesc = (!tmpType.isInterface());
1207                                 }
1208                             }
1209                         }
1210 
1211                         if (!containsDesc) {
1212                             //-- check for class mapping, we don't use the
1213                             //-- resolver directly because it will try to
1214                             //-- load a compiled descriptor, or introspect
1215                             //-- one
1216                             if (atRoot) {
1217                                 if (_useXSITypeAtRoot) {
1218                                     XMLMappingLoader ml = (XMLMappingLoader) getResolver().getMappingLoader();
1219                                     if (ml != null) {
1220                                         containsDesc = (ml.getDescriptor(cls.getName()) != null);
1221                                     }
1222                                 }
1223                                 else {
1224                                     //-- prevent xsi:type from appearing
1225                                     //-- on root
1226                                     containsDesc = true;
1227                                 }
1228 
1229                             }
1230 
1231                             //-- The following logic needs to be expanded to use
1232                             //-- namespace -to- package mappings
1233                             if ((!containsDesc) && (pkgName == null)) {
1234                                 //-- check to see if the class name is guessable
1235                                 //-- from the xml name
1236                                 classDesc = getClassDescriptor(cls);
1237                                 if (classDesc != null) {
1238                                     String tmpName = classDesc.getXMLName();
1239                                     if (name.equals(tmpName))
1240                                         saveType = false;
1241                                 }
1242                             }
1243                         }
1244 
1245                         if (containsDesc) saveType = false;
1246 
1247                     }
1248 
1249                     //  marshal as the actual type
1250                     if (classDesc == null)
1251                         classDesc = getClassDescriptor(cls);
1252 
1253                 } //-- end if (marshalExtendedType)
1254                 else {
1255                     // marshall as the base field type
1256                     cls = descriptor.getFieldType();
1257                     classDesc = getClassDescriptor(cls);
1258                 }
1259 
1260                 //-- If we are marshalling an array as the top
1261                 //-- level object, or if we run into a multi
1262                 //-- dimensional array, use the special
1263                 //-- ArrayDescriptor
1264                 if ((classDesc == null) && cls.isArray()) {
1265                     classDesc = new RootArrayDescriptor(cls);
1266                     if (atRoot) {
1267                         containerField = (!_asDocument);
1268                     }
1269                 }
1270             } //-- end else not primitive
1271 
1272             if (classDesc == null) {
1273                 //-- make sure we are allowed to marshal Object
1274                 if ((cls == Void.class) ||
1275                     (cls == Object.class) ||
1276                     (cls == Class.class)) {
1277 
1278                     throw new MarshalException
1279                         (MarshalException.BASE_CLASS_OR_VOID_ERR);
1280                 }
1281                 _parents.pop();
1282                 return;
1283             }
1284         }
1285 
1286         //-- handle auto-naming by class
1287         if (autoNameByClass) {
1288             if (classDesc.getXMLName() != null) {
1289                 name = classDesc.getXMLName();
1290             }
1291         }
1292 
1293         //-- at this point naming should be done, update
1294         //-- MarshalState.xmlName if root element
1295         if (atRoot) {
1296             mstate._xmlName = name;
1297         }
1298 
1299         //------------------------------------------------/
1300         //- Next few sections of code deal with xsi:type -/
1301         //- prevention, if necessary                     -/
1302         //------------------------------------------------/
1303 
1304         //-- Allow user to prevent xsi:type
1305         saveType = (saveType && (!_suppressXSIType));
1306 
1307         //-- Suppress xsi:type for special types
1308         if (saveType) {
1309             //-- java.util.Enumeration and java.util.Date fix
1310             if (descriptor.getHandler() instanceof DateFieldHandler)
1311                 saveType = false;
1312             else if (descriptor.getHandler() instanceof EnumFieldHandler)
1313                 saveType = false;
1314             else if (isNil)
1315                 saveType = false;
1316         }
1317 
1318         //-- Suppress 'xsi:type' attributes when Castor is able to infer
1319         //-- the correct type during unmarshalling
1320         if (saveType) {
1321              // When the type of the instance of the field is not the
1322              // type specified for the field, it might be necessary to
1323              // store the type explicitly (using xsi:type) to avoid
1324              // confusion during unmarshalling.
1325              //
1326              // However, it might be possible to use the XMLName of the
1327              // instance rather than the XMLName of the field. If
1328              // Castor could find back the type from the name of the
1329              // element, there is no need to add an xsi:type.
1330              //
1331              // In order to do that, there is two conditions:
1332              // 1. Castor should be able to find the right class to
1333              // instantiate form the XMLName of the instance.
1334              // 2. Castor should be sure than when using the XMLName of
1335              // the instance, there is only one field wich will match
1336              // that name (and that it is the current field)
1337 
1338              // XML Name associated with the class we are marshalling
1339              String xmlElementName = name;
1340              String xmlNamespace = descriptor.getNameSpaceURI();
1341 
1342              // We try to find if there is a XMLClassDescriptor associated
1343              // with the XML name of this class
1344              XMLClassDescriptor xmlElementNameClassDesc = null;
1345              try {
1346                  xmlElementNameClassDesc = getResolver().resolveByXMLName(xmlElementName, null, null);
1347              }
1348              catch(ResolverException rx) {
1349                  //-- exception not important as we're simply
1350                  //-- testing to see if we can resolve during
1351                  //-- unmarshalling
1352                  if (LOG.isDebugEnabled()) {
1353                     LOG.debug("Error resolving " + xmlElementName, rx);
1354                 }
1355              }
1356 
1357              // Test if we are not dealing with a source generated vector
1358              if ((xmlElementName != null) && (xmlElementNameClassDesc != null)) {
1359                  // More than one class can map to a given element name
1360                  try {
1361                      Iterator<ClassDescriptor> classDescriptorIter = getResolver().resolveAllByXMLName(xmlElementName, null, null);
1362                      for (; classDescriptorIter.hasNext();) {
1363                          xmlElementNameClassDesc = (XMLClassDescriptor) classDescriptorIter.next();
1364                          if (cls == xmlElementNameClassDesc.getJavaClass()) {
1365                             break;
1366                          }
1367                          //reset the classDescriptor --> none has been found
1368                          xmlElementNameClassDesc = null;
1369                      }
1370                  }
1371                  catch(ResolverException rx) {
1372                      if (LOG.isDebugEnabled()) {
1373                          LOG.debug("Error resolving " + xmlElementName, rx);
1374                      }
1375                      xmlElementNameClassDesc = null;
1376                  }
1377 
1378                  //make sure we only run into this logic if the classDescriptor
1379                  //is coming from a mapping file.
1380                  if  (xmlElementNameClassDesc instanceof XMLClassDescriptorAdapter) {
1381 
1382                     // Try to find a field descriptor directly in the parent object
1383                     XMLClassDescriptor tempContaining = (XMLClassDescriptor)descriptor.getContainingClassDescriptor();
1384 
1385                     //--if no containing class descriptor
1386                     //--it means the container class could have been introspected
1387                     //--so no need to enter the logic
1388                     if (tempContaining != null) {
1389                         XMLFieldDescriptor fieldDescMatch =
1390                             tempContaining.getFieldDescriptor(xmlElementName, xmlNamespace, NodeType.Element);
1391 
1392                         // Try to find a field descriptor by inheritance in the parent object
1393                         InheritanceMatch[] matches =
1394                               searchInheritance(xmlElementName, null, tempContaining); // TODO: Joachim, _cdResolver);
1395 
1396                         if (matches.length == 1) {
1397 
1398                             boolean foundTheRightClass = ((xmlElementNameClassDesc != null) && (cls == xmlElementNameClassDesc.getJavaClass()));
1399 
1400                             boolean oneAndOnlyOneMatchedField
1401                               = ((fieldDescMatch != null) ||
1402                                   (matches[0].parentFieldDesc == descriptor));
1403 
1404                             // Can we remove the xsi:type ?
1405                             if (foundTheRightClass && oneAndOnlyOneMatchedField) {
1406                                 saveType = false;
1407                                 //no name swapping for now
1408                             }
1409                         }//lengh is one
1410                     }
1411                  }//the classDesc comes from a mapping file
1412              }
1413          }//--- End of "if (saveType)"
1414 
1415 
1416         //------------------------/
1417         //- Namespace Management -/
1418         //------------------------/
1419 
1420         //-- Set a new namespace scoping
1421         //-- Note: We still need to declare a new scope even if
1422         //-- we are suppressing most namespaces. Certain elements
1423         //-- like xsi:type and xsi:nil will require a namespace
1424         //-- declaration and cannot be suppressed.
1425         if (!atRoot) {
1426            namespacesStack.addNewNamespaceScope();
1427         }
1428 
1429         String nsPrefix = "";
1430         String nsURI = "";
1431 
1432         if (!_suppressNamespaces) {
1433 
1434             //-- Must be done before any attributes are processed
1435             //-- since attributes can be namespaced as well.
1436 
1437             nsPrefix = descriptor.getNameSpacePrefix();
1438             if (nsPrefix == null) nsPrefix = classDesc.getNameSpacePrefix();
1439 
1440             nsURI = descriptor.getNameSpaceURI();
1441             if (nsURI == null) nsURI = classDesc.getNameSpaceURI();
1442 
1443             if ((nsURI == null) && (nsPrefix != null)) {
1444                 nsURI = namespacesStack.getNamespaceURI(nsPrefix);
1445             }
1446             else if ((nsPrefix == null) && (nsURI != null)) {
1447                 nsPrefix = namespacesStack.getNamespacePrefix(nsURI);
1448             }
1449             //-- declare namespace at this element scope?
1450             if (nsURI != null) {
1451                 String defaultNamespace = namespacesStack.getDefaultNamespaceURI();
1452                 if ((nsPrefix == null) && (!nsURI.equals(defaultNamespace)))
1453                 {
1454                     if ((defaultNamespace == null) && atRoot) {
1455                         nsPrefix = "";
1456                     }
1457                     else nsPrefix = DEFAULT_PREFIX + (++_namespaceCounter);
1458                 }
1459                 declareNamespace(nsPrefix, nsURI);
1460             }
1461             else {
1462                 nsURI = "";
1463                 //-- redeclare default namespace as empty
1464                 String defaultNamespace = namespacesStack.getNamespaceURI("");
1465                 if ((defaultNamespace != null) && (!"".equals(defaultNamespace)))
1466                   namespacesStack.addNamespace("", "");
1467             }
1468         }
1469 
1470 
1471 
1472         //---------------------/
1473         //- handle attributes -/
1474         //---------------------/
1475 
1476         AttributesImpl atts = new AttributesImpl();
1477 
1478         //-- user defined attributes
1479         if (atRoot) {
1480             //-- declare xsi prefix if necessary
1481             if (_topLevelAtts.getSize() > 0) {
1482                 namespacesStack.addNamespace(XSI_PREFIX, XSI_NAMESPACE);
1483             }
1484 
1485             for (int i = 0; i < _topLevelAtts.getSize(); i++) {
1486                 String localName = _topLevelAtts.getName(i);
1487                 String qName = localName;
1488                 String ns = "";
1489                 if (!_suppressNamespaces) {
1490                     ns = _topLevelAtts.getNamespace(i);
1491                     String prefix = null;
1492                     if (StringUtils.isNotEmpty(ns)) {
1493                         prefix = namespacesStack.getNonDefaultNamespacePrefix(ns);
1494                     }
1495                     if (StringUtils.isNotEmpty(prefix)) {
1496                         qName = prefix + ':' + qName;
1497                     }
1498                     if (ns == null) ns = "";
1499                 }
1500                 atts.addAttribute(ns, localName, qName, CDATA,
1501                     _topLevelAtts.getValue(i));
1502             }
1503         }
1504 
1505         //----------------------------
1506         //-- process attr descriptors
1507         //----------------------------
1508 
1509         int nestedAttCount = 0;
1510         XMLFieldDescriptor[] nestedAtts = null;
1511         XMLFieldDescriptor[] descriptors = null;
1512         if ((!descriptor.isReference()) && (!isNil)) {
1513             descriptors = classDesc.getAttributeDescriptors();
1514         }
1515         else {
1516             // references don't have attributes
1517             descriptors = NO_FIELD_DESCRIPTORS;
1518         }
1519 
1520         for (int i = 0; i < descriptors.length; i++) {
1521             XMLFieldDescriptor attributeDescriptor = descriptors[i];
1522             if (attributeDescriptor == null) {
1523                 continue;
1524             }
1525             String path = attributeDescriptor.getLocationPath();
1526             if (StringUtils.isNotEmpty(path)) {
1527                 //-- save for later processing
1528                 if (nestedAtts == null) {
1529                     nestedAtts = new XMLFieldDescriptor[descriptors.length - i];
1530                 }
1531                 nestedAtts[nestedAttCount] = attributeDescriptor;
1532                 nestedAttCount++;
1533             } else {
1534                 processAttribute(object, attributeDescriptor, atts);
1535             }
1536         }
1537 
1538         //-- handle ancestor nested attributes
1539         if (mstate._nestedAttCount > 0) {
1540             for (int i = 0; i < mstate._nestedAtts.length; i++) {
1541                 XMLFieldDescriptor attributeDescriptor = mstate._nestedAtts[i];
1542                 if (attributeDescriptor == null) {
1543                     continue;
1544                 }
1545                 String locationPath = attributeDescriptor.getLocationPath();
1546                 if (name.equals(locationPath)) {
1547                     // indicate that this 'nested' attribute has been processed
1548                     mstate._nestedAtts[i] = null;
1549                     // decrease number of unprocessed 'nested' attributes by one
1550                     mstate._nestedAttCount--;
1551                     processAttribute(mstate.getOwner(), attributeDescriptor, atts);
1552                 }
1553             }
1554         }
1555 
1556         //-- Look for attributes in container fields,
1557         //-- (also handle container in container)
1558         if (!isNil) processContainerAttributes(object, classDesc, atts);
1559 
1560         //-- xml:space
1561         String attValue = descriptor.getXMLProperty(XMLFieldDescriptor.PROPERTY_XML_SPACE);
1562         if (attValue != null) {
1563             atts.addAttribute(Namespaces.XML_NAMESPACE, SPACE_ATTR, XML_SPACE_ATTR, CDATA, attValue);
1564         }
1565 
1566         //-- xml:lang
1567         attValue = descriptor.getXMLProperty(XMLFieldDescriptor.PROPERTY_XML_LANG);
1568         if (attValue != null) {
1569             atts.addAttribute(Namespaces.XML_NAMESPACE, LANG_ATTR, XML_LANG_ATTR, CDATA, attValue);
1570         }
1571 
1572 
1573         //------------------/
1574         //- Create element -/
1575         //------------------/
1576 
1577 
1578         //-- save xsi:type information, if necessary
1579 
1580         if (saveType) {
1581             //-- declare XSI namespace, if necessary
1582             declareNamespace(XSI_PREFIX, XSI_NAMESPACE);
1583 
1584             //-- calculate type name, either use class name or
1585             //-- schema type name. If XMLClassDescriptor is introspected,
1586             //-- or is the default XMLClassDescriptorImpl, then
1587             //-- use java:classname, otherwise use XML name.
1588             String typeName = classDesc.getXMLName();
1589 
1590             //-- Check for introspection...
1591             boolean introspected = false;
1592             if (classDesc instanceof InternalXMLClassDescriptor)
1593                 introspected = ((InternalXMLClassDescriptor)classDesc).introspected();
1594             else
1595                 introspected = Introspector.introspected(classDesc);
1596 
1597             boolean useJavaPrefix = false;
1598             if ((typeName == null) || introspected) {
1599                 typeName = JAVA_PREFIX + cls.getName();
1600                 useJavaPrefix  = true;
1601             }
1602             else if (classDesc instanceof RootArrayDescriptor) {
1603                 typeName = JAVA_PREFIX + cls.getName();
1604                 useJavaPrefix = true;
1605             }
1606             else {
1607                 String dcn = classDesc.getClass().getName();
1608                 if (dcn.equals(XMLClassDescriptorImpl.class.getName())) {
1609                     typeName = JAVA_PREFIX + cls.getName();
1610                     useJavaPrefix = true;
1611                 }
1612                 else {
1613                     //-- calculate proper prefix
1614                     String tns = classDesc.getNameSpaceURI();
1615                     String prefix = null;
1616                     if (StringUtils.isNotEmpty(tns)) {
1617                         prefix = namespacesStack.getNamespacePrefix(tns);
1618                         if (StringUtils.isNotEmpty(prefix)) {
1619                             typeName = prefix + ':' + typeName;
1620                         }
1621                     }
1622                 }
1623             }
1624             //-- save type information
1625             atts.addAttribute(XSI_NAMESPACE, TYPE_ATTR, XSI_TYPE, CDATA, typeName);
1626             if (useJavaPrefix) {
1627                if (namespacesStack.getNamespaceURI("java") == null) {
1628                   //-- declare Java namespace, if necessary
1629                   declareNamespace("java", "http://java.sun.com");
1630                }
1631             }
1632         }
1633 
1634         if (isNil && !_suppressXSIType) {
1635             //-- declare XSI namespace, if necessary
1636             declareNamespace(XSI_PREFIX, XSI_NAMESPACE);
1637             //-- add xsi:nil="true"
1638             atts.addAttribute(XSI_NAMESPACE, NIL_ATTR, XSI_NIL_ATTR, CDATA, TRUE_VALUE);
1639         }
1640 
1641        //check if the value is a QName that needs to
1642        //be resolved ({URI}value -> ns:value)
1643        //This should be done BEFORE declaring the namespaces as attributes
1644        //because we can declare new namespace during the QName resolution
1645        String valueType = descriptor.getSchemaType();
1646        if ((valueType != null) && (valueType.equals(QNAME_NAME))) {
1647            object = resolveQName(object, descriptor);
1648        }
1649 
1650 
1651         String qName = null;
1652         if (nsPrefix != null) {
1653             int len = nsPrefix.length();
1654             if (len > 0) {
1655                 StringBuffer sb = new StringBuffer(len+name.length()+1);
1656                 sb.append(nsPrefix);
1657                 sb.append(':');
1658                 sb.append(name);
1659                 qName = sb.toString();
1660             }
1661             else qName = name;
1662         }
1663         else qName = name;
1664 
1665 
1666         Object firstNonNullValue = null;
1667         int    firstNonNullIdx   = 0;
1668 
1669         try {
1670 
1671 
1672             if (!containerField) {
1673 
1674 
1675                 //-- isNillable?
1676                 if ((!isNil) && descriptor.isNillable()) {
1677                     XMLFieldDescriptor desc = classDesc.getContentDescriptor();
1678                     descriptors = classDesc.getElementDescriptors();
1679                     int descCount = descriptors.length;
1680                     boolean isNilContent = (descCount > 0) || (desc != null);
1681 
1682                     //-- check content descriptor for a valid value
1683                     if (desc != null) {
1684                         Object value = desc.getHandler().getValue(object);
1685                         if (value != null) {
1686                             isNilContent = false;
1687                             descCount = 0;
1688                         }
1689                         else if (desc.isNillable() && desc.isRequired()) {
1690                             isNilContent = false;
1691                             descCount = 0;
1692                         }
1693                     }
1694 
1695 
1696                     for (int i = 0; i < descCount; i++) {
1697                         desc = descriptors[i];
1698                         if (desc == null) continue;
1699                         Object value = desc.getHandler().getValue(object);
1700                         if (value != null) {
1701                             isNilContent = false;
1702                             firstNonNullIdx = i;
1703                             firstNonNullValue = value;
1704                             break;
1705                         }
1706                         else if (desc.isNillable() && desc.isRequired()) {
1707                             isNilContent = false;
1708                             firstNonNullIdx = i;
1709                             firstNonNullValue = new NilObject(classDesc, desc);
1710                             break;
1711                         }
1712                     }
1713 
1714                     if (isNilContent) {
1715                         declareNamespace(XSI_PREFIX, XSI_NAMESPACE);
1716                         atts.addAttribute(XSI_NAMESPACE, NIL_ATTR, XSI_NIL_ATTR, CDATA, TRUE_VALUE);
1717                     }
1718                 }
1719 
1720                 //-- declare all necesssary namespaces
1721                 namespacesStack.getCurrentNamespaceScope().sendStartEvents(handler);
1722                 //-- Make sure qName is not null
1723                 if (qName == null) {
1724                     //-- hopefully this never happens, but if it does, it means
1725                     //-- we have a bug in our naming logic
1726                     String err = "Error in deriving name for type: " +
1727                         cls.getName() + ", please report bug to: " +
1728                         "http://castor.exolab.org.";
1729                     throw new IllegalStateException(err);
1730                 }
1731 
1732 
1733                 handler.startElement(nsURI, name, qName, atts);
1734             }
1735         }
1736         catch (org.xml.sax.SAXException sx) {
1737             throw new MarshalException(sx);
1738         }
1739 
1740 
1741         //---------------------------------------
1742         //-- process all child content, including
1743         //-- text nodes + daughter elements
1744         //---------------------------------------
1745 
1746         Stack<WrapperInfo> wrappers = null;
1747 
1748 
1749         //----------------------
1750         //-- handle text content
1751         //----------------------
1752 
1753         if (!isNil) {
1754 
1755             XMLFieldDescriptor cdesc = null;
1756             if (!descriptor.isReference()) {
1757                 cdesc = classDesc.getContentDescriptor();
1758             }
1759             if (cdesc != null) {
1760                 Object obj = null;
1761                 try {
1762                     obj = cdesc.getHandler().getValue(object);
1763                 }
1764                 catch(IllegalStateException ise) {
1765                     LOG.warn("Error getting value from: " + object, ise);
1766                 }
1767 
1768                 if (obj != null) {
1769 
1770                     //-- <Wrapper>
1771                     //-- handle XML path
1772                     String path = cdesc.getLocationPath();
1773                     String currentLoc = null;
1774 
1775                     if (path != null) {
1776                         _attributes.clear();
1777                         if (wrappers == null) {
1778                             wrappers  = new SafeStack<WrapperInfo>();
1779                         }
1780                         try {
1781                             while (path != null) {
1782 
1783                                 String elemName = null;
1784                                 int idx = path.indexOf('/');
1785 
1786                                 if (idx > 0) {
1787                                     elemName = path.substring(0, idx);
1788                                     path = path.substring(idx+1);
1789                                 }
1790                                 else {
1791                                     elemName = path;
1792                                     path = null;
1793                                 }
1794 
1795                                 //-- save current location without namespace
1796                                 //-- information for now.
1797                                 if (currentLoc == null)
1798                                     currentLoc = elemName;
1799                                 else
1800                                     currentLoc = currentLoc + "/" + elemName;
1801 
1802                                 String elemQName = elemName;
1803                                 if (StringUtils.isNotEmpty(nsPrefix)) {
1804                                     elemQName = nsPrefix + ':' + elemName;
1805                                 }
1806                                 wrappers.push(new WrapperInfo(elemName, elemQName, currentLoc));
1807 
1808 
1809                                 _attributes.clear();
1810                                 if (nestedAttCount > 0) {
1811                                     for (int na = 0; na < nestedAtts.length; na++) {
1812                                         if (nestedAtts[na] == null) continue;
1813                                         String tmpPath = nestedAtts[na].getLocationPath();
1814                                         if (tmpPath.equals(currentLoc)) {
1815                                             processAttribute(object, nestedAtts[na],_attributes);
1816                                             nestedAtts[na] = null;
1817                                             --nestedAttCount;
1818                                         }
1819                                     }
1820                                 }
1821                                 handler.startElement(nsURI, elemName, elemQName, _attributes);
1822                             }
1823                         }
1824                         catch(SAXException sx) {
1825                             throw new MarshalException(sx);
1826                         }
1827                     }
1828                     //-- </Wrapper>
1829 
1830                     char[] chars = null;
1831                     Class<?> objType = obj.getClass();
1832                     if (objType.isArray() && (objType.getComponentType() == Byte.TYPE)) {
1833                         //-- handle base64/hexbinary content
1834                         final String schemaType = descriptor.getSchemaType();
1835                         if (HexDecoder.DATA_TYPE.equals(schemaType)) {
1836                             chars = new String(HexDecoder.encode((byte[]) obj)).toCharArray();
1837                         } else {
1838                             chars = Base64Encoder.encode((byte[]) obj);
1839                         }
1840                     } else {
1841                         //-- all other types
1842                         String str = obj.toString();
1843                         if (StringUtils.isNotEmpty(str)) {
1844                             chars = str.toCharArray();
1845                         }
1846                     }
1847                     if ((chars != null) && (chars.length > 0)) {
1848                         try {
1849                             handler.characters(chars, 0, chars.length);
1850                         }
1851                         catch(org.xml.sax.SAXException sx) {
1852                             throw new MarshalException(sx);
1853                         }
1854                     }
1855                 }
1856             }
1857             //-- element references
1858             else if (descriptor.isReference()) {
1859                 Object id = getObjectID(object);
1860                 if (id != null) {
1861                     char[] chars = id.toString().toCharArray();
1862                     try {
1863                         handler.characters(chars, 0, chars.length);
1864                     }
1865                     catch(org.xml.sax.SAXException sx) {
1866                         throw new MarshalException(sx);
1867                     }
1868                 }
1869             }
1870             // special case for byte[]
1871             else if (byteArray) {
1872                 //-- Base64Encoding / HexBinary
1873                 String schemaType = descriptor.getSchemaType();
1874                 String componentType = descriptor.getComponentType();
1875                 char[] chars = new char[0];
1876                 if ((descriptor.isMultivalued() && HexDecoder.DATA_TYPE.equals(componentType)) || 
1877                         HexDecoder.DATA_TYPE.equals(schemaType)) {
1878                     chars = new String(HexDecoder.encode((byte[]) object)).toCharArray();
1879                 } else {
1880                     chars = Base64Encoder.encode((byte[]) object);
1881                 }
1882                 try {
1883                     handler.characters(chars, 0, chars.length);
1884                 }  catch (org.xml.sax.SAXException sx) {
1885                     throw new MarshalException(sx);
1886                 }
1887             }
1888             /* special case for Strings and primitives */
1889             else if (isPrimitive(cls)) {
1890                 
1891                 char[] chars;
1892                 if (cls == java.math.BigDecimal.class) {
1893                     chars = convertBigDecimalToString(object).toCharArray();
1894                 } else {
1895                     chars = object.toString().toCharArray();
1896                 }
1897                 try {
1898                     handler.characters(chars,0,chars.length);
1899                 }
1900                 catch(org.xml.sax.SAXException sx) {
1901                     throw new MarshalException(sx);
1902                 }
1903             }
1904             else if (isEnum(cls)) {
1905                 char[] chars = object.toString().toCharArray();
1906                 try {
1907                     handler.characters(chars,0,chars.length);
1908                 }
1909                 catch(org.xml.sax.SAXException sx) {
1910                     throw new MarshalException(sx);
1911                 }
1912                 
1913             }
1914         }
1915 
1916         //---------------------------
1917         //-- handle daughter elements
1918         //---------------------------
1919 
1920         if (isNil || descriptor.isReference()) {
1921             descriptors = NO_FIELD_DESCRIPTORS;
1922         }
1923         else {
1924             descriptors = classDesc.getElementDescriptors();
1925         }
1926 
1927         ++_depth;
1928 
1929         //-- marshal elements
1930         for (int i = firstNonNullIdx; i < descriptors.length; i++) {
1931 
1932             XMLFieldDescriptor elemDescriptor = descriptors[i];
1933             Object obj = null;
1934             boolean nil = false;
1935 
1936             //-- used previously cached value?
1937             if ((i == firstNonNullIdx) && (firstNonNullValue != null)) {
1938                 obj = firstNonNullValue;
1939             } else {
1940                 //-- obtain value from handler
1941                 try {
1942                     obj = elemDescriptor.getHandler().getValue(object);
1943                 } catch (IllegalStateException ise) {
1944                     LOG.warn("Error marshalling " + object, ise);
1945                     continue;
1946                 }
1947             }
1948             
1949             if (obj == null 
1950                     || (obj instanceof Enumeration && !((Enumeration<?>) obj).hasMoreElements())) {
1951                              if (elemDescriptor.isNillable() && (elemDescriptor.isRequired())) {
1952                     nil = true;
1953                 } else {
1954                     continue;
1955                 }
1956             }
1957 
1958 
1959             //-- handle XML path
1960             String path = elemDescriptor.getLocationPath();
1961             String currentLoc = null;
1962             //-- Wrapper/Location cleanup
1963             if (wrappers != null) {
1964                 try {
1965                     while (!wrappers.empty()) {
1966                         WrapperInfo wInfo = wrappers.peek();
1967                         if (path != null) {
1968                             if (wInfo._location.equals(path)) {
1969                                 path = null;
1970                                 break;
1971                             }
1972                             else if (path.startsWith(wInfo._location + "/")) {
1973                                 path = path.substring(wInfo._location.length()+1);
1974                                 currentLoc = wInfo._location;
1975                                 break;
1976                             }
1977                         }
1978                         handler.endElement(nsURI, wInfo._localName, wInfo._qName);
1979                         wrappers.pop();
1980                     }
1981                 }
1982                 catch(SAXException sx) {
1983                     throw new MarshalException(sx);
1984                 }
1985             }
1986 
1987 
1988             if (path != null) {
1989                 _attributes.clear();
1990                 if (wrappers == null) {
1991                     wrappers  = new SafeStack<WrapperInfo>();
1992                 }
1993                 try {
1994                     while (path != null) {
1995 
1996                         String elemName = null;
1997                         int idx = path.indexOf('/');
1998 
1999                         if (idx > 0) {
2000                             elemName = path.substring(0, idx);
2001                             path = path.substring(idx+1);
2002                         }
2003                         else {
2004                             elemName = path;
2005                             path = null;
2006                         }
2007 
2008                         //-- save current location without namespace
2009                         //-- information for now.
2010                         if (currentLoc == null)
2011                             currentLoc = elemName;
2012                         else
2013                             currentLoc = currentLoc + "/" + elemName;
2014 
2015                         String elemQName = elemName;
2016                         if (StringUtils.isNotEmpty(nsPrefix)) {
2017                             elemQName = nsPrefix + ':' + elemName;
2018                         }
2019                         wrappers.push(new WrapperInfo(elemName, elemQName, currentLoc));
2020 
2021 
2022                         _attributes.clear();
2023                         if (nestedAttCount > 0) {
2024                             for (int na = 0; na < nestedAtts.length; na++) {
2025                                 if (nestedAtts[na] == null) continue;
2026                                 String tmpPath = nestedAtts[na].getLocationPath();
2027                                 if (tmpPath.equals(currentLoc)) {
2028                                     processAttribute(object, nestedAtts[na],_attributes);
2029                                     nestedAtts[na] = null;
2030                                     --nestedAttCount;
2031                                 }
2032                             }
2033                         }
2034                         handler.startElement(nsURI, elemName, elemQName, _attributes);
2035                     }
2036                 }
2037                 catch(SAXException sx) {
2038                     throw new MarshalException(sx);
2039                 }
2040             }
2041 
2042             if (nil) {
2043                 obj = new NilObject(classDesc, elemDescriptor);
2044             }
2045 
2046             final Class<?> type = obj.getClass();
2047 
2048             MarshalState myState = mstate.createMarshalState(object, name);
2049             myState._nestedAtts = nestedAtts;
2050             myState._nestedAttCount = nestedAttCount;
2051 
2052 
2053             //-- handle byte arrays
2054             if (type.isArray() && (type.getComponentType() == Byte.TYPE)) {
2055                 marshal(obj, elemDescriptor, handler, myState);
2056             } else if (type.isArray() && elemDescriptor.isDerivedFromXSList()) {
2057                 Object buffer = processXSListType(obj, elemDescriptor);
2058                 String elemName = elemDescriptor.getXMLName();
2059                 String elemQName = elemName;
2060                 if (StringUtils.isNotEmpty(nsPrefix)) {
2061                     elemQName = nsPrefix + ':' + elemName;
2062                 }
2063                 char[] chars = buffer.toString().toCharArray();
2064                 try {
2065                     handler.startElement(nsURI, elemName, elemQName, _attributes);
2066                     handler.characters(chars,0,chars.length);
2067                     handler.endElement(nsURI, elemName, elemQName);
2068                 }
2069                 catch(org.xml.sax.SAXException sx) {
2070                     throw new MarshalException(sx);
2071                 }
2072             }
2073             //-- handle all other collection types
2074             else if (isCollection(type)) {
2075                 boolean processCollection = true;
2076                 if (_saveMapKeys) {
2077                     MapHandler mapHandler = MapHandlers.getHandler(type);
2078                     if (mapHandler != null) {
2079                         processCollection = false;
2080                         MapItem item = new MapItem();
2081                         Enumeration<?> keys = mapHandler.keys(obj);
2082                         while (keys.hasMoreElements()) {
2083                             item.setKey(keys.nextElement());
2084                             item.setValue(mapHandler.get(obj, item.getKey()));
2085                             marshal(item, elemDescriptor, handler, myState);
2086                         }
2087                     }
2088 
2089                 }
2090                 if (processCollection) {
2091                     CollectionHandler<?> colHandler = getCollectionHandler(type);
2092                     Enumeration<?> enumeration = colHandler.elements(obj);
2093                     while (enumeration.hasMoreElements()) {
2094                         Object item = enumeration.nextElement();
2095                         if (item != null) {
2096                             marshal(item, elemDescriptor, handler, myState);
2097                         }
2098                     }
2099                 }
2100             }
2101             //-- otherwise just marshal object as is
2102             else {
2103                 marshal(obj, elemDescriptor, handler, myState);
2104             }
2105 
2106             if (nestedAttCount > 0) {
2107                 nestedAttCount = myState._nestedAttCount;
2108             }
2109 
2110         }
2111 
2112 
2113         //-- Wrapper/Location cleanup for elements
2114         if (wrappers != null) {
2115             try {
2116                 while (!wrappers.empty()) {
2117                     WrapperInfo wInfo = wrappers.peek();
2118                     boolean popStack = true;
2119                     if (nestedAttCount > 0) {
2120                         for (int na = 0; na < nestedAtts.length; na++) {
2121                             // TODO[LL]: refactor to avoid check against null
2122                             if (nestedAtts[na] == null) continue;
2123                             String nestedAttributePath = nestedAtts[na].getLocationPath();
2124                             if (nestedAttributePath.startsWith(wInfo._location + "/")) {
2125                                 popStack = false;
2126                                 break;
2127                             }
2128                         }
2129                     }
2130                     if (popStack) {
2131                         handler.endElement(nsURI, wInfo._localName, wInfo._qName);
2132                         wrappers.pop();
2133                     } else {
2134                         break;
2135                     }
2136                 }
2137             }
2138             catch(SAXException sx) {
2139                 throw new MarshalException(sx);
2140             }
2141         }
2142 
2143         // TODO: Handling additional attributes at the end causes elements to be marshalled in the wrong
2144         // order when element 'text' is null, but their attribute value is not null. Can this be fixed
2145         // to process element attributes even when the element text value is null?
2146 
2147         if (wrappers != null && !wrappers.isEmpty()) {
2148             dealWithNestedAttributesNested(object, handler, nsPrefix, nsURI,
2149                     nestedAttCount, nestedAtts, wrappers);
2150         }
2151 
2152         dealWithNestedAttributes(object, handler, nsPrefix, nsURI,
2153                 nestedAttCount, nestedAtts, new SafeStack<WrapperInfo>());
2154 
2155         //-- finish element
2156         try {
2157             if (!containerField) {
2158                 handler.endElement(nsURI, name, qName);
2159                 //-- undeclare all necesssary namespaces
2160                 namespacesStack.getCurrentNamespaceScope().sendEndEvents(handler);
2161             }
2162         }
2163         catch(org.xml.sax.SAXException sx) {
2164             throw new MarshalException(sx);
2165         }
2166 
2167         --_depth;
2168         _parents.pop();
2169         if (!atRoot) {
2170            namespacesStack.removeNamespaceScope();
2171         }
2172 
2173         //-- notify listener of post marshal
2174         if (_marshalListener != null) {
2175             try {
2176             _marshalListener.postMarshal(object);
2177             } catch (RuntimeException e) {
2178                 LOG.error("Invoking #postMarshal() on your custom MarshalListener instance caused the following problem:", e);
2179             }
2180         }
2181 
2182     }
2183 
2184    private void dealWithNestedAttributes(Object object,
2185             ContentHandler handler, String nsPrefix, String nsURI,
2186             int nestedAttCount, XMLFieldDescriptor[] nestedAtts, Stack<WrapperInfo> wrappers)
2187             throws MarshalException {
2188         //-- Handle any additional attribute locations that were
2189         //-- not handled when dealing with wrapper elements
2190         if (nestedAttCount > 0) {
2191             for (int i = 0; i < nestedAtts.length; i++) {
2192                 if (nestedAtts[i] == null) continue;
2193                 String path = nestedAtts[i].getLocationPath();
2194                 String currentLoc = null;
2195                 
2196                 
2197                 //-- Make sure attribute has value before continuing
2198                 //-- We really could use a FieldHandler#hasValue()
2199                 //-- method (since sometimes getValue() methods may
2200                 //-- be expensive and we don't always want to call it
2201                 //-- multiple times)
2202                 if (nestedAtts[i].getHandler().getValue(object) == null) {
2203                     nestedAtts[i] = null;
2204                     -- nestedAttCount;
2205                     continue;
2206                 }
2207                 try {
2208                     while (path != null) {
2209                         int idx = path.indexOf('/');
2210                         String elemName = null;
2211                         if (idx > 0) {
2212                             elemName = path.substring(0,idx);
2213                             path = path.substring(idx+1);
2214                         }
2215                         else {
2216                             elemName = path;
2217                             path = null;
2218                         }
2219                         if (currentLoc == null)
2220                             currentLoc = elemName;
2221                         else
2222                             currentLoc = currentLoc + "/" + elemName;
2223 
2224                         String elemQName = elemName;
2225                         if (StringUtils.isNotEmpty(nsPrefix)) {
2226                             elemQName = nsPrefix + ':' + elemName;
2227                         }
2228                         wrappers.push(new WrapperInfo(elemName, elemQName, null));
2229 
2230                         _attributes.clear();
2231                         if (path == null) {
2232                             processAttribute(object, nestedAtts[i],_attributes);
2233                             nestedAtts[i] = null;
2234                             --nestedAttCount;
2235                         }
2236                         if (nestedAttCount > 0) {
2237                             for (int na = i+1; na < nestedAtts.length; na++) {
2238                                 if (nestedAtts[na] == null) continue;
2239                                 String tmpPath = nestedAtts[na].getLocationPath();
2240                                 if (tmpPath.equals(currentLoc)) {
2241                                     processAttribute(object, nestedAtts[na],_attributes);
2242                                     nestedAtts[na] = null;
2243                                     --nestedAttCount;
2244                                 }
2245                             }
2246                         }
2247                         handler.startElement(nsURI, elemName, elemQName, _attributes);
2248                     }
2249 
2250                     while (!wrappers.empty()) {
2251                         WrapperInfo wInfo = wrappers.pop();
2252                         handler.endElement(nsURI, wInfo._localName, wInfo._qName);
2253                     }
2254                 } catch (Exception e) {
2255                     throw new MarshalException(e);
2256                 }
2257             }
2258         } // if (nestedAttCount > 0)
2259     }
2260 
2261     private void dealWithNestedAttributesNested(Object object,
2262             ContentHandler handler, String nsPrefix, String nsURI,
2263             int nestedAttCount, XMLFieldDescriptor[] nestedAtts, Stack<WrapperInfo> wrappers)
2264             throws MarshalException {
2265         //-- Handle any additional attribute locations that were
2266         //-- not handled when dealing with wrapper elements
2267         
2268         WrapperInfo wrapperInfo = wrappers.peek();
2269         String currentLocation = wrapperInfo._location;
2270         
2271         if (nestedAttCount > 0) {
2272             for (int i = 0; i < nestedAtts.length; i++) {
2273                 if (nestedAtts[i] == null) continue;
2274                 String nestedAttributePath = nestedAtts[i].getLocationPath();
2275                 
2276                 if (!nestedAttributePath.startsWith(currentLocation + "/")) {
2277                     continue;
2278                 }
2279                 
2280                 nestedAttributePath = nestedAttributePath.substring(wrapperInfo._location.length() + 1);
2281                 String currentLoc = currentLocation;
2282                 
2283                 
2284                 //-- Make sure attribute has value before continuing
2285                 //-- We really could use a FieldHandler#hasValue()
2286                 //-- method (since sometimes getValue() methods may
2287                 //-- be expensive and we don't always want to call it
2288                 //-- multiple times)
2289                 if (nestedAtts[i].getHandler().getValue(object) == null) {
2290                     nestedAtts[i] = null;
2291                     -- nestedAttCount;
2292                     continue;
2293                 }
2294                 try {
2295                     while (nestedAttributePath != null) {
2296                         int idx = nestedAttributePath.indexOf('/');
2297                         String elemName = null;
2298                         if (idx > 0) {
2299                             elemName = nestedAttributePath.substring(0,idx);
2300                             nestedAttributePath = nestedAttributePath.substring(idx+1);
2301                         }
2302                         else {
2303                             elemName = nestedAttributePath;
2304                             nestedAttributePath = null;
2305                         }
2306                         if (currentLoc == null)
2307                             currentLoc = elemName;
2308                         else
2309                             currentLoc = currentLoc + "/" + elemName;
2310 
2311                         String elemQName = elemName;
2312                         if (StringUtils.isNotEmpty(nsPrefix)) {
2313                             elemQName = nsPrefix + ':' + elemName;
2314                         }
2315                         wrappers.push(new WrapperInfo(elemName, elemQName, null));
2316 
2317                         _attributes.clear();
2318                         if (nestedAttributePath == null) {
2319                             processAttribute(object, nestedAtts[i],_attributes);
2320                             nestedAtts[i] = null;
2321                             --nestedAttCount;
2322                         }
2323                         if (nestedAttCount > 0) {
2324                             for (int na = i+1; na < nestedAtts.length; na++) {
2325                                 if (nestedAtts[na] == null) continue;
2326                                 String tmpPath = nestedAtts[na].getLocationPath();
2327                                 if (tmpPath.equals(currentLoc)) {
2328                                     processAttribute(object, nestedAtts[na],_attributes);
2329                                     nestedAtts[na] = null;
2330                                     --nestedAttCount;
2331                                 }
2332                             }
2333                         }
2334                         handler.startElement(nsURI, elemName, elemQName, _attributes);
2335                     }
2336 
2337                     while (!wrappers.empty()) {
2338                         WrapperInfo wInfo = wrappers.pop();
2339                         handler.endElement(nsURI, wInfo._localName, wInfo._qName);
2340                     }
2341                 } catch (Exception e) {
2342                     throw new MarshalException(e);
2343                 }
2344             }
2345         } // if (nestedAttCount > 0)
2346     }
2347 
2348 
2349     /**
2350      * Converts a {@link BigDecimal} instance value to its String representation. This
2351      * method will take into into account the Java version number, as the semantics of
2352      * BigDecimal.toString() have changed between Java 1.4 and Java 5.0 and above.
2353      * @param object The {@link BigDecimal} instance
2354      * @return The String representation of the {@link BigDecimal} instance
2355      * @throws MarshalException If invocation of BigDecimal#toPlainString() fails.
2356      */
2357     private String convertBigDecimalToString(Object object) throws MarshalException {
2358         String stringValue;
2359         float javaVersion = Float.parseFloat(System.getProperty("java.specification.version"));
2360         if (javaVersion >= 1.5) {
2361             // as of Java 5.0 and above, BigDecimal.toPlainString() should be used.
2362             // TODO: reconsider if we start using BigDecimal for XSTypes that can hold scientific values
2363             Method method;
2364             try {
2365                 method = java.math.BigDecimal.class.getMethod("toPlainString", (Class[]) null);
2366                 stringValue = (String) method.invoke(object, (Object[]) null);
2367             } catch (Exception e) {
2368                 LOG.error("Problem accessing java.math.BigDecimal.toPlainString().", e);
2369                 throw new MarshalException("Problem accessing java.math.BigDecimal.toPlainString().", e);
2370             }
2371         } else {
2372             // use BigDecimal.toString() with Java 1.4 and below
2373             stringValue = object.toString();
2374         }
2375         return stringValue;
2376     }
2377 
2378     /**
2379      * Retrieves the ID for the given Object
2380      *
2381      * @param object the Object to retrieve the ID for
2382      * @return the ID for the given Object
2383     **/
2384     private Object getObjectID(Object object)
2385         throws MarshalException
2386     {
2387         if (object == null) return null;
2388 
2389         Object id = null;
2390         XMLClassDescriptor cd = getClassDescriptor(object.getClass());
2391         String err = null;
2392         if (cd != null) {
2393             XMLFieldDescriptor fieldDesc
2394                 = (XMLFieldDescriptor) cd.getIdentity();
2395             if (fieldDesc != null) {
2396                 FieldHandler<?> fieldHandler = fieldDesc.getHandler();
2397                 if (fieldHandler != null) {
2398                     try {
2399                         id = fieldHandler.getValue(object);
2400                     }
2401                     catch(IllegalStateException ise) {
2402                         err = ise.toString();
2403                     }
2404                 }//fieldHandler != null
2405                 else {
2406                     err = "FieldHandler for Identity descriptor is null.";
2407                 }
2408             }//fieldDesc != null
2409             else err = "No identity descriptor available";
2410         }//cd!=null
2411         else  {
2412             err = "Unable to resolve ClassDescriptor.";
2413         }
2414         if (err != null) {
2415             String errMsg = "Unable to resolve ID for instance of class '";
2416             errMsg += object.getClass().getName();
2417             errMsg += "' due to the following error: ";
2418             throw new MarshalException(errMsg + err);
2419         }
2420         return id;
2421     } //-- getID
2422 
2423 
2424     /**
2425      * Declares the given namespace, if not already in scope
2426      *
2427      * @param nsPrefix the namespace prefix
2428      * @param nsURI the namespace URI to declare
2429      * @return true if the namespace was not in scope and was
2430      *  sucessfully declared, other false
2431     **/
2432     private boolean declareNamespace(String nsPrefix, String nsURI)
2433     {
2434         boolean declared = false;
2435 
2436         //-- make sure it's not already declared...
2437         if ( (nsURI != null) && (nsURI.length() != 0)) {
2438 
2439             String tmpURI = namespacesStack.getNamespaceURI(nsPrefix);
2440             if ((tmpURI != null) && (tmpURI.equals(nsURI))) {
2441                 return declared;
2442             }
2443             String tmpPrefix = namespacesStack.getNamespacePrefix(nsURI);
2444             if ((tmpPrefix == null) || (!tmpPrefix.equals(nsPrefix))) {
2445                 namespacesStack.addNamespace(nsPrefix, nsURI);
2446                 declared = true;
2447             }
2448         }
2449         return declared;
2450     } //-- declareNamespace
2451 
2452     /**
2453      * Sets the PrintWriter used for logging
2454      * @param printWriter the PrintWriter to use for logging
2455     **/
2456     public void setLogWriter(final PrintWriter printWriter) { }
2457 
2458     /**
2459      * Sets the encoding for the serializer. Note that this method
2460      * cannot be called if you've passed in your own DocumentHandler.
2461      *
2462      * @param encoding the encoding to set
2463     **/
2464     public void setEncoding(String encoding) {
2465 
2466         if (_serializer != null) {
2467             if (_format == null) {
2468                 _format = getInternalContext().getOutputFormat();
2469             }
2470             _format.setEncoding(encoding);
2471             //-- reset output format, this needs to be done
2472             //-- any time a change occurs to the format.
2473             _serializer.setOutputFormat( _format );
2474             try {
2475                 //-- Due to a Xerces Serializer bug that doesn't allow declaring
2476                 //-- multiple prefixes to the same namespace, we use the old
2477                 //-- DocumentHandler format and process namespaces ourselves
2478                 _handler = new DocumentHandlerAdapter(_serializer.asDocumentHandler());
2479             }
2480             catch (java.io.IOException iox) {
2481                 //-- we can ignore this exception since it shouldn't
2482                 //-- happen. If _serializer is not null, it means
2483                 //-- we've already called this method sucessfully
2484                 //-- in the Marshaller() constructor
2485                 if (LOG.isDebugEnabled()) {
2486                     LOG.debug("Error setting encoding to " + encoding, iox);
2487                 }
2488             }
2489         }
2490         else {
2491             String error = "encoding cannot be set if you've passed in "+
2492             "your own DocumentHandler";
2493             throw new IllegalStateException(error);
2494         }
2495     } //-- setEncoding
2496 
2497     /**
2498      * Sets the value for the xsi:noNamespaceSchemaLocation attribute.
2499      * When set, this attribute will appear on the root element
2500      * of the marshalled document.
2501      *
2502      * @param schemaLocation the URI location of the schema
2503      * to which the marshalled document is an instance of.
2504     **/
2505     public void setNoNamespaceSchemaLocation(String schemaLocation) {
2506         if (schemaLocation == null) {
2507             //-- remove if necessary
2508             //-- to be added later.
2509         }
2510         else {
2511             _topLevelAtts.setAttribute(XSI_NO_NAMESPACE_SCHEMA_LOCATION,
2512                 schemaLocation, XSI_NAMESPACE);
2513         }
2514     } //-- setNoNamespaceSchemaLocation
2515 
2516     /**
2517      * Sets the value for the xsi:schemaLocation attribute.
2518      * When set, this attribute will appear on the root element
2519      * of the marshalled document.
2520      *
2521      * @param schemaLocation the URI location of the schema
2522      * to which the marshalled document is an instance of.
2523     **/
2524     public void setSchemaLocation(String schemaLocation) {
2525         if (schemaLocation == null) {
2526             //-- remove if necessary
2527             //-- to be added later.
2528         }
2529         else {
2530             _topLevelAtts.setAttribute(XSI_SCHEMA_LOCATION,
2531                 schemaLocation, XSI_NAMESPACE);
2532         }
2533     } //-- setSchemaLocation
2534 
2535     /**
2536      * Sets whether or not namespaces are output. By default
2537      * the Marshaller will output namespace declarations and
2538      * prefix elements and attributes with their respective
2539      * namespace prefix. This method can be used to prevent
2540      * the usage of namespaces.
2541      *
2542      * @param suppressNamespaces a boolean that when true
2543      * will prevent namespaces from being output.
2544      */
2545     public void setSuppressNamespaces(boolean suppressNamespaces) {
2546         _suppressNamespaces = suppressNamespaces;
2547     } //-- setSuppressNamespaces
2548 
2549     /**
2550      * Sets whether or not the xsi:type attribute should appear
2551      * on the marshalled document.
2552      *
2553      * @param suppressXSIType a boolean that when true will prevent
2554      * xsi:type attribute from being used in the marshalling process.
2555      */
2556      public void setSuppressXSIType(boolean suppressXSIType) {
2557         _suppressXSIType = suppressXSIType;
2558      } //-- setSuppressXSIType
2559 
2560      /**
2561       * Sets whether or not to output the xsi:type at the root
2562       * element. This is usually needed when the root element
2563       * type cannot be determined by the element name alone.
2564       * By default xsi:type will not be output on the root
2565       * element.
2566       *
2567       * @param useXSITypeAtRoot a boolean that when true indicates
2568       * that the xsi:type should be output on the root element.
2569       */
2570      public void setUseXSITypeAtRoot(boolean useXSITypeAtRoot) {
2571          _useXSITypeAtRoot = useXSITypeAtRoot;
2572      } //-- setUseXSITypeAtRoot
2573 
2574     /**
2575      * Finds and returns an XMLClassDescriptor for the given class. If
2576      * a XMLClassDescriptor could not be found, this method will attempt to
2577      * create one automatically using reflection.
2578      * @param cls the Class to get the XMLClassDescriptor for
2579      * @exception MarshalException when there is a problem
2580      * retrieving or creating the XMLClassDescriptor for the given class
2581     **/
2582     private XMLClassDescriptor getClassDescriptor(final Class<?> cls)
2583     throws MarshalException {
2584         XMLClassDescriptor classDesc = null;
2585 
2586         try {
2587             if (!isPrimitive(cls))
2588                 classDesc = (XMLClassDescriptor) getResolver().resolve(cls);
2589         }
2590         catch(ResolverException rx) {
2591             Throwable actual = rx.getCause();
2592             if (actual instanceof MarshalException) {
2593                 throw (MarshalException)actual;
2594             }
2595             if (actual != null) {
2596                 throw new MarshalException(actual);
2597             }
2598             throw new MarshalException(rx);
2599         }
2600 
2601         if (classDesc != null)
2602             classDesc = new InternalXMLClassDescriptor(classDesc);
2603 
2604         return classDesc;
2605     } //-- getClassDescriptor
2606 
2607     /**
2608      * Processes the attribute associated with the given attDescriptor and parent
2609      * object.
2610      *
2611      * @param atts the SAX attribute list to add the attribute to
2612      */
2613     private void processAttribute
2614         (Object object, XMLFieldDescriptor attDescriptor, AttributesImpl atts)
2615         throws MarshalException
2616     {
2617         if (attDescriptor == null) return;
2618 
2619         //-- process Namespace nodes from Object Model,
2620         //-- if necessary.
2621         if (attDescriptor.getNodeType() == NodeType.Namespace) {
2622             if (!_suppressNamespaces) {
2623                 Object map = attDescriptor.getHandler().getValue(object);
2624                 MapHandler mapHandler = MapHandlers.getHandler(map);
2625                 if (mapHandler != null) {
2626                     Enumeration<?> keys = mapHandler.keys(map);
2627                     while (keys.hasMoreElements()) {
2628                         Object key = keys.nextElement();
2629                         Object val = mapHandler.get(map, key);
2630                         declareNamespace(key.toString(), val.toString());
2631                     }
2632                 }
2633             }
2634             return;
2635         }
2636 
2637         String localName = attDescriptor.getXMLName();
2638         String qName     = localName;
2639 
2640         //-- handle attribute namespaces
2641         String namespace = "";
2642         if (!_suppressNamespaces) {
2643             namespace = attDescriptor.getNameSpaceURI();
2644             if (StringUtils.isNotEmpty(namespace)) {
2645                 String prefix = attDescriptor.getNameSpacePrefix();
2646                 if ((prefix == null) || (prefix.length() == 0))
2647                     prefix = namespacesStack.getNonDefaultNamespacePrefix(namespace);
2648 
2649                 if ((prefix == null) || (prefix.length() == 0)) {
2650                     //-- automatically create namespace prefix?
2651                     prefix = DEFAULT_PREFIX + (++_namespaceCounter);
2652                 }
2653                 declareNamespace(prefix, namespace);
2654                 qName = prefix + ':' + qName;
2655             }
2656             else namespace = "";
2657         }
2658 
2659         Object value = null;
2660 
2661         try {
2662             value = attDescriptor.getHandler().getValue(object);
2663         }
2664         catch(IllegalStateException ise) {
2665             LOG.warn("Error getting value from " + object, ise);
2666             return;
2667         }
2668 
2669         //-- handle IDREF(S)
2670         if (attDescriptor.isReference() && (value != null)) {
2671 
2672             if (attDescriptor.isMultivalued()) {
2673                 Enumeration<?> enumeration = null;
2674                 if (value instanceof Enumeration) {
2675                     enumeration = (Enumeration<?>)value;
2676                 }
2677                 else {
2678                     CollectionHandler<?> colHandler = null;
2679                     try {
2680                         colHandler = CollectionHandlers.getHandler(value.getClass());
2681                     }
2682                     catch(MappingException mx) {
2683                         throw new MarshalException(mx);
2684                     }
2685                     enumeration = colHandler.elements(value);
2686                 }
2687                 if (enumeration.hasMoreElements()) {
2688                     StringBuffer sb = new StringBuffer();
2689                     for (int v = 0; enumeration.hasMoreElements(); v++) {
2690                         if (v > 0) sb.append(' ');
2691                         sb.append(getObjectID(enumeration.nextElement()).toString());
2692                     }
2693                     value = sb;
2694                 }
2695                 else value = null;
2696             }
2697             else {
2698                 value = getObjectID(value);
2699             }
2700         }
2701         //-- handle multi-value attributes
2702         else if (attDescriptor.isMultivalued() && (value != null)) {
2703             value = processXSListType(value, attDescriptor);
2704         }
2705         else if (value != null) {
2706             //-- handle hex/base64 content
2707             Class<?> objType = value.getClass();
2708             if (objType.isArray() && (objType.getComponentType() == Byte.TYPE)) {
2709                 value = encodeBinaryData(value, attDescriptor.getSchemaType());
2710             }
2711         }
2712 
2713         if (value != null) {
2714             //check if the value is a QName that needs to
2715             //be resolved ({URI}value -> ns:value).
2716             String valueType = attDescriptor.getSchemaType();
2717             if ((valueType != null) && (valueType.equals(QNAME_NAME)))
2718                     value = resolveQName(value, attDescriptor);
2719 
2720             atts.addAttribute(namespace, localName, qName, CDATA, value.toString());
2721         }
2722     }
2723 
2724     private Object processXSListType(final Object value, XMLFieldDescriptor descriptor) 
2725     throws MarshalException {
2726         Object returnValue = null;
2727         Enumeration<?> enumeration = null;
2728         if (value instanceof Enumeration) {
2729             enumeration = (Enumeration<?>)value;
2730         }
2731         else {
2732             CollectionHandler<?> colHandler = null;
2733             try {
2734                 colHandler = CollectionHandlers.getHandler(value.getClass());
2735             }
2736             catch(MappingException mx) {
2737                 throw new MarshalException(mx);
2738             }
2739             enumeration = colHandler.elements(value);
2740         }
2741         if (enumeration.hasMoreElements()) {
2742             StringBuffer sb = new StringBuffer();
2743             for (int v = 0; enumeration.hasMoreElements(); v++) {
2744                 if (v > 0) {
2745                     sb.append(' ');
2746                 }
2747                 Object collectionValue = enumeration.nextElement();
2748                 //-- handle hex/base64 content
2749                 Class<?> objType = collectionValue.getClass();
2750                 if (objType.isArray() && (objType.getComponentType() == Byte.TYPE)) {
2751                     collectionValue = encodeBinaryData(collectionValue, descriptor.getComponentType());
2752                 }
2753                 
2754                 sb.append(collectionValue.toString());
2755             }
2756             returnValue = sb;
2757         }
2758         
2759         return returnValue;
2760     }
2761 
2762 
2763     /**
2764      * Encode binary data.
2765      * @param valueToEncode The binary data to encode.
2766      * @param componentType The XML schema component type.
2767      * @return Encoded binary data in {@link String} form.
2768      */
2769     private Object encodeBinaryData(final Object valueToEncode,
2770             final String componentType) {
2771         String encodedValue;
2772         if (HexDecoder.DATA_TYPE.equals(componentType)) {
2773             encodedValue = HexDecoder.encode((byte[]) valueToEncode);
2774         } else {
2775             encodedValue = new String(Base64Encoder.encode((byte[]) valueToEncode));
2776         }
2777         return encodedValue;
2778     }
2779 
2780 
2781     /**
2782      * Processes the attributes for container objects
2783      *
2784      * @param target the object currently being marshalled.
2785      * @param classDesc the XMLClassDescriptor for the target object
2786      * @param atts the SAX attributes list to add attributes to
2787      */
2788     private void processContainerAttributes
2789         (Object target, XMLClassDescriptor classDesc, AttributesImpl atts)
2790         throws MarshalException
2791     {
2792         if (classDesc instanceof XMLClassDescriptorImpl) {
2793             if (!((XMLClassDescriptorImpl)classDesc).hasContainerFields())
2794                 return;
2795         }
2796 
2797         XMLFieldDescriptor[] elemDescriptors = classDesc.getElementDescriptors();
2798         for (int i = 0; i < elemDescriptors.length; i++) {
2799             if (elemDescriptors[i] == null) continue;
2800             if (!elemDescriptors[i].isContainer()) continue;
2801             processContainerAttributes(target, elemDescriptors[i], atts);
2802         }
2803     } //-- processContainerAttributes
2804 
2805     /**
2806      * Processes the attributes for container objects.
2807      *
2808      * @param target the object currently being marshalled.
2809      * @param containerFieldDesc the XMLFieldDescriptor for the containter to process
2810      * @param atts the SAX attributes list to add any necessary attributes to.
2811      * @throws MarshalException If there's a problem marshalling the attribute(s).
2812      */
2813     private void processContainerAttributes
2814         (final Object target, final XMLFieldDescriptor containerFieldDesc, 
2815                 final AttributesImpl atts)
2816         throws MarshalException {
2817         if (target.getClass().isArray()) {
2818              int length = Array.getLength(target);
2819              for (int j = 0; j < length; j++) {
2820                   Object item = Array.get(target, j);
2821                   if (item != null) {
2822                     processContainerAttributes(item, containerFieldDesc, atts);
2823                 }
2824              }
2825              return;
2826         } else if (target instanceof Enumeration) {
2827             Enumeration<?> enumeration = (Enumeration<?>) target;
2828             while (enumeration.hasMoreElements()) {
2829                 Object item = enumeration.nextElement();
2830                 if (item != null) {
2831                     processContainerAttributes(item, containerFieldDesc, atts);
2832                 }
2833             }
2834             return;
2835         }
2836 
2837         Object containerObject = containerFieldDesc.getHandler().getValue(target);
2838 
2839         if (containerObject == null) {
2840             return;
2841         }
2842 
2843         XMLClassDescriptor containerClassDesc
2844             = (XMLClassDescriptor) containerFieldDesc.getClassDescriptor();
2845 
2846         if (containerClassDesc == null) {
2847             containerClassDesc = getClassDescriptor(containerFieldDesc.getFieldType());
2848             if (containerClassDesc == null) {
2849                 return;
2850             }
2851         }
2852 
2853         // Look for attributes
2854         XMLFieldDescriptor[] attrDescriptors = containerClassDesc.getAttributeDescriptors();
2855         for (int idx = 0; idx < attrDescriptors.length; idx++) {
2856             if (attrDescriptors[idx] == null) {
2857                 continue;
2858             }
2859             if (attrDescriptors[idx].getLocationPath() == null 
2860                     || attrDescriptors[idx].getLocationPath().length() == 0) {
2861                 processAttribute(containerObject, attrDescriptors[idx], atts);
2862             }
2863         }
2864 
2865         // recursively process containers
2866         processContainerAttributes(containerObject, containerClassDesc, atts);
2867     } //-- processContainerAttributes
2868 
2869     /**
2870      * Resolve a QName value ({URI}value) by declaring a namespace after
2871      * having retrieved the prefix.
2872      */
2873     private Object resolveQName(Object value, XMLFieldDescriptor fieldDesc) {
2874         if ( (value == null) || !(value instanceof String))
2875             return value;
2876         if (!(fieldDesc instanceof XMLFieldDescriptorImpl))
2877            return value;
2878 
2879         String result = (String)value;
2880 
2881         String nsURI = null;
2882         int idx = -1;
2883         if ((result.length() > 0) && (result.charAt(0) == '{')) {
2884             idx = result.indexOf('}');
2885             if (idx <= 0) {
2886                 String err = "Bad QName value :'"+result+"', it should follow the pattern '{URI}value'";
2887                 throw new IllegalArgumentException(err);
2888             }
2889             nsURI = result.substring(1, idx);
2890         }
2891         else return value;
2892 
2893         String prefix = ((XMLFieldDescriptorImpl)fieldDesc).getQNamePrefix();
2894         //no prefix provided, check if one has been previously defined
2895         if (prefix == null)
2896             prefix = namespacesStack.getNamespacePrefix(nsURI);
2897         //if still no prefix, use a naming algorithm (ns+counter).
2898         if (prefix == null)
2899             prefix = DEFAULT_PREFIX+(++_namespaceCounter);
2900         result = (prefix.length() != 0)?prefix+":"+result.substring(idx+1):result.substring(idx+1);
2901         declareNamespace(prefix, nsURI);
2902         return result;
2903     }
2904 
2905    private void validate(final Object object) throws ValidationException {
2906         if  (_validate) {
2907             //-- we must have a valid element before marshalling
2908             Validator validator = new Validator();
2909             ValidationContext context = new ValidationContext();
2910             context.setInternalContext(getInternalContext());
2911 //            context.setConfiguration(_config);
2912 //            context.setResolver(_cdResolver);
2913             validator.validate(object, context);
2914         }
2915     }
2916 
2917     /**
2918      * Returns the value of the given Castor XML-specific property.
2919      * @param name Qualified name of the CASTOR XML-specific property.
2920      * @return The current value of the given property.
2921      * @since 1.1.2
2922     */
2923     public String getProperty(final String name) {
2924         return getInternalContext().getStringProperty(name);
2925     }
2926 
2927     /**
2928      * Sets a custom value of a given Castor XML-specific property.
2929      * @param name Name of the Castor XML property 
2930      * @param value Custom value to set.
2931      * @since 1.1.2
2932      */
2933     public void setProperty(final String name, final String value) {
2934         getInternalContext().setProperty(name, value);
2935         deriveProperties();
2936     }
2937     
2938     /**
2939      * Inner-class used for handling wrapper elements
2940      * and locations.
2941      */
2942     static class WrapperInfo {
2943         private String _localName = null;
2944         private String _qName = null;
2945         private String _location = null;
2946 
2947         WrapperInfo(final String localName, final String qName, final String location) {
2948             _localName = localName;
2949             _qName = qName;
2950             _location = location;
2951         }
2952     }
2953 
2954 
2955     static class MarshalState {
2956         private String _xpath = null;
2957         private XMLFieldDescriptor[] _nestedAtts = null;
2958         private int _nestedAttCount = 0;
2959         private MarshalState _parent = null;
2960         private Object _owner        = null;
2961         private String _xmlName      = null;
2962 
2963         MarshalState(Object owner, String xmlName) {
2964             checkNotNull(owner, "The argument 'owner' must not be null.");
2965             checkNotNull(xmlName, "The argument 'xmlName' must not be null.");
2966 
2967             _owner = owner;
2968             _xmlName = xmlName;
2969         }
2970 
2971 
2972         MarshalState createMarshalState(Object owner, String xmlName) {
2973             MarshalState ms = new MarshalState(owner, xmlName);
2974             ms._parent = this;
2975             return ms;
2976         }
2977 
2978         String getXPath() {
2979             if (_xpath == null) {
2980                 if (_parent != null) {
2981                     _xpath = _parent.getXPath() + "/" + _xmlName;
2982                 }
2983                 else {
2984                     _xpath = _xmlName;
2985                 }
2986             }
2987             return _xpath;
2988         }
2989 
2990         Object getOwner() {
2991             return _owner;
2992         }
2993 
2994         MarshalState getParent() {
2995             return _parent;
2996         }
2997     }
2998 
2999     /**
3000      * A wrapper for a "Nil" object
3001      *
3002      */
3003     public static class NilObject {
3004 
3005         private XMLClassDescriptor _classDesc = null;
3006         private XMLFieldDescriptor _fieldDesc = null;
3007 
3008         NilObject(XMLClassDescriptor classDesc, XMLFieldDescriptor fieldDesc) {
3009             _classDesc = classDesc;
3010             _fieldDesc = fieldDesc;
3011         }
3012 
3013         /**
3014          * Returns the associated XMLClassDescriptor
3015          *
3016          * @return the XMLClassDescriptor
3017          */
3018         public XMLClassDescriptor getClassDescriptor() {
3019             return _classDesc;
3020         }
3021 
3022         /**
3023          * Returns the associated XMLFieldDescriptor
3024          *
3025          * @return the associated XMLFieldDescriptor
3026          */
3027         public XMLFieldDescriptor getFieldDescriptor() {
3028             return _fieldDesc;
3029         }
3030     }
3031 
3032     /**
3033      * To set the SAX {@link ContentHandler} which is used as destination at marshalling.
3034      * @param contentHandler the SAX {@link ContentHandler} to use as destination at marshalling
3035      */
3036     public void setContentHandler(final ContentHandler contentHandler) {
3037         _handler = contentHandler;
3038     }
3039 
3040     /**
3041      * Assigns the document handler, ignoring any possible exception.
3042      */
3043     private void setDocumentHandler() {
3044         try {
3045             //-- Due to a Xerces Serializer bug that doesn't allow declaring
3046             //-- multiple prefixes to the same namespace, we use the old
3047             //-- DocumentHandler format and process namespaces ourselves
3048             _handler = new DocumentHandlerAdapter(_serializer.asDocumentHandler());
3049         }
3050         catch (IOException iox) {
3051             //-- we can ignore this exception since it shouldn't
3052             //-- happen. If _serializer is not null, it means
3053             //-- we've already called this method successfully
3054             //-- in the Marshaller() constructor
3055             if (LOG.isDebugEnabled()) {
3056                 LOG.debug("Error setting up document handler", iox);
3057             }
3058         }
3059     }
3060 
3061     /**
3062      * Checks if passed parameter is not null and not a empty string. In case it is, a
3063      * {@link IllegalArgumentException} is thrown.
3064      *
3065      * @param param the parameter to check
3066      * @param msg the error message to use for thrown exception
3067      *
3068      * @throws IllegalArgumentException if param is null
3069      */
3070     private static void checkNotEmpty(String param, String msg) {
3071         checkNotNull(param, msg);
3072 
3073         if (param.length() == 0) {
3074             throw new IllegalArgumentException(msg);
3075         }
3076     }
3077 
3078     /**
3079      * Checks if passed parameter is not null. In case it is, a {@link IllegalArgumentException} is thrown.
3080      *
3081      * @param param the parameter to check
3082      * @param msg the error message to use for thrown exception
3083      *
3084      * @throws IllegalArgumentException if param is null
3085      */
3086     private static void checkNotNull(Object param, String msg) {
3087 
3088         if (param  == null) {
3089             throw new IllegalArgumentException(msg);
3090         }
3091     }
3092 } //-- Marshaller
3093 
3094