1 /*
2 * Copyright 2006 Keith Visco, Ralf Joachim
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.exolab.castor.xml.util;
17
18 import org.exolab.castor.mapping.ClassDescriptor;
19 import org.exolab.castor.mapping.FieldDescriptor;
20 import org.exolab.castor.mapping.FieldHandler;
21 import org.exolab.castor.mapping.MapItem;
22 import org.exolab.castor.mapping.MappingException;
23 import org.exolab.castor.mapping.loader.FieldDescriptorImpl;
24 import org.exolab.castor.mapping.loader.FieldHandlerImpl;
25 import org.exolab.castor.xml.FieldValidator;
26 import org.exolab.castor.xml.NodeType;
27 import org.exolab.castor.xml.XMLClassDescriptor;
28 import org.exolab.castor.xml.XMLFieldDescriptor;
29 import org.exolab.castor.xml.descriptors.CoreDescriptors;
30 import org.exolab.castor.xml.handlers.DateFieldHandler;
31
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.StringTokenizer;
35 import java.util.Properties;
36
37 /**
38 * XML field descriptor. Wraps {@link FieldDescriptor} and adds XML-related
39 * information, type conversion, etc.
40 * <p>
41 * Note: When using a GeneralizedFieldHandler the getFieldType() methods of
42 * handler and descriptor need to return the same result.
43 *
44 * @author <a href="mailto:keith AT kvisco DOT com">Keith Visco</a>
45 * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
46 * @version $Revision$ $Date: 2006-04-13 06:47:36 -0600 (Thu, 13 Apr 2006) $
47 */
48 public class XMLFieldDescriptorImpl extends FieldDescriptorImpl implements XMLFieldDescriptor {
49 private static final String WILD_CARD = "*";
50
51 private static final String NULL_CLASS_ERR
52 = "The 'type' argument passed to the constructor of "
53 + "XMLFieldDescriptorImpl may not be null.";
54
55 private static final String NULL_NAME_ERR
56 = "The 'fieldName' argument passed to the constructor of "
57 + "XMLFieldDescriptorImpl may not be null.";
58
59 /** The index of this field within the constructor arguments. Note: This
60 * field is only applicable if the field is an attribute field and it's
61 * supposed to be set via the constructor. A value less than zero indicates
62 * that this field is not part of the constructor arguments.
63 */
64 private int _argIndex = -1;
65
66 /** True if the field is a container field. */
67 private boolean _container = false;
68
69 /** Flag to indicate that objects should be added to their as soon as they
70 * are created, but before they are finished being populated.
71 */
72 private boolean _incremental = false;
73
74 /** True if the field is a reference to another Object in the hierarchy. */
75 public boolean _isReference = false;
76
77 private boolean _isWild = false;
78
79 /** True if the field type is mapped in a Hashtable or Map. */
80 private boolean _mapped = false;
81
82 private String[] _matches = null;
83
84 /** True if the field is allowed to have nil content. */
85 private boolean _nillable = false;
86
87 /** The node type (attribute, element, text). */
88 private NodeType _nodeType = null;
89
90 /** The namespace prefix that is to be used when marshaling */
91 private String _nsPrefix = null;
92
93 /** The namespace URI used for both marshaling and unmarshaling. */
94 private String _nsURI = null;
95
96 /** The "user-set" properties of this XMLFieldDescriptor. */
97 private Properties _xmlProperties = null;
98
99 /** The XML Schema type of this field value. */
100 private String _schemaType = null;
101
102 /** The XML Schema type of the components for XML schema lists. */
103 private String _componentType = null;
104
105 /** The prefix used in case the value of the field described is of type QName. */
106 private String _qNamePrefix = null;
107
108 /** A flag which indicates the parent class' namespace should be used by default. */
109 private boolean _useParentClassNamespace = false;
110
111 private FieldValidator _validator = null;
112
113 /**
114 * The local XML name of the field; this does not include any
115 * path elements.
116 */
117 private String _xmlName = null;
118
119 /** The relative XML path used when wrapping in nested elements, does not
120 * include the name of the field itself.
121 */
122 private String _xmlPath = null;
123
124 private List<String> _substitutes;
125
126 /**
127 * Indicates whether the field described by this {@link XMLFieldDescriptorImpl} is
128 * created as a result of a <xs:list> definition.
129 */
130 private boolean _derivedFromXSList;
131
132
133 //----------------/
134 //- Constructors -/
135 //----------------/
136
137 public XMLFieldDescriptorImpl(final Class<?> fieldType, final String fieldName,
138 final String xmlName, final NodeType nodeType) {
139 _matches = new String[0];
140
141 if (fieldName == null) {
142 throw new IllegalArgumentException(NULL_NAME_ERR);
143 }
144 if (fieldType == null) {
145 throw new IllegalArgumentException(NULL_CLASS_ERR);
146 }
147
148 setFieldName(fieldName);
149
150 if (fieldType == org.exolab.castor.types.AnyNode.class) {
151 // if the field type is an AnyNode Castor must treat it as
152 // an object to avoid changes in the marshaling framework
153 setFieldType(java.lang.Object.class);
154 } else {
155 setFieldType(fieldType);
156 }
157
158 _nodeType = ((nodeType == null) ? NodeType.Attribute : nodeType );
159
160 //-- call the setXMLName method to handle checking for full path
161 setXMLName(xmlName);
162 }
163
164 /**
165 * Construct a new field descriptor for the specified field. This is an XML
166 * field descriptor wrapping a field descriptor and adding XML related
167 * properties and methods.
168 *
169 * @param fieldDesc The field descriptor
170 * @param xmlName The XML name of the field
171 * @param nodeType The node type of this field
172 * @param primitiveNodeType
173 * @throws MappingException Invalid mapping information
174 */
175 public XMLFieldDescriptorImpl(final FieldDescriptor fieldDesc, final String xmlName,
176 final NodeType nodeType, final NodeType primitiveNodeType) throws MappingException {
177 _matches = new String[0];
178
179 if (fieldDesc instanceof XMLFieldDescriptor) {
180 setContainingClassDescriptor(fieldDesc.getContainingClassDescriptor());
181 }
182
183 setFieldName(fieldDesc.getFieldName());
184
185 if (fieldDesc.getFieldType() == org.exolab.castor.types.AnyNode.class) {
186 // if the field type is an AnyNode Castor must treat it as
187 // an object to avoid changes in the marshaling framework
188 setFieldType(java.lang.Object.class);
189 } else {
190 setFieldType(fieldDesc.getFieldType());
191 }
192
193 ClassDescriptor cd = fieldDesc.getClassDescriptor();
194 if (cd != null) {
195 if (cd instanceof XMLClassDescriptor) {
196 setClassDescriptor(cd);
197 } else {
198 setClassDescriptor(new XMLClassDescriptorAdapter(cd, null, primitiveNodeType));
199 }
200 }
201
202 setHandler(fieldDesc.getHandler());
203 // Check for instances of java.util.Date. This logic really doesn't belong here,
204 // as it can interfere with user specified handlers. Instead it should go into
205 // XMLMappingLoader.
206 if (getFieldType() != null) {
207 if (java.util.Date.class.isAssignableFrom(getFieldType())) {
208 if (getHandler() instanceof FieldHandlerImpl) {
209 setHandler(new DateFieldHandler(getHandler()));
210 }
211 }
212 }
213
214 setTransient(fieldDesc.isTransient());
215 setImmutable(fieldDesc.isImmutable());
216 setRequired(fieldDesc.isRequired());
217 setMultivalued(fieldDesc.isMultivalued());
218
219 //-- handle xml name
220 if (xmlName == null) {
221 setXMLName(getFieldName());
222 } else {
223 setXMLName(xmlName);
224 }
225
226 if (nodeType == null) {
227 if (isMultivalued()) {
228 _nodeType = NodeType.Element;
229 } else {
230 _nodeType = NodeType.Attribute;
231 }
232 } else {
233 _nodeType = nodeType;
234 }
235
236 if (isRequired()) {
237 _validator = new FieldValidator();
238 _validator.setMinOccurs(1);
239 _validator.setDescriptor(this);
240 }
241 }
242
243 /**
244 * Sets whether or not the value of the field represented by this
245 * FieldDescriptor should be set via the constructor of the containing
246 * ClassDescriptor. The index value greater than 0 specifies the index
247 * within the argument array that the value of this field should be.
248 * <p>
249 * Note: This only applies to attribute mapped fields at this time.
250 *
251 * @param index the index within the argument array. A value less than zero
252 * indicates that this field should not be part of the constructor
253 * arguments.
254 */
255 public void setConstructorArgumentIndex(final int index) {
256 if (_nodeType != NodeType.Attribute) {
257 String err = "constructor arguments only valid for attribute mapped fields.";
258 throw new IllegalStateException(err);
259 }
260 _argIndex = index;
261 }
262
263 /**
264 * @see org.exolab.castor.xml.XMLFieldDescriptor#getConstructorArgumentIndex()
265 * {@inheritDoc}
266 */
267 public int getConstructorArgumentIndex() {
268 return _argIndex;
269 }
270
271 /**
272 * @see org.exolab.castor.xml.XMLFieldDescriptor#isConstructorArgument()
273 * {@inheritDoc}
274 */
275 public boolean isConstructorArgument() {
276 return (_argIndex >= 0);
277 }
278
279 /**
280 * Sets the location path for the field being described.
281 * <p>
282 * In most cases, this isn't needed. However sometimes a field may be mapped
283 * to a nested element. In which case the value of the location path should
284 * be the nested element name. If more than one level of nesting is needed
285 * each nested element name should be separated by a path separator (forward
286 * slash '/').
287 * <p>
288 * The location path name is "relative" to the parent Class. The name of the
289 * parent should not be included in the path.
290 * <p>
291 * For example, give the following two classes: <code>
292 * class Root {
293 * Bar bar;
294 * }
295 *
296 * class Bar {
297 * String value;
298 * }
299 * </code>
300 *
301 * And the following XML:
302 *
303 * <code>
304 * <root>
305 * <foo>
306 * <bar> value of bar </bar>
307 * </foo>
308 * </root>
309 * </code>
310 *
311 * Since foo has no associated class, the path for 'bar' would be: "foo"
312 *
313 * @param path the "relative" location path for the field.
314 * @see #getLocationPath
315 */
316 public void setLocationPath(final String path) {
317 //-- need to add some validation to the path at some point.
318 _xmlPath = path;
319 }
320
321 /**
322 * @see org.exolab.castor.xml.XMLFieldDescriptor#getLocationPath()
323 * {@inheritDoc}
324 */
325 public String getLocationPath() {
326 return _xmlPath;
327 }
328
329 /**
330 * Sets the namespace prefix used when marshaling as XML.
331 *
332 * @param nsPrefix The namespace prefix used when marshaling the
333 * "described" object.
334 */
335 public void setNameSpacePrefix(final String nsPrefix) {
336 _nsPrefix = nsPrefix;
337 }
338
339 /**
340 * @see org.exolab.castor.xml.XMLFieldDescriptor#getNameSpacePrefix()
341 * {@inheritDoc}
342 */
343 public String getNameSpacePrefix() {
344 return _nsPrefix;
345 }
346
347 /**
348 * Sets whether or not the namespace for the parent "containing" class
349 * should be used during marshaling/unmarshaling when no specific
350 * namespace URI has been set for this field.
351 */
352 public void setUseParentsNamespace(final boolean useParentsNamespace) {
353 _useParentClassNamespace = useParentsNamespace;
354 }
355
356 /**
357 * Sets the namespace URI used when marshaling and unmarshaling as XML.
358 *
359 * @param nsURI The namespace URI used when marshaling and unmarshaling the
360 * "described" Object.
361 */
362 public void setNameSpaceURI(final String nsURI) {
363 _nsURI = nsURI;
364 }
365
366 /**
367 * @see org.exolab.castor.xml.XMLFieldDescriptor#getNameSpaceURI()
368 * {@inheritDoc}
369 */
370 public String getNameSpaceURI() {
371 ClassDescriptor parent = getContainingClassDescriptor();
372 if ((_nsURI == null) && (parent != null) && _useParentClassNamespace) {
373 Class<?> type = getFieldType();
374 boolean test = ! (isAnyNode(type));
375 if ((_nodeType == NodeType.Element) && test) {
376 if (parent instanceof XMLClassDescriptor) {
377 return ((XMLClassDescriptor) parent).getNameSpaceURI();
378 }
379 }
380 }
381 return _nsURI;
382 }
383
384 /**
385 * Sets the XML node type for the described field.
386 *
387 * @param nodeType the NodeType for the described field.
388 */
389 public void setNodeType(final NodeType nodeType) {
390 _nodeType = ((nodeType == null) ? NodeType.Attribute : nodeType);
391 }
392
393 /**
394 * @see org.exolab.castor.xml.XMLFieldDescriptor#getNodeType()
395 * {@inheritDoc}
396 */
397 public NodeType getNodeType() {
398 return _nodeType;
399 }
400
401 /**
402 * Sets the value property with the given name.
403 *
404 * @param propertyName The name of the property to set the value of.
405 * @param value The value of the property.
406 * @see #getProperty
407 */
408 public void setXMLProperty(final String propertyName, final String value) {
409 if (propertyName == null) {
410 String err = "The argument 'propertyName' must not be null.";
411 throw new IllegalArgumentException(err);
412 }
413
414 if (_xmlProperties == null) {
415 _xmlProperties = new Properties();
416 }
417
418 if (value == null) {
419 _xmlProperties.remove(propertyName);
420 } else {
421 _xmlProperties.put(propertyName, value);
422 }
423 }
424
425 /**
426 * @see org.exolab.castor.xml.XMLFieldDescriptor#getProperty(java.lang.String)
427 * {@inheritDoc}
428 */
429 public String getXMLProperty(final String propertyName) {
430 if ((_xmlProperties == null) || (propertyName == null)) {
431 return null;
432 }
433 return _xmlProperties.getProperty(propertyName);
434 }
435
436 /**
437 * Sets the type of the XML Schema type of the value for the field being
438 * described.
439 *
440 * @param schemaType The value type.
441 */
442 public void setSchemaType(final String schemaType) {
443 _schemaType =schemaType;
444 }
445
446 /**
447 * Sets the type of the XML Schema type of the value for the field being
448 * described.
449 *
450 * @param componentType The component type for <xs:list>s.
451 */
452 public void setComponentType(final String componentType) {
453 _componentType = componentType;
454 }
455
456 /**
457 * @see org.exolab.castor.xml.XMLFieldDescriptor#getSchemaType()
458 * {@inheritDoc}
459 */
460 public String getSchemaType() {
461 return _schemaType;
462 }
463
464 /**
465 * {@inheritDoc}
466 * @see org.exolab.castor.xml.XMLFieldDescriptor#getComponentType()
467 */
468 public String getComponentType() {
469 return _componentType;
470 }
471
472 public void setValidator(final FieldValidator validator) {
473 if (_validator != null) {
474 _validator.setDescriptor(null);
475 }
476 _validator = validator;
477 if (_validator != null) {
478 _validator.setDescriptor(this);
479 }
480 }
481
482 /**
483 * @see org.exolab.castor.xml.XMLFieldDescriptor#getValidator()
484 * {@inheritDoc}
485 */
486 public FieldValidator getValidator() {
487 return _validator;
488 }
489
490 /**
491 * Sets the xml name for the described field.
492 *
493 * @param xmlName the XML name for the described field.
494 */
495 public void setXMLName(final String xmlName) {
496 _xmlName = xmlName;
497 }
498
499 /**
500 * @see org.exolab.castor.xml.XMLFieldDescriptor#getXMLName()
501 * {@inheritDoc}
502 */
503 public String getXMLName() {
504 return _xmlName;
505 }
506
507 /**
508 * Set if the field is a container field or not.
509 *
510 * @param isContainer a boolean indicating whether or not the field is a
511 * container field.
512 */
513 public void setContainer(final boolean isContainer) {
514 _container = isContainer;
515 }
516
517 /**
518 * @see org.exolab.castor.xml.XMLFieldDescriptor#isContainer()
519 * {@inheritDoc}
520 */
521 public boolean isContainer() {
522 return _container;
523 }
524
525 /**
526 * Sets the incremental flag which indicates whether this member can be
527 * added before the unmarshaler is finished unmarshaling it.
528 *
529 * @param incremental the boolean which if true indicated that this member
530 * can safely be added before the unmarshaler is finished
531 * unmarshaling it.
532 */
533 public void setIncremental(final boolean incremental) {
534 _incremental = incremental;
535 }
536
537 /**
538 * @see org.exolab.castor.xml.XMLFieldDescriptor#isIncremental()
539 * {@inheritDoc}
540 */
541 public boolean isIncremental() {
542 return _incremental;
543 }
544
545 /**
546 * Sets whether or not this field has been mapped in a Map or Hashtable.
547 *
548 * @param mapped a boolean that when true indicates this field is a
549 * Hashtable or Map.
550 */
551 public void setMapped(final boolean mapped) {
552 _mapped = mapped;
553 }
554
555 /**
556 * @see org.exolab.castor.xml.XMLFieldDescriptor#isMapped()
557 * {@inheritDoc}
558 */
559 public boolean isMapped() {
560 return _mapped;
561 }
562
563 /**
564 * Sets whether or not the described field is allowed to be nil. A nillable
565 * field can have empty content (text or element content), but may have
566 * attribute values, and still be considered value, even if the child
567 * elements are required.
568 *
569 * @param nillable a boolean indicating whether or not the described field
570 * may be nillable.
571 */
572 public void setNillable(final boolean nillable) {
573 _nillable = nillable;
574 }
575
576 /**
577 * @see org.exolab.castor.xml.XMLFieldDescriptor#isNillable()
578 * {@inheritDoc}
579 */
580 public boolean isNillable() {
581 return _nillable;
582 }
583
584 /**
585 * Sets the flag indicating that the field described by this descriptor is a
586 * reference to another field in the object model.
587 *
588 * @param isReference true if the field is a reference to another field.
589 */
590 public void setReference(final boolean isReference) {
591 _isReference = isReference;
592 }
593
594 /**
595 * @see org.exolab.castor.xml.XMLFieldDescriptor#isReference()
596 * {@inheritDoc}
597 */
598 public boolean isReference() {
599 return _isReference;
600 }
601
602 /**
603 * Sets the prefix used in case the value of the field described by this
604 * descriptor is of type QName.
605 *
606 * @param qNamePrefix
607 */
608 public void setQNamePrefix(final String qNamePrefix) {
609 _qNamePrefix = qNamePrefix;
610 }
611
612 /**
613 * Returns the prefix used in case the value of the field described by this
614 * descriptor is of type QName. This is helpful for the marshaler but not
615 * mandatory.
616 *
617 * @return the prefix used in the QName value.
618 */
619 public String getQNamePrefix() {
620 return _qNamePrefix;
621 }
622
623 /**
624 * This is a space separated list of xml names that this Field descriptor
625 * matches. A '*' is wild.
626 *
627 * @param matchExpr the space separated list of xml names, matched by this
628 * descriptor.
629 */
630 public void setMatches(String matchExpr) {
631 _isWild = false;
632
633 if ((matchExpr == null) || (matchExpr.length() == 0)) {
634 return;
635 }
636
637 StringTokenizer st = new StringTokenizer(matchExpr);
638 List<String> names = new ArrayList<String>();
639 while (st.hasMoreTokens()) {
640 String token = st.nextToken();
641 if (WILD_CARD.equals(token)) {
642 _isWild = true;
643 break;
644 }
645 names.add(token);
646 }
647 _matches = new String[names.size()];
648 names.toArray(_matches);
649 }
650
651 /**
652 * @see org.exolab.castor.xml.XMLFieldDescriptor#matches(java.lang.String)
653 * {@inheritDoc}
654 */
655 public boolean matches(final String xmlName) {
656 if (xmlName != null) {
657 if (_isWild) {
658 return true;
659 } else if (_matches.length > 0) {
660 for (int i = 0; i < _matches.length; i++) {
661 if (xmlName.equals(_matches[i])) {
662 return true;
663 }
664 }
665 } else {
666 return xmlName.equals(_xmlName);
667 }
668 }
669 return false;
670 }
671
672 /**
673 * @see org.exolab.castor.xml.XMLFieldDescriptor#matches(java.lang.String, java.lang.String)
674 * {@inheritDoc}
675 */
676 public boolean matches(final String xmlName, final String namespace) {
677 // compare namespaces
678 if (namespace == null) {
679 if ((_nsURI != null) && (_nsURI.length() > 0)) {
680 return false;
681 }
682 } else if (_nsURI == null) {
683 if ((namespace.length() > 0) && (!_isWild)) {
684 return false;
685 }
686 } else if (!_nsURI.equals(namespace)) {
687 return false;
688 }
689
690 // if we make this far the namespaces match, now compare names
691 return matches(xmlName);
692 }
693
694 /**
695 * Returns true if two XMLFieldDescriptors should be treated as equal. Any
696 * XMLFieldDescriptor that handles the same field is considered equal.
697 * @param obj The object to compare to <code>this</code>
698 *
699 * @return true if two XMLFieldDescriptors should be treated as equal.
700 */
701 public boolean equals(final Object obj) {
702 if (obj == this) {
703 return true;
704 }
705
706 if ((obj == null) || (!(obj instanceof XMLFieldDescriptor))) {
707 return false;
708 }
709
710 XMLFieldDescriptor descriptor = (XMLFieldDescriptor) obj;
711
712 // check field names
713 if (!getFieldName().equals(descriptor.getFieldName())) {
714 return false;
715 }
716
717 // check field types
718 if (!getFieldType().equals(descriptor.getFieldType())) {
719 return false;
720 }
721
722 // check field handler
723 FieldHandler tmpHandler = descriptor.getHandler();
724 if (getHandler() == null) {
725 return (tmpHandler == null);
726 } else if (tmpHandler == null) {
727 return false;
728 }
729
730 // The following line causes some issues when used against a FieldHandlerImpl
731 // because the equals method for FieldHandlerImpl is the default. Temporarily
732 // replace it with a slightly more generic comparison but this should probably
733 // change in the future. (kv)
734 // return (_handler.equals(tmpHandler));
735 return (getHandler().getClass().isInstance(tmpHandler));
736 }
737
738 /**
739 * Returns the hashCode for this XMLFieldDescriptor
740 * @return the hashCode for this XMLFieldDescriptor
741 */
742 public int hashCode() {
743 int hash = 17;
744 hash = 17 * getFieldName().hashCode();
745 hash = hash * 17 * getFieldType().hashCode();
746 if (getHandler() != null) { hash = hash * 17 * getHandler().hashCode(); }
747 return hash;
748 }
749
750 public String toString() {
751 StringBuffer buffer = new StringBuffer(32);
752 buffer.append("XMLFieldDesciptor: ");
753 buffer.append(getFieldName());
754 buffer.append(" AS ");
755 buffer.append(_xmlName);
756 if (getNameSpaceURI() != null) {
757 buffer.append("{" + getNameSpaceURI() + "}");
758 }
759 return buffer.toString();
760 }
761
762 /**
763 * Returns true if the given class should be treated as a primitive type.
764 * This method will return true for all Java primitive types, the set of
765 * primitive object wrappers, as well as Strings.
766 *
767 * @return true If the given class should be treated as a primitive type.
768 */
769 private static boolean isPrimitive(final Class<?> type) {
770 if (type == null) {
771 return false;
772 }
773 if (type.isPrimitive()) {
774 return true;
775 }
776 if (type == String.class) {
777 return true;
778 }
779 if ((type == Boolean.class) || (type == Character.class)) {
780 return true;
781 }
782
783 // Any class which extends Number should be treated as a primitive.
784 return (type.getSuperclass() == Number.class);
785 }
786
787 /**
788 * Return true if the given class is a "built-in" type. A built-in type is
789 * one in which Castor provides the default descriptor for.
790 *
791 * @param type The class to check.
792 * @return true If the given class is a built-in type.
793 */
794 private static boolean isBuiltInType(final Class<?> type) {
795 if (type == null) {
796 return false;
797 }
798 //-- All built-in Java types, such as java.util.Date,
799 //-- java.sql.Date, various Collection classes, etc.
800 return (CoreDescriptors.getDescriptor(type) != null);
801 }
802
803 private static boolean isMappedItem(final Class<?> fieldType) {
804 return (fieldType == MapItem.class);
805 }
806
807 private static boolean isAnyNode(final Class<?> type) {
808 return (type == Object.class);
809 }
810
811 /**
812 * Returns the possible substitution groups for this class.
813 * @return the possible substitution groups for this class.
814 */
815 public List<String> getSubstitutes() {
816 return _substitutes;
817 }
818
819 /**
820 * Sets the possible substitution groups for this class.
821 * @param substitutes Possible substitution groups for this class.
822 */
823 public void setSubstitutes(List<String> substitutes) {
824 _substitutes = substitutes;
825 }
826
827 /**
828 * {@inheritDoc}
829 *
830 * @see org.exolab.castor.xml.XMLFieldDescriptor#setDerivedFromXSList(boolean)
831 */
832 public void setDerivedFromXSList(final boolean derivedFromXSList) {
833 _derivedFromXSList = derivedFromXSList;
834 }
835
836 /**
837 * {@inheritDoc}
838 *
839 * @see org.exolab.castor.xml.XMLFieldDescriptor#isDerivedFromXSList()
840 */
841 public boolean isDerivedFromXSList() {
842 return _derivedFromXSList;
843 }
844
845 }