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.util;
51  
52  import java.lang.reflect.Array;
53  import java.util.ArrayList;
54  import java.util.Collection;
55  import java.util.HashMap;
56  import java.util.HashSet;
57  import java.util.LinkedList;
58  import java.util.List;
59  import java.util.Map;
60  import java.util.Set;
61  
62  import org.exolab.castor.mapping.AbstractFieldHandler;
63  import org.exolab.castor.mapping.AccessMode;
64  import org.exolab.castor.mapping.ClassDescriptor;
65  import org.exolab.castor.mapping.FieldDescriptor;
66  import org.exolab.castor.mapping.FieldHandler;
67  import org.exolab.castor.xml.FieldValidator;
68  import org.exolab.castor.xml.NodeType;
69  import org.exolab.castor.xml.TypeValidator;
70  import org.exolab.castor.xml.UnmarshalState;
71  import org.exolab.castor.xml.ValidationContext;
72  import org.exolab.castor.xml.ValidationException;
73  import org.exolab.castor.xml.Validator;
74  import org.exolab.castor.xml.XMLClassDescriptor;
75  import org.exolab.castor.xml.XMLFieldDescriptor;
76  import org.exolab.castor.xml.location.XPathLocation;
77  import org.exolab.castor.xml.util.resolvers.ResolveHelpers;
78  
79  /**
80   * The core implementation of XMLClassDescriptor. This class is used by both
81   * generated source code as well as the XMLMappingLoader.
82   *
83   * @author <a href="keith AT kvisco DOT com">Keith Visco</a>
84   * @version $Revision$ $Date: 2006-04-13 06:47:36 -0600 (Thu, 13 Apr 2006) $
85   */
86  public class XMLClassDescriptorImpl extends Validator implements XMLClassDescriptor {
87      /** The ALL compositor to signal the fields of the described class must all
88       *  be present and valid, if they are required. */
89      private static final short ALL = 0;
90  
91      /** The CHOICE compositor to signal the fields of the described class must be
92       *  only a choice. They are mutually exclusive. */
93      private static final short CHOICE = 1;
94  
95      /** The SEQUENCE compositor....currently is the same as ALL. */
96      private static final short SEQUENCE = 2;
97  
98  
99      private static final String NULL_CLASS_ERR = 
100         "The Class passed as an argument to the constructor of "
101         + "XMLClassDescriptorImpl may not be null.";
102 
103     private static final String WILDCARD = "*";
104 
105     /** The set of attribute descriptors. */
106     private XMLFieldDescriptors _attributes = null;
107 
108     /** Cached attribute descriptors for improved performance. */
109     private XMLFieldDescriptor[] _attArray = null;
110 
111     /** The Class that this ClassDescriptor describes. */
112     private Class<?> _class = null;
113 
114     /** A variable to keep track of the number of container fields. */
115     private int _containerCount = 0;
116 
117     /** The XMLFieldDescriptor for text data. */
118     private XMLFieldDescriptor _contentDescriptor = null;
119 
120     /** The TypeValidator to use for validation of the described class. */
121     private TypeValidator _validator = null;
122 
123     /** The set of element descriptors. */
124     private XMLFieldDescriptors _elements = null;
125 
126     /** Cached element descriptors for improved performance. */
127     private XMLFieldDescriptor[] _elemArray = null;
128 
129     /** The namespace prefix that is to be used when marshalling. */
130     private String _nsPrefix = null;
131 
132     /** The namespace URI used for both Marshalling and Unmarshalling. */
133     private String _nsURI = null;
134 
135     /** The name of the XML element. */
136     private String  _xmlName;
137 
138     /** Flag to indicate XML refers to global element or element with
139      *  anonymous type in XSD. Useful information for frameworks
140      *  that use Castor for XML binding and generate additional schema
141      *  definition elements. */
142     private boolean _elementDefinition = false;
143 
144     /** The descriptor of the class which this class extends,
145      *  or null if this is a top-level class. */
146     private XMLClassDescriptor _extends;
147 
148     /** The field of the identity for this class. */
149     private FieldDescriptor _identity;
150 
151     /** The access mode specified for this class. */
152     private AccessMode _accessMode;
153 
154     /** A flag to indicate that this XMLClassDescriptor was
155      *  created via introspection. */
156     private boolean _introspected = false;
157 
158     private short _compositor = ALL;
159     
160     /** Defines the sequence of elements for unmarshalling validation
161      *  (to be used with compositor == SEQUENCE only). */
162     private List<XMLFieldDescriptor> _sequenceOfElements = new ArrayList<XMLFieldDescriptor>();
163     
164     private List<String> _substitutes = new LinkedList<String>();    
165 
166     /** Map holding the properties set and read by Natures. */
167     private Map<String, Object> _properties = new HashMap<String, Object>();
168     
169     /** Map holding the available natures. */
170     private Set<String> _natures = new HashSet<String>();
171 
172     /**
173      * Creates an XMLClassDescriptor class used by the Marshalling Framework.
174      *
175      * @param type the Class type with which this ClassDescriptor describes.
176      */
177     public XMLClassDescriptorImpl(final Class<?> type) {
178         this();
179         if (type == null) {
180             throw new IllegalArgumentException(NULL_CLASS_ERR);
181         }
182 
183         _class = type;
184         // useless, no name is known setXMLName(null);
185     }
186 
187     /**
188      * Creates an XMLClassDescriptor class used by the Marshalling Framework.
189      *
190      * @param type the Class type with which this ClassDescriptor describes.
191      */
192     public XMLClassDescriptorImpl(Class<?> type, String xmlName) {
193         this();
194 
195         if (type == null) {
196             throw new IllegalArgumentException(NULL_CLASS_ERR);
197         }
198 
199         this._class = type;
200         setXMLName(xmlName);
201     } //-- XMLClassDescriptorImpl
202 
203     /**
204      * Protected constructor used by this class, and subclasses only
205      */
206     protected XMLClassDescriptorImpl() {
207         _attributes = new XMLFieldDescriptors(5);
208         _elements = new XMLFieldDescriptors(7);
209     } //-- XMLClassDescriptor
210 
211     //------------------/
212     //- Public Methods -/
213     //------------------/
214 
215     /**
216      * Adds the given XMLFieldDescriptor to the list of descriptors. The
217      * descriptor will be added to the appropriate list by calling
218      * XMLFieldDescriptor#getNodeType() to determine it's type.
219      *
220      * @param descriptor the XMLFieldDescriptor to add
221      */
222     public void addFieldDescriptor(XMLFieldDescriptor descriptor) {
223         addFieldDescriptor(descriptor, true);
224     } //-- addFieldDescriptor
225 
226     /**
227      * Returns true if the given XMLFieldDescriptor is contained
228      * within this XMLClassDescriptor.
229      *
230      * @return true if the XMLFieldDescriptor is part of this
231      * XMLClassDescriptor, otherwise false.
232      */
233     public boolean contains(XMLFieldDescriptor descriptor) {
234         if (descriptor == null) {
235             return false;
236         }
237 
238         if (_attributes.contains(descriptor)) {
239             return true;
240         }
241         if (_elements.contains(descriptor)) {
242             return true;
243         }
244 
245         return descriptor.equals(_contentDescriptor);
246     } //-- contains
247 
248     /**
249      * Returns the set of XMLFieldDescriptors for all members that should be
250      * marshalled as XML attributes.
251      *
252      * @return an array of XMLFieldDescriptors for all members that should be
253      *         marshalled as XML attributes.
254      */
255     public XMLFieldDescriptor[]  getAttributeDescriptors() {
256         return getAttributeArray().clone();
257     } // getAttributeDescriptors
258 
259     /**
260      * Returns the XMLFieldDescriptor for the member that should be marshalled
261      * as text content.
262      *
263      * @return the XMLFieldDescriptor for the member that should be marshalled
264      *         as text content.
265      */
266     public XMLFieldDescriptor getContentDescriptor() {
267         return _contentDescriptor;
268     } // getContentDescriptor
269 
270     /**
271      * Returns the set of XMLFieldDescriptors for all members
272      * that should be marshalled as XML elements.
273      *
274      * @return an array of XMLFieldDescriptors for all members
275      * that should be marshalled as XML elements.
276      */
277     public XMLFieldDescriptor[]  getElementDescriptors() {
278         return getElementArray().clone();
279     } // getElementDescriptors
280     
281     /**
282      * Checks whether the given XMLFieldDescriptor is the one actually expected,
283      * given the natural order as defined by a sequence definition
284      * @param elementDescriptor The XML field descriptor to be checked
285      * @throws ValidationException If the descriptor is not the one expected
286      */
287     public void checkDescriptorForCorrectOrderWithinSequence(
288             final XMLFieldDescriptor elementDescriptor, UnmarshalState parentState, String xmlName) throws ValidationException {
289         if (_compositor == SEQUENCE && _sequenceOfElements.size() > 0) {
290         	
291         	if (parentState.getExpectedIndex() == _sequenceOfElements.size() ) {
292         		throw new ValidationException ("Element with name " + xmlName + " passed to type " + getXMLName() + " in incorrect order; It is not allowed to be the last element of this sequence!");
293         	}
294         	
295         	XMLFieldDescriptor expectedElementDescriptor = _sequenceOfElements.get(parentState.getExpectedIndex());
296             
297             String expectedElementName = expectedElementDescriptor.getXMLName();
298             String elementName = xmlName;
299 
300             boolean anyNode = expectedElementDescriptor.getFieldName().equals("_anyObject") && expectedElementName == null;
301             
302             // choices
303             if (!anyNode && expectedElementDescriptor.getXMLName().equals("-error-if-this-is-used-")) {
304             	
305             	// find possible names
306             	List<String> possibleNames = new ArrayList<String>();
307             	fillPossibleNames(possibleNames, expectedElementDescriptor);
308             	
309             	// check name
310             	if (!possibleNames.contains(elementName)) {
311             		if (!expectedElementDescriptor.isRequired()) {
312             			 parentState.setExpectedIndex(parentState.getExpectedIndex() + 1);
313                          checkDescriptorForCorrectOrderWithinSequence(elementDescriptor, parentState, xmlName);
314             		} else {
315             			throw new ValidationException ("Element with name " + elementName + " passed to type " + getXMLName() + " in incorrect order; expected element has to be member of the expected choice.");
316             		}
317             	} else {
318             		parentState.setExpectedIndex(parentState.getExpectedIndex() + 1);
319             	}
320                 return;
321             }
322 
323             // multi valued flag
324             if (expectedElementDescriptor.isMultivalued() && !parentState.isWithinMultivaluedElement()) {
325             	parentState.setWithinMultivaluedElement(true);
326             }
327             
328             if (!anyNode && !(expectedElementName).equals(elementName)) {
329                 
330                 // handle substitution groups !!! 
331                 List<String> substitutes =  expectedElementDescriptor.getSubstitutes();
332                 if (substitutes != null && !substitutes.isEmpty()) {
333                     if (substitutes.contains(elementName)) {
334                         if (!parentState.isWithinMultivaluedElement()) {
335                             parentState.setExpectedIndex(parentState.getExpectedIndex() + 1);
336                         }
337                         return;
338                     }
339                 }
340                 // handle multi-valued fields
341                 if (expectedElementDescriptor.isMultivalued()) {
342                 	parentState.setWithinMultivaluedElement(false);
343                     parentState.setExpectedIndex(parentState.getExpectedIndex() + 1);
344                     checkDescriptorForCorrectOrderWithinSequence(elementDescriptor, parentState, xmlName);
345                     return;
346                 }
347                 // handle required fields
348                 if (expectedElementDescriptor.isRequired()) {
349                     throw new ValidationException ("Element with name " + elementName + " passed to type " + getXMLName() + " in incorrect order; expected element with name '" + expectedElementName + "' or any other optional element declared prior to it.");
350                 }
351                 
352                 // non required field, proceed until next required field
353                 parentState.setExpectedIndex(parentState.getExpectedIndex() + 1);
354                 checkDescriptorForCorrectOrderWithinSequence(elementDescriptor, parentState, xmlName);
355                 return;
356                 
357             }
358             
359             if (!parentState.isWithinMultivaluedElement()) {
360                 parentState.setExpectedIndex(parentState.getExpectedIndex() + 1);
361             }
362         }
363    }
364     
365     
366     private void fillPossibleNames(List<String> possibleNames, XMLFieldDescriptor descriptor) {
367     	XMLFieldDescriptor[] descriptors = ((XMLClassDescriptor)descriptor.getClassDescriptor()).getElementDescriptors();
368     	if (descriptors.length == 0) {
369     		return;
370     	}
371     	for (XMLFieldDescriptor fieldDescriptor : descriptors) {
372     	    if ("_items".equals(fieldDescriptor.getFieldName() ) 
373     	            || "-error-if-this-is-used-".equals(fieldDescriptor.getXMLName())) {
374     	        fillPossibleNames(possibleNames, fieldDescriptor);
375     	    } else {
376     	        possibleNames.add(fieldDescriptor.getXMLName());
377     	    }
378         }
379     }
380 
381     /**
382      * Returns the XML field descriptor matching the given xml name and
383      * nodeType. If NodeType is null, then either an AttributeDescriptor, or
384      * ElementDescriptor may be returned. Null is returned if no matching
385      * descriptor is available.
386      * <p>
387      * If an field is matched in one of the container field, it will return the
388      * container field that contain the field named 'name'
389      *
390      * @param name the xml name to match against
391      * @param nodeType the NodeType to match against, or null if the node type
392      *        is not known.
393      * @return the matching descriptor, or null if no matching descriptor is
394      *         available.
395      */
396     public XMLFieldDescriptor getFieldDescriptor(String name, String namespace,
397             NodeType nodeType) {
398         boolean wild = ((nodeType == null) || _introspected);
399         XMLFieldDescriptor result = null;
400 
401         XMLFieldDescriptor[] attributes = _attArray;
402         XMLFieldDescriptor[] elements   = _elemArray;
403 
404         // TODO: clean up location patch
405         String location = null;
406         if (name != null) {
407             int idx = name.lastIndexOf('/');
408             if (idx >= 0) {
409                 location = name.substring(0, idx);
410                 name = name.substring(idx+1);
411             }
412         }
413 
414         if (wild || (nodeType == NodeType.Element)) {
415 
416             if (elements == null) elements = getElementArray();
417 
418 //            if (_compositor == SEQUENCE && sequenceOfElements.size() > 0) {
419 //                XMLFieldDescriptor elementDescriptor = (XMLFieldDescriptor) sequenceOfElements.get(sequenceElementCount);
420 //                String elementName = elementDescriptor.getXMLName();
421 //                if (!elementName.equals(name)) {
422 //                    throw new IllegalArgumentException ("Element with name " + name + " passed to type " + getXMLName() + " in incorrect order; expected TODO.");
423 //                } else {
424 //                    sequenceElementCount++;
425 //                }
426 //            }
427             
428             for (int i = 0; i < elements.length; i++) {
429                 XMLFieldDescriptor desc = elements[i];
430                 
431                 if (desc == null) continue;
432 
433                 if (location != null) {
434                     if (!location.equals(desc.getLocationPath()))
435                         continue;
436                 }
437 
438                 if (desc.matches(name)) {
439                       if (!desc.matches(WILDCARD)) {
440                           // currently, a 'null' namespace value indicates that the XML artifact in question
441                           // either is namespace-less or is part of the default namespace; in both cases, 
442                           // we currently can not perform namespace comparison.
443                           // TODO[wguttmn, 20071205]: add code to handle default namespace declarations rather than checking for null
444                           if (namespace == null 
445                                   || ResolveHelpers.namespaceEquals(namespace, desc.getNameSpaceURI())) {
446                               return desc;
447                           }
448                       } else {
449                           //--  possible wildcard match check for
450                           //--  exact match (we need to extend the
451                           //--  the descriptor interface to handle this
452                           if (name.equals(desc.getXMLName()))
453                               return desc;
454                           //-- assume wild-card match
455                           result = desc;
456                       }
457                 }
458 
459                 //handle container
460                 if ( desc.isContainer() ) {
461                     XMLClassDescriptor xcd = (XMLClassDescriptor)desc.getClassDescriptor();
462                     //prevent endless loop
463                     if (xcd != this) {
464                         //is it in this class descriptor?
465                         if (xcd.getFieldDescriptor(name, namespace, NodeType.Element) != null) {
466                             result = desc;
467                             break;
468                         }
469                     }
470                 }//container
471             }
472 
473             if (result != null)
474                 return result;
475         }
476 
477         //-- handle attributes
478         if (wild || (nodeType == NodeType.Attribute))
479         {
480             if (attributes == null) attributes = getAttributeArray();
481             for (int i = 0; i < attributes.length; i++) {
482                 XMLFieldDescriptor desc = attributes[i];
483                 if (desc == null) continue;
484                 if (desc.matches(name)) {
485                     return desc;
486                 }
487             }
488         }
489 
490         //-- handle namespace node
491         if (nodeType == NodeType.Namespace) {
492             if (attributes == null) attributes = getAttributeArray();
493             for (int i = 0; i < attributes.length; i++) {
494                 if (attributes[i] == null) continue;
495                 if (attributes[i].getNodeType() == NodeType.Namespace) {
496                     return attributes[i];
497                 }
498             }
499         }
500 
501         // To handle container object, we need to check if an attribute of a
502         // container field match this attribute
503         if (nodeType == NodeType.Attribute) {
504             if (elements == null) elements = getElementArray();
505             for (int i = 0; i < elements.length; i++) {
506                 XMLFieldDescriptor desc = elements[i];
507                 if (desc.isContainer()) {
508                     XMLClassDescriptor xcd = (XMLClassDescriptor)desc.getClassDescriptor();
509                     //prevent endless loop
510                     if (xcd != this) {
511                         //is it in this class descriptor?
512                         XMLFieldDescriptor temp = xcd.getFieldDescriptor(name, namespace, NodeType.Attribute);
513                         if (temp != null) {
514                             return desc;
515                         }
516                     }
517                 }
518             }
519         }
520 
521         return null;
522 
523     } //-- getFieldDescriptor
524 
525 
526     /**
527      * @return the namespace prefix to use when marshalling as XML.
528      */
529     public String getNameSpacePrefix() {
530         return _nsPrefix;
531     } //-- getNameSpacePrefix
532 
533     /**
534      * @return the namespace URI used when marshalling and unmarshalling as XML.
535      */
536     public String getNameSpaceURI() {
537         return _nsURI;
538     } //-- getNameSpaceURI
539 
540 
541     /**
542      * Returns a specific validator for the class described by
543      * this ClassDescriptor. A null value may be returned
544      * if no specific validator exists.
545      *
546      * @return the type validator for the class described by this
547      * ClassDescriptor.
548      */
549     public TypeValidator getValidator() {
550         if (_validator != null)
551             return _validator;
552         return this;
553     } //-- getValidator
554 
555     /**
556      * Returns the XML Name for the Class being described.
557      *
558      * @return the XML name.
559      */
560     public String getXMLName() {
561         return _xmlName;
562     } //-- getXMLName
563 
564     /**
565      * Returns true if XML schema definition of this Class is that of a global
566      * element or element with anonymous type definition.
567      */
568     public boolean isElementDefinition() {
569             return _elementDefinition;
570     } //-- isElementDefinition
571 
572     /**
573      * Returns true if this XMLClassDescriptorImpl has any fields which are
574      * container objects. A container object is a Java object which holds
575      * data the should be marshalled, but the object itself should not be.
576      * So the container object will be "unwrapped" and the fields
577      * associated with the container will appear as if they were part
578      * of this class.
579      *
580      * @return true if any of the fields are container fields, otherwise false.
581      */
582     public boolean hasContainerFields() {
583         return (_containerCount > 0);
584     } //-- hasContainerFields
585 
586     /**
587      * Removes the given XMLFieldDescriptor from the list of descriptors.
588      *
589      * @param descriptor the XMLFieldDescriptor to remove
590      * @return true if the descriptor was removed.
591      */
592     public boolean removeFieldDescriptor(XMLFieldDescriptor descriptor) {
593 
594         if (descriptor == null) return false;
595 
596         boolean removed = false;
597         NodeType nodeType = descriptor.getNodeType();
598         switch(nodeType.getType()) {
599             case NodeType.NAMESPACE:
600             case NodeType.ATTRIBUTE:
601                 if (_attributes.remove(descriptor)) {
602                     removed = true;
603                     _attArray = null;
604                 }
605                 break;
606             case NodeType.TEXT:
607                 if (_contentDescriptor == descriptor) {
608                     _contentDescriptor = null;
609                     removed = true;
610                 }
611                 break;
612             default:
613                 if (_elements.remove(descriptor)) {
614                     _elemArray = null;
615                     removed = true;
616                     if (descriptor.isContainer()) --_containerCount;
617                 }
618                 break;
619         }
620         return removed;
621     } //-- removeFieldDescriptor
622 
623     /**
624      * Sets the compositor for the fields of the described
625      * class to be ALL.
626      */
627     public void setCompositorAsAll() {
628         _compositor = ALL;
629     }  //-- setCompositorAsAll
630 
631     /**
632      * Sets the compositor for the fields of the described
633      * class to be CHOICE.
634      */
635     public void setCompositorAsChoice() {
636         _compositor = CHOICE;
637     }  //-- setCompositorAsChoice
638 
639     /**
640      * Sets the compositor for the fields of the described
641      * class to be a Sequence.
642      */
643     public void setCompositorAsSequence() {
644         _compositor = SEQUENCE;
645     }  //-- setCompositorAsSequence
646 
647     /**
648      * Sets the XMLClassDescriptor that this descriptor inherits from
649      * @param classDesc the XMLClassDescriptor that this descriptor
650      * extends
651      */
652     public void setExtends(XMLClassDescriptor classDesc) {
653 
654         FieldDescriptor[] fields = null;
655         //-- remove reference to previous extended descriptor
656         if (_extends != null) {
657             sortDescriptors();
658             fields = _extends.getFields();
659             for (int i = 0; i < fields.length; i++) {
660                 removeFieldDescriptor((XMLFieldDescriptor)fields[i]);
661             }
662         }
663 
664         this._extends = classDesc;
665 
666         //-- flatten out the hierarchy
667         if (_extends != null) {
668             fields = classDesc.getFields();
669             for (int i = 0; i < fields.length; i++) {
670                 addFieldDescriptor((XMLFieldDescriptor)fields[i], false);
671             }
672         }
673 
674     } //-- setExtends
675 
676     /**
677      * Sets the Identity FieldDescriptor, if the FieldDescriptor is
678      * not already a contained in this ClassDescriptor, it will be
679      * added
680      */
681     public void setIdentity(XMLFieldDescriptor fieldDesc) {
682         if (fieldDesc != null) {
683             if ( (! _attributes.contains(fieldDesc)) &&
684                 (! _elements.contains(fieldDesc))) {
685                 addFieldDescriptor(fieldDesc);
686             }
687         }
688         this._identity = fieldDesc;
689     } //-- setIdentity
690 
691     /**
692      * Sets the namespace prefix used when marshalling as XML.
693      *
694      * @param nsPrefix the namespace prefix used when marshalling
695      * the "described" object
696      */
697     public void setNameSpacePrefix(String nsPrefix) {
698         this._nsPrefix = nsPrefix;
699     } //-- setNameSpacePrefix
700 
701     /**
702      * Sets the namespace URI used when marshalling and unmarshalling as XML.
703      *
704      * @param nsURI the namespace URI used when marshalling and
705      * unmarshalling the "described" Object.
706      */
707     public void setNameSpaceURI(String nsURI) {
708         this._nsURI = nsURI;
709     } //-- setNameSpaceURI
710 
711     /**
712      * Sets the validator to use for the class described by this
713      * ClassDescriptor
714      *
715      * @param validator the validator to use when peforming validation
716      * of the described class. This may be null to signal default
717      * validation.
718      */
719     //public void setValidator(TypeValidator validator) {
720     //    this.validator = validator;
721     //} //-- setValidator
722 
723     /**
724      * Sets the XML name for the Class described by this XMLClassDescriptor
725      *
726      * @param xmlName the XML name for the Class described by this
727      * XMLClassDescriptor
728      */
729     public void setXMLName(String xmlName) {
730         if (xmlName == null) {
731             if (_xmlName == null && _class != null) {
732                 _xmlName = _class.getName();
733             }
734         }
735         else this._xmlName = xmlName;
736     } //-- setXMLName
737 
738     /**
739      * Set elementDefinition to true to indicate Class XML schema
740      * definition is a global element or element with anonymous type.
741      *
742      * @param elementDefinition flag to indicate XML definition is
743      * global element or element with anonymous type
744      */
745     public void setElementDefinition(boolean elementDefinition){
746             this._elementDefinition = elementDefinition;
747     } //-- setElementDefinition
748 
749     /**
750      * This method is used to keep the set of descriptors in the proper
751      * sorted lists. If you dynamically change the NodeType of
752      * an XMLFieldDescriptor after adding it the this ClassDescriptor,
753      * then call this method.
754      */
755     public void sortDescriptors() {
756 
757         //-- handle attributes
758         XMLFieldDescriptor[] descriptors = getAttributeArray();
759         for (int i = 0; i < descriptors.length; i++) {
760             XMLFieldDescriptor fieldDesc = descriptors[i];
761             switch (fieldDesc.getNodeType().getType()) {
762                 case NodeType.ELEMENT:
763                     _elements.add(fieldDesc);
764                     _attributes.remove(fieldDesc);
765                     _attArray = null;
766                     break;
767                 case NodeType.TEXT:
768                     _attributes.remove(fieldDesc);
769                     _attArray = null;
770                     break;
771                 default:
772                     break;
773             }
774         }
775 
776         //-- handle elements
777         descriptors = getElementArray();
778         for (int i = 0; i < descriptors.length; i++) {
779             XMLFieldDescriptor fieldDesc = descriptors[i];
780             switch (fieldDesc.getNodeType().getType()) {
781                 case NodeType.NAMESPACE:
782                 case NodeType.ATTRIBUTE:
783                     _attributes.add(fieldDesc);
784                     _elements.remove(fieldDesc);
785                     _elemArray = null;
786                     break;
787                 case NodeType.TEXT:
788                     _elements.remove(fieldDesc);
789                     _elemArray = null;
790                     break;
791                 default:
792                     break;
793             }
794         }
795 
796     } //-- sortDescriptors
797 
798     /**
799      * Returns the String representation of this XMLClassDescriptor
800      *
801      * @return the String representation of this XMLClassDescriptor
802      */
803     public String toString() {
804 
805         String str = super.toString() + "; descriptor for class: ";
806 
807         //-- add class name
808         if (_class != null)
809             str += _class.getName();
810         else
811             str += "[null]";
812 
813         //-- add xml name
814         str += "; xml name: " + _xmlName;
815 
816         return str;
817     } //-- toString
818 
819     /**
820      * Validates the given Object
821      *
822      * @param object the Object to validate
823      */
824     public void validate(Object object)
825         throws ValidationException
826     {
827         validate(object, (ValidationContext)null);
828     } //-- validate
829 
830     /**
831      * Validates the given object
832      * @param object the Object to validate
833      * @param context the ValidationContext
834      */
835     public void validate(Object object, ValidationContext context)
836     throws ValidationException {
837         if (object == null) {
838             throw new ValidationException("Cannot validate a null object.");
839         }
840         Class<?> a = getJavaClass();
841         ClassLoader acl = a.getClassLoader();
842         Class<? extends Object> b = object.getClass();
843         ClassLoader bcl = b.getClassLoader();
844         if (!getJavaClass().isAssignableFrom(object.getClass())) {
845             String err = "The given object is not an instance of the class"
846                 + " described by this ClassDecriptor.";
847             throw new ValidationException(err);
848         }
849 
850         //-- DEBUG
851         //System.out.println("Validating class: " + object.getClass().getName());
852         //-- /DEBUG
853 
854         XMLFieldDescriptor[] localElements = getElementArray();
855         XMLFieldDescriptor[] localAttributes = getAttributeArray();
856 
857         if (_extends != null) {
858 
859             //-- cascade call for validation
860             if (_extends instanceof XMLClassDescriptorImpl) {
861                 ((XMLClassDescriptorImpl) _extends).validate(object, context);
862             } else {
863                 TypeValidator baseValidator = _extends.getValidator();
864                 if (baseValidator != null) {
865                     baseValidator.validate(object, context);
866                 }
867             }
868 
869             //-- get local element descriptors by filtering out inherited ones
870             XMLFieldDescriptor[] inheritedElements   = _extends.getElementDescriptors();
871             XMLFieldDescriptor[] allElements = localElements;
872             localElements = new XMLFieldDescriptor[allElements.length - inheritedElements.length];
873             int localIdx = 0;
874             for (int i = 0; i < allElements.length; i++) {
875                 XMLFieldDescriptor desc = allElements[i];
876                 boolean isInherited = false;
877                 for (int idx = 0; idx < inheritedElements.length; idx++) {
878                     if (inheritedElements[idx].equals(desc)) {
879                         isInherited = true;
880                         break;
881                     }
882                 }
883                 if (!isInherited) {
884                     localElements[localIdx] = desc;
885                     ++localIdx;
886                 }
887             }
888 
889             //-- get local attribute descriptors by filtering out inherited ones
890             XMLFieldDescriptor[] inheritedAttributes = _extends.getAttributeDescriptors();
891             XMLFieldDescriptor[] allAttributes = localAttributes;
892             localAttributes = 
893                 new XMLFieldDescriptor[allAttributes.length - inheritedAttributes.length];
894             localIdx = 0;
895             for (int i = 0; i < allAttributes.length; i++) {
896                 XMLFieldDescriptor desc = allAttributes[i];
897                 boolean isInherited = false;
898                 for (int idx = 0; idx < inheritedAttributes.length; idx++) {
899                     if (inheritedAttributes[idx].equals(desc)) {
900                         isInherited = true;
901                         break;
902                     }
903                 }
904                 if (!isInherited) {
905                     localAttributes[localIdx] = desc;
906                     ++localIdx;
907                 }
908             }
909 
910         }
911 
912         switch (_compositor) {
913 
914             case CHOICE:
915 
916                 boolean found = false;
917                 boolean hasLocalDescs = (localElements.length > 0);
918                 XMLFieldDescriptor fieldDesc = null;
919                 //-- handle elements, affected by choice
920                 for (int i = 0; i < localElements.length; i++) {
921                     XMLFieldDescriptor desc = localElements[i];
922 
923                     if (desc == null) {
924                         continue;
925                     }
926 
927                     FieldHandler handler = desc.getHandler();
928 
929                     if (handler.getValue(object) != null) {
930                          //Special case if we have a Vector, an ArrayList
931                          //or an Array --> need to check if it is not empty
932                          if (desc.isMultivalued()) {
933                              Object temp = handler.getValue(object);
934                              //-- optimize this?
935                              if (Array.getLength(temp) == 0) {
936                                   temp = null;
937                                   continue;
938                              }
939                              temp = null;
940                          }
941 
942                         if (found) {
943                             String err = null;
944                             if (desc.isContainer()) {
945                                 err = "The group '" + desc.getFieldName();
946                                 err += "' cannot exist at the same time that ";
947                                 if (fieldDesc.isContainer()) {
948                                     err += "the group '" + fieldDesc.getFieldName();
949                                 } else {
950                                     err += "the element '" + fieldDesc.getXMLName();
951                                 }
952                                 err += "' also exists.";
953                             } else {
954                                  err = "The element '" + desc.getXMLName();
955                                  err += "' cannot exist at the same time that ";
956                                  err += "element '" + fieldDesc.getXMLName() + "' also exists.";
957                             }
958                             throw new ValidationException(err);
959                         }
960                         found = true;
961                         fieldDesc = desc;
962 
963                         FieldValidator fieldValidator = desc.getValidator();
964                         if (fieldValidator != null) {
965                             fieldValidator.validate(object, context);
966                         }
967                     }
968                 }
969 
970                 // if all elements are mandatory, print the grammar that the choice 
971                 // must match.
972                 if ((!found) && (hasLocalDescs))  {
973                     StringBuffer buffer = new StringBuffer(40);
974                     boolean existsOptionalElement = false;
975                     buffer.append('(');
976                     String sep = " | ";
977                     for (int i = 0; i < localElements.length; i++) {
978                         XMLFieldDescriptor  desc = localElements[i];
979                         if (desc == null) {
980                             continue;
981                         }
982 
983                         FieldValidator fieldValidator = desc.getValidator();
984                         if (fieldValidator.getMinOccurs() == 0) {
985                             existsOptionalElement = true;
986                             break;
987                         }
988                         buffer.append(sep);
989                         buffer.append(desc.getXMLName());
990                     }
991                     buffer.append(')');
992                     if (!existsOptionalElement) {
993                         String err = "In the choice contained in <" + this.getXMLName()
994                                      + ">, at least one of these elements must appear:\n"
995                                      + buffer.toString();
996                         throw new ValidationException(err);
997                     }
998                 }
999                 //-- handle attributes, not affected by choice
1000                 for (int i = 0; i < localAttributes.length; i++) {
1001                     validateField(object, context, localAttributes[i]);
1002                 }
1003                 //-- handle content, not affected by choice
1004                 if (_contentDescriptor != null) {
1005                     validateField(object, context, _contentDescriptor);
1006                 }
1007                 break;
1008             //-- Currently SEQUENCE is handled the same as all
1009             case SEQUENCE:
1010             //-- ALL
1011             default:
1012 
1013 
1014                 //-- handle elements
1015                 for (int i = 0; i < localElements.length; i++) {
1016                     final XMLFieldDescriptor fieldDescriptor = localElements[i];
1017                     if (fieldDescriptor == null) {
1018                         continue;
1019                     }
1020                     validateField(object, context, fieldDescriptor);
1021                 }
1022                 //-- handle attributes
1023                 // for (int i = 0; i < _attributes.size(); i++) {
1024                 for (int i = 0; i < localAttributes.length; i++) {
1025                     validateField(object, context, localAttributes[i]);
1026                 }
1027                 //-- handle content
1028                 if (_contentDescriptor != null) {
1029                     validateField(object, context, _contentDescriptor);
1030                 }
1031                 break;
1032         }
1033 
1034     } //-- validate
1035 
1036     /**
1037      * Validates agiven field of an object, as described by its {@link XMLFieldDescriptor}
1038      * instance.
1039      * @param object The parent object, whose field to validate.
1040      * @param context The current {@link ValidationContext} instance.
1041      * @param fieldDescriptor The {@link XMLFieldDescriptor} instance describing the 
1042      *          field to validate.
1043      * @throws ValidationException If validation did report a problem.
1044      */
1045     private void validateField(final Object object, final ValidationContext context,
1046             final XMLFieldDescriptor fieldDescriptor) throws ValidationException {
1047         FieldValidator fieldValidator = fieldDescriptor.getValidator();
1048         if (fieldValidator != null) {
1049             try {
1050                 fieldValidator.validate(object, context);
1051             } catch (ValidationException e) {
1052                 if (fieldDescriptor.getNodeType() == NodeType.Attribute 
1053                         || fieldDescriptor.getNodeType() == NodeType.Element) {
1054                     addLocationInformation(fieldDescriptor, e);
1055                 }
1056                 throw e;
1057             }
1058         }
1059     }
1060 
1061     /**
1062      * Adds location information to the {@link ValidationException} instance.
1063      * @param localElement The {@link XMLFieldDescriptor} instance whose has been responsible for
1064      * generating the error.
1065      * @param e The {@link ValidationException} to enrich.
1066      */
1067     private void addLocationInformation(final XMLFieldDescriptor localElement,
1068             final ValidationException e) {
1069         XPathLocation loc = (XPathLocation) e.getLocation();
1070         if (loc == null) {
1071             loc = new XPathLocation();
1072             e.setLocation(loc);
1073             if (localElement.getNodeType() == NodeType.Attribute) {
1074                 loc.addAttribute(localElement.getXMLName());
1075             } else {
1076                 loc.addChild(localElement.getXMLName());
1077             }
1078         }
1079     }
1080 
1081     //-------------------------------------/
1082     //- Implementation of ClassDescriptor -/
1083     //-------------------------------------/
1084 
1085     /**
1086      * Returns the Java class represented by this descriptor.
1087      *
1088      * @return The Java class
1089      */
1090     public Class<?> getJavaClass() {
1091         return _class;
1092     } //-- getJavaClass
1093 
1094 
1095     /**
1096      * Returns a list of fields represented by this descriptor.
1097      *
1098      * @return A list of fields
1099      */
1100     public FieldDescriptor[] getFields() {
1101         int size = _attributes.size();
1102         size += _elements.size();
1103         if (_contentDescriptor != null) ++size;
1104 
1105 
1106         XMLFieldDescriptor[] fields = new XMLFieldDescriptor[size];
1107 
1108         _attributes.toArray(fields);
1109         _elements.toArray(fields, _attributes.size());
1110 
1111         if (_contentDescriptor != null) {
1112             fields[size-1] = _contentDescriptor;
1113         }
1114         return fields;
1115     } //-- getFields
1116 
1117 
1118 
1119     /**
1120      * Returns the class descriptor of the class extended by this class.
1121      *
1122      * @return The extended class descriptor
1123      */
1124     public ClassDescriptor getExtends() {
1125         return _extends;
1126     } //-- getExtends
1127 
1128 
1129     /**
1130      * Returns the identity field, null if this class has no identity.
1131      *
1132      * @return The identity field
1133      */
1134     public FieldDescriptor getIdentity() {
1135         return _identity;
1136     } //-- getIdentity
1137 
1138 
1139     /**
1140      * Returns the access mode specified for this class.
1141      *
1142      * @return The access mode
1143      */
1144     public AccessMode getAccessMode() {
1145         return _accessMode;
1146     } //-- getAccessMode
1147 
1148     /**
1149      * @see org.exolab.castor.xml.XMLClassDescriptor#canAccept(java.lang.String,
1150      *      java.lang.String, java.lang.Object)
1151      */
1152     public boolean canAccept(String name, String namespace, Object object) {
1153 
1154         boolean result = false;
1155         boolean hasValue = false;
1156         XMLFieldDescriptor[] fields = null;
1157         int i = 0;
1158         //1--direct look up for a field
1159         XMLFieldDescriptor fieldDesc = this.getFieldDescriptor(name, namespace, NodeType.Element);
1160         if (fieldDesc == null)
1161            fieldDesc = this.getFieldDescriptor(name, namespace, NodeType.Attribute);
1162 
1163         //if the descriptor is still null, the field can't be in stored in this classDescriptor
1164         if (fieldDesc == null)
1165             return false;
1166 
1167         Object tempObject;
1168         //3--The descriptor is multivalued
1169         if (fieldDesc.isMultivalued()) {
1170             //-- check size
1171             FieldValidator validator = fieldDesc.getValidator();
1172             if (validator != null) {
1173                 if (validator.getMaxOccurs() < 0) {
1174                    result = true;
1175                 }
1176                  else {
1177                     // count current objects and add 1
1178                     tempObject = fieldDesc.getHandler().getValue(object);
1179                     int current = Array.getLength(tempObject);
1180                     int newTotal = current + 1;
1181                     result = (newTotal <= validator.getMaxOccurs());
1182                  }
1183             }
1184             else {
1185                 //-- not created by source generator...assume unbounded
1186                 result = true;
1187             }
1188         }
1189         //3-- the fieldDescriptor is a container
1190         else if (fieldDesc.isContainer()) {
1191             tempObject = fieldDesc.getHandler().getValue(object);
1192             //if the object is not yet instantiated, we return true
1193             if (tempObject == null)
1194                 result = true;
1195             else
1196                 result = ((XMLClassDescriptor)fieldDesc.getClassDescriptor()).canAccept(name, namespace, tempObject);
1197         }
1198         //4-- Check if the value is set or not
1199         else {
1200             FieldHandler handler = fieldDesc.getHandler();
1201 
1202             boolean checkPrimitiveValue = true;
1203             if (handler instanceof AbstractFieldHandler) {
1204                 hasValue = ((AbstractFieldHandler)handler).hasValue( object );
1205                 //-- poor man's check for a generated handler, since
1206                 //-- all generated handlers extend XMLFieldHandler, however
1207                 //-- this doesn't guarantee that that handler is indeed
1208                 //-- a generated handler, it does however mean that
1209                 //-- the handler definately didn't come from the
1210                 //-- MappingLoader.
1211                 //checkPrimitiveValue = (!(handler instanceof XMLFieldHandler));
1212             }
1213             else {
1214                 hasValue = (handler.getValue(object) != null);
1215             }
1216 
1217             //-- patch for primitives, we should check
1218             //-- for the has-method somehow...but this
1219             //-- is good enough for now. This may break
1220             //-- some non-Castor-generated code with
1221             //-- primitive values that have been set
1222             //-- with the same as the defaults
1223             if (hasValue &&
1224                 checkPrimitiveValue &&
1225                 fieldDesc.getFieldType().isPrimitive())
1226             {
1227                 if (isDefaultPrimitiveValue(handler.getValue( object ))) {
1228                     hasValue = false;
1229                 }
1230             }
1231             //-- end patch
1232             result = !hasValue;
1233         }
1234 
1235         //5--if there is no value and the _compositor is CHOICE
1236         //--we have to check to see if another value has not been set
1237         if (result && (_compositor == CHOICE)
1238             && (fieldDesc.getNodeType() == NodeType.Element) ) {
1239             fields = getElementArray();
1240             i = 0;
1241             while (result && i<fields.length) {
1242                 XMLFieldDescriptor desc = fields[i];
1243                 if (desc != fieldDesc && (object!=null) ) {
1244                     Object tempObj = desc.getHandler().getValue(object);
1245                     hasValue = (tempObj != null);
1246                     if (hasValue) {
1247                         result = false;
1248                         //special case for array
1249                         if (tempObj.getClass().isArray()) {
1250                             result =  Array.getLength(tempObj) == 0;
1251                         }
1252                         //special case for collection
1253                         if (tempObj instanceof Collection) {
1254                            result = ((Collection) tempObj).isEmpty();
1255                         }
1256                     }
1257                 }
1258                 i++;
1259             }//while
1260         }//CHOICE
1261         return result;
1262     }//--canAccept
1263 
1264     //---------------------/
1265     //- Protected Methods -/
1266     //---------------------/
1267 
1268 
1269     /**
1270      * Returns true if the given class should be treated as a primitive
1271      * type. This method will return true for all Java primitive
1272      * types, the set of primitive object wrappers, as well
1273      * as Strings.
1274      *
1275      * @return true if the given class should be treated as a primitive
1276      * type
1277     **/
1278     static boolean isPrimitive(Class<?> type) {
1279 
1280         if (type == null) return false;
1281 
1282         //-- java primitive
1283         if (type.isPrimitive()) return true;
1284 
1285         //-- primtive wrapper classes
1286         if ((type == Boolean.class) || (type == Character.class))
1287             return true;
1288 
1289         return (type.getSuperclass() == Number.class);
1290     } //-- isPrimitive
1291 
1292     /**
1293      * Checks to see if the given Object is a java primitive
1294      * (does not check for primitive wrappers) and has a
1295      * value that is equal to the default value for that
1296      * primitive. This method will return true if the value
1297      * is a java primitive with a default value.
1298      *
1299      * @return true if the value is a java primitive with
1300      * a default value
1301      */
1302     static boolean isDefaultPrimitiveValue(Object value) {
1303         if (value == null) return false;
1304 
1305         Class<?> type = value.getClass();
1306         if (type.isPrimitive()) {
1307             try {
1308                 return (value.equals(type.newInstance()));
1309             }
1310             catch(java.lang.IllegalAccessException iax) {
1311                 //-- Just return false, we should be
1312                 //-- able to instantiate primitive types
1313             }
1314             catch(java.lang.InstantiationException ix) {
1315                 //-- Just return false, we should be
1316                 //-- able to instantiate primitive types
1317             }
1318         }
1319         else if (type.getSuperclass() == Number.class) {
1320             return ((Number)value).intValue() == 0;
1321         }
1322         else if (type == Boolean.class) {
1323             return value.equals(Boolean.FALSE);
1324         }
1325         else if (type == Character.class) {
1326             return ((Character)value).charValue() == '\0';
1327         }
1328 
1329         return false;
1330     } //-- isDefaultPrimitiveValue
1331 
1332 
1333     /**
1334      * Sets the Class type being described by this descriptor.
1335      *
1336      * @param type the Class type being described
1337      */
1338     public void setJavaClass(Class<?> type) {
1339         this._class = type;
1340     } //-- setJavaClass
1341 
1342     protected void setExtendsWithoutFlatten(XMLClassDescriptor classDesc) {
1343         this._extends = classDesc;
1344     } //-- setExtendsWithoutFlatten
1345 
1346     /**
1347      * Sets a flag to indicate whether or not this XMLClassDescriptorImpl
1348      * was created via introspection
1349      *
1350      * @param introspected a boolean, when true indicated that this
1351      * XMLClassDescriptor was created via introspection
1352      */
1353     protected void setIntrospected(boolean introspected) {
1354         this._introspected = introspected;
1355     } //-- setIntrospected
1356 
1357 //    protected String toXMLName(String className) {
1358 //        //-- create default XML name
1359 //        String name = className;
1360 //        int idx = name.lastIndexOf('.');
1361 //        if (idx >= 0) name = name.substring(idx+1);
1362 //        return _naming.toXMLName(name);
1363 //    }
1364 
1365     //-------------------/
1366     //- Private Methods -/
1367     //-------------------/
1368 
1369     /**
1370      * Adds the given XMLFieldDescriptor to the list of descriptors. The
1371      * descriptor will be added to the appropriate list by calling
1372      * XMLFieldDescriptor#getNodeType() to determine it's type.
1373      *
1374      * @param descriptor the XMLFieldDescriptor to add
1375      */
1376     private void addFieldDescriptor(XMLFieldDescriptor descriptor, boolean relink) {
1377 
1378         if (descriptor == null) return;
1379 
1380         boolean added = false;
1381 
1382         NodeType nodeType = descriptor.getNodeType();
1383         switch(nodeType.getType()) {
1384             case NodeType.NAMESPACE:
1385             case NodeType.ATTRIBUTE:
1386                 added = _attributes.add(descriptor);
1387                 if (added) {
1388                     _attArray = null;
1389                 }
1390                 break;
1391             case NodeType.TEXT:
1392                 _contentDescriptor = descriptor;
1393                 added = true;
1394                 break;
1395             default:
1396                 added = _elements.add(descriptor);
1397                 if (added) {
1398                     _elemArray = null;
1399                     if (descriptor.isContainer()) ++_containerCount;
1400                 }
1401                 break;
1402         }
1403 
1404         if ((added) && (relink)) {
1405             descriptor.setContainingClassDescriptor( this );
1406         }
1407 
1408     } //-- addFieldDescriptor
1409 
1410 
1411     private XMLFieldDescriptor[] getAttributeArray() {
1412         //-- create local reference to prevent possible
1413         //-- null pointer (_attArray could be re-set to null)
1414         //-- in multi-threaded environment
1415         XMLFieldDescriptor[] descriptors = _attArray;
1416         if (descriptors == null) {
1417             descriptors = _attributes.toArray();
1418             _attArray = descriptors;
1419         }
1420         return descriptors;
1421     }
1422 
1423     private XMLFieldDescriptor[] getElementArray() {
1424         //-- create local reference to prevent possible
1425         //-- null pointer (_elemArray could be re-set to null)
1426         //-- in multi-threaded environment
1427         XMLFieldDescriptor[] descriptors = _elemArray;
1428         if (descriptors == null) {
1429             descriptors = _elements.toArray();
1430             _elemArray = descriptors;
1431         }
1432         return descriptors;
1433     }
1434     
1435     /**
1436      * Adds a XMLFieldDescriptor instance to the internally maintained
1437      * list of sequence elements.
1438      * @param element An {@link XMLFieldDescriptor} instance for an element definition.
1439      */
1440     protected void addSequenceElement(XMLFieldDescriptor element) {
1441         _sequenceOfElements.add(element);
1442     }
1443     
1444     public List<String> getSubstitutes() {
1445         return _substitutes;
1446     }
1447 
1448     public void setSubstitutes(List<String> substitutes) {
1449         _substitutes = substitutes;
1450     }
1451          
1452     public boolean isChoice() {
1453         return this._compositor == CHOICE;
1454     }
1455     
1456     /**
1457      * @see org.exolab.castor.builder.info.nature.PropertyHolder#
1458      *      getProperty(java.lang.String)
1459      * @param name
1460      *            of the property
1461      * @return value of the property
1462      */
1463     public Object getProperty(final String name) {
1464         return _properties.get(name);
1465     }
1466 
1467     /**
1468      * @see org.exolab.castor.builder.info.nature.PropertyHolder#
1469      *      setProperty(java.lang.String, java.lang.Object)
1470      * @param name
1471      *            of the property
1472      * @param value
1473      *            of the property
1474      */
1475     public void setProperty(final String name, final Object value) {
1476         _properties.put(name, value);
1477     }
1478 
1479     /**
1480      * @see org.exolab.castor.builder.info.nature.NatureExtendable#
1481      *      addNature(java.lang.String)
1482      * @param nature
1483      *            ID of the Nature
1484      */
1485     public void addNature(final String nature) {
1486         _natures.add(nature);
1487     }
1488 
1489     /**
1490      * @see org.exolab.castor.builder.info.nature.NatureExtendable#
1491      *      hasNature(java.lang.String)
1492      * @param nature
1493      *            ID of the Nature
1494      * @return true if the Nature ID was added.
1495      */
1496     public boolean hasNature(final String nature) {
1497         return _natures.contains(nature);
1498     }
1499     
1500 }