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