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