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 }