View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Intalio, Inc.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Intalio, Inc. Exolab is a registered
23   *    trademark of Intalio, Inc.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 1999-2004 (C) Intalio, Inc. All Rights Reserved.
42   *
43   * This file was originally developed by Keith Visco during the
44   * course of employment at Intalio Inc.
45   * All portions of this file developed by Keith Visco after Jan 19 2005 are
46   * Copyright (C) 2005 Keith Visco. All Rights Reserverd.
47   *
48   * $Id$
49   */
50  package org.exolab.castor.builder.descriptors;
51  
52  import java.util.Iterator;
53  import java.util.List;
54  
55  import org.exolab.castor.builder.BuilderConfiguration;
56  import org.exolab.castor.builder.SGTypes;
57  import org.exolab.castor.builder.factory.XMLFieldHandlerFactory;
58  import org.exolab.castor.builder.info.ClassInfo;
59  import org.exolab.castor.builder.info.CollectionInfo;
60  import org.exolab.castor.builder.info.FieldInfo;
61  import org.exolab.castor.builder.info.XMLInfo;
62  import org.exolab.castor.builder.info.NodeType;
63  import org.exolab.castor.builder.info.nature.XMLInfoNature;
64  import org.exolab.castor.builder.types.XSList;
65  import org.exolab.castor.builder.types.XSListType;
66  import org.exolab.castor.builder.types.XSType;
67  import org.exolab.castor.mapping.FieldDescriptor;
68  import org.exolab.castor.xml.Validator;
69  import org.exolab.castor.xml.XMLConstants;
70  import org.exolab.castor.xml.XMLFieldDescriptor;
71  import org.exolab.javasource.JClass;
72  import org.exolab.javasource.JConstant;
73  import org.exolab.javasource.JConstructor;
74  import org.exolab.javasource.JField;
75  import org.exolab.javasource.JMember;
76  import org.exolab.javasource.JModifiers;
77  import org.exolab.javasource.JNaming;
78  import org.exolab.javasource.JPrimitiveType;
79  import org.exolab.javasource.JSourceCode;
80  import org.exolab.javasource.JType;
81  
82  /**
83   * A factory for creating the source code of descriptor classes.
84   *
85   * @author <a href="mailto:keith AT kvisco DOT com">Keith Visco</a>
86   * @version $Revision$ $Date: 2006-04-13 07:37:49 -0600 (Thu, 13 Apr 2006) $
87   */
88  public final class DescriptorSourceFactory {
89      /** GeneralizedFieldHandler. */
90      private static final JClass GENERALIZED_FIELD_HANDLER_CLASS =
91          new JClass("org.exolab.castor.mapping.GeneralizedFieldHandler");
92      /** Name of the field validator instance variable in generated code. */
93      private static final String FIELD_VALIDATOR_NAME = "fieldValidator";
94  
95      /** The BuilderConfiguration instance. */
96      private final BuilderConfiguration _config;
97  
98      /** Factory for creating XMLFieldHandler instances embedded in descriptors. */
99      private XMLFieldHandlerFactory _xmlFieldHandlerFactory;
100 
101     /**
102      * Creates a new DescriptorSourceFactory with the given configuration.
103      *
104      * @param config the BuilderConfiguration instance
105      */
106     public DescriptorSourceFactory(final BuilderConfiguration config) {
107         if (config == null) {
108             String err = "The argument 'config' must not be null.";
109             throw new IllegalArgumentException(err);
110         }
111         _config = config;
112         _xmlFieldHandlerFactory = new XMLFieldHandlerFactory(config);
113     } //-- DescriptorSourceFactory
114 
115     /**
116      * Creates the Source code of a MarshalInfo for a given XML Schema element
117      * declaration.
118      *
119      * @param classInfo the XML Schema element declaration
120      * @return the JClass representing the MarshalInfo source code
121      */
122     public JClass createSource(final ClassInfo classInfo) {
123         JClass jClass              = classInfo.getJClass();
124         String localClassName      = jClass.getLocalName();
125 
126         String descriptorClassName = getQualifiedDescriptorClassName(jClass.getName());
127         DescriptorJClass classDesc = new DescriptorJClass(_config, descriptorClassName, jClass);
128 
129         //-- get handle to default constuctor
130         JConstructor cons = classDesc.getConstructor(0);
131         JSourceCode jsc   = cons.getSourceCode();
132         XMLInfoNature xmlNature = new XMLInfoNature(classInfo);
133 
134         //-- Set namespace prefix
135         String nsPrefix = xmlNature.getNamespacePrefix();
136         if ((nsPrefix != null) && (nsPrefix.length() > 0)) {
137             jsc.add("_nsPrefix = \"");
138             jsc.append(nsPrefix);
139             jsc.append("\";");
140         }
141 
142         //-- Set namespace URI
143         String nsURI = xmlNature.getNamespaceURI();
144         if ((nsURI != null) && (nsURI.length() > 0)) {
145             jsc.add("_nsURI = \"");
146             jsc.append(nsURI);
147             jsc.append("\";");
148         }
149 
150         //-- set XML Name
151         String xmlName = xmlNature.getNodeName();
152         if (xmlName != null) {
153             jsc.add("_xmlName = \"");
154             jsc.append(xmlName);
155             jsc.append("\";");
156         }
157 
158         //-- set Element Definition flag
159         boolean elementDefinition = xmlNature.isElementDefinition();
160         jsc.add("_elementDefinition = ");
161         jsc.append(new Boolean(elementDefinition).toString());
162         jsc.append(";");
163         
164         //-- set grouping compositor
165         if (xmlNature.isChoice()) {
166             jsc.add("");
167             jsc.add("//-- set grouping compositor");
168             jsc.add("setCompositorAsChoice();");
169         } else if (xmlNature.isSequence()) {
170             jsc.add("");
171             jsc.add("//-- set grouping compositor");
172             jsc.add("setCompositorAsSequence();");
173         }
174         
175         // handle substitution groups
176         List<String> substitutionGroups = xmlNature.getSubstitutionGroups();
177         if (!substitutionGroups.isEmpty()) {
178             jsc.add("java.util.List substitutionGroups = new java.util.ArrayList();");
179             Iterator<String> substitutionGroupIter = substitutionGroups.iterator();
180             while (substitutionGroupIter.hasNext()) {
181                 String substitutionGroup = substitutionGroupIter.next();
182                 jsc.add("substitutionGroups.add(\"");
183                 jsc.append(substitutionGroup);
184                 jsc.append("\");");
185             }
186             jsc.add("setSubstitutes(substitutionGroups);");
187         }
188                 
189         //-- To prevent compiler warnings...make sure
190         //-- we don't declare temp variables if field count is 0;
191         if (classInfo.getFieldCount() == 0) {
192             return classDesc;
193         }
194 
195         //-- declare temp variables
196         jsc.add("org.exolab.castor.xml.util.XMLFieldDescriptorImpl  desc           = null;");
197         jsc.add("org.exolab.castor.mapping.FieldHandler             handler        = null;");
198         jsc.add("org.exolab.castor.xml.FieldValidator               fieldValidator = null;");
199 
200         //-- handle  content
201         if (classInfo.allowContent()) {
202             createDescriptor(classDesc, classInfo.getTextField(), localClassName, null, jsc);
203         }
204 
205         ClassInfo   base = classInfo.getBaseClass();
206         FieldInfo[] atts = classInfo.getAttributeFields();
207 
208         //--------------------------------/
209         //- Create attribute descriptors -/
210         //--------------------------------/
211 
212         jsc.add("//-- initialize attribute descriptors");
213         jsc.add("");
214 
215         for (int i = 0; i < atts.length; i++) {
216             FieldInfo member = atts[i];
217             //-- skip transient members
218             if (member.isTransient()) {
219                 continue;
220             }
221 
222             if (base != null) {
223                 String baseNodeName = new XMLInfoNature(member).getNodeName();
224                 if (baseNodeName.equals(XMLInfo.CHOICE_NODE_NAME_ERROR_INDICATION)) {
225                     createDescriptor(classDesc, member, localClassName, nsURI, jsc);
226                 } else {
227                     if (base.getAttributeField(baseNodeName) != null) {
228                         createRestrictedDescriptor(member, jsc);
229                     } else {
230                         createDescriptor(classDesc, member, localClassName,
231                                 nsURI, jsc);
232                     }
233                 }
234             } else {
235                 createDescriptor(classDesc, member, localClassName, nsURI, jsc);
236             }
237         }
238 
239         //------------------------------/
240         //- Create element descriptors -/
241         //------------------------------/
242         FieldInfo[] elements = classInfo.getElementFields();
243 
244         jsc.add("//-- initialize element descriptors");
245         jsc.add("");
246 
247         for (int i = 0; i < elements.length; i++) {
248             FieldInfo member = elements[i];
249             XMLInfoNature fieldNature = new XMLInfoNature(member);
250             
251             //-- skip transient members
252             if (member.isTransient()) {
253                 continue;
254             }
255 
256             if (base != null) {
257                 String baseNodeName = fieldNature.getNodeName();
258                 if (baseNodeName == null) {
259                     createDescriptor(classDesc, member, localClassName, nsURI, jsc);
260                 } else if (baseNodeName.equals(XMLInfo.CHOICE_NODE_NAME_ERROR_INDICATION)) {
261                     createDescriptor(classDesc, member, localClassName, nsURI, jsc);
262                 } else {
263                     if (base.getElementField(baseNodeName) != null) {
264                         createRestrictedDescriptor(member, jsc);
265                     } else {
266                         createDescriptor(classDesc, member, localClassName,
267                                 nsURI, jsc);
268                     }
269                 }
270             } else {
271                 createDescriptor(classDesc, member, localClassName, nsURI, jsc);
272             }
273         }
274 
275         return classDesc;
276     } //-- createSource
277 
278     //-------------------/
279     //- Private Methods -/
280     //-------------------/
281 
282     /**
283      * Returns the fully-qualified class name of the Descriptor to create. Given
284      * the fully-qualified class name of the class we are creating a Descriptor
285      * for, return the correct fully-qualified name for the Descriptor.
286      *
287      * @param name
288      *            fully-qualified class name of the class we are describing
289      * @return the fully-qualified class name of the Descriptor to create
290      */
291     private String getQualifiedDescriptorClassName(final String name) {
292         String descPackage   = JNaming.getPackageFromClassName(name);
293         String descClassName = JNaming.getLocalNameFromClassName(name);
294 
295         if (descPackage != null && descPackage.length() > 0) {
296             descPackage = descPackage +  "." + XMLConstants.DESCRIPTOR_PACKAGE + ".";
297         } else {
298             descPackage = "";
299         }
300         return descPackage + descClassName + XMLConstants.DESCRIPTOR_SUFFIX;
301     }
302 
303     /**
304      * Create special code for handling a member that is a restriction,
305      * only changing the validation code.
306      * 
307      * This basically works by obtaining the {@link FieldDescriptor} instance from the
308      * base class, and setting a new {@link Validator} instance.
309      *  
310      * @param member the restricted member for which we generate the restriction handling.
311      * @param jsc the source code to which we append the validation code.
312      */
313     private static void createRestrictedDescriptor(final FieldInfo member, final JSourceCode jsc) {
314         jsc.add("desc = (org.exolab.castor.xml.util.XMLFieldDescriptorImpl) getFieldDescriptor(\"");
315         XMLInfoNature xmlNature = new XMLInfoNature(member);
316         jsc.append(xmlNature.getNodeName());
317         jsc.append("\"");
318         jsc.append(", _nsURI");
319         NodeType nodeType = xmlNature.getNodeType();
320         if (nodeType == NodeType.ELEMENT) {
321             jsc.append(", org.exolab.castor.xml.NodeType.Element);");
322         } else if (nodeType == NodeType.ATTRIBUTE) {
323             jsc.append(", org.exolab.castor.xml.NodeType.Attribute);");
324         } else {
325             jsc.append("org.exolab.castor.xml.NodeType.Text);");
326         }
327         
328         //-- deal with amended cardinality
329         if (xmlNature.isRequired()) {
330             jsc.add("desc.setRequired(true);");
331         }
332         
333         //--modify the validation code
334         addValidationCode(member, jsc);
335     }
336 
337     /**
338      * Creates a specific descriptor for a given member (whether an attribute or
339      * an element) represented by a given Class name.
340      *
341      * @param classDesc JClass-equivalent descriptor for this Descriptor class
342      * @param member the member for which to create a descriptor
343      * @param localClassName unqualified (no package) name of this class
344      * @param nsURI namespace URI
345      * @param jsc the source code to which we'll add this descriptor
346      */
347     private void createDescriptor(final DescriptorJClass classDesc, final FieldInfo member,
348                                   final String localClassName, final String nsURI,
349                                   final JSourceCode jsc) {
350 
351         XMLInfoNature xmlNature = new XMLInfoNature(member);
352         
353         XSType xsType = xmlNature.getSchemaType();
354         XSType xsCollectionType = null;
355         
356         boolean any         = false;
357         NodeType nodeType = xmlNature.getNodeType();
358         boolean isElement   = (nodeType == NodeType.ELEMENT);
359         boolean isAttribute = (nodeType == NodeType.ATTRIBUTE);
360         boolean isText      = (nodeType == NodeType.TEXT);
361 
362         jsc.add("//-- ");
363         jsc.append(member.getName());
364 
365         //-- a hack, I know, I will change later (kv)
366         if (member.getName().equals("_anyObject")) {
367             any = true;
368         }
369 
370         if (xsType.isCollection()) {
371             //Attributes can handle COLLECTION type for NMTOKENS or IDREFS for instance
372             xsCollectionType = xsType;
373             xsType = new XMLInfoNature(((CollectionInfo) member).getContent()).getSchemaType();
374         }
375 
376         // Resolve how the node name parameter to the XMLFieldDescriptorImpl constructor is supplied
377         String nodeName = xmlNature.getNodeName();
378         String nodeNameParam = null;
379         if ((nodeName != null) && (!isText)) {
380             //-- By default the node name parameter is a literal string
381             nodeNameParam = "\"" + nodeName + "\"";
382             if (_config.classDescFieldNames()) {
383                 //-- The node name parameter is a reference to a public static final
384                 nodeNameParam = nodeName.toUpperCase();
385                 //-- Expose node name as public static final (reused by XMLFieldDescriptorImpl)
386                 JConstant constant = new JConstant(SGTypes.STRING, nodeNameParam);
387                 constant.setInitString("\"" + nodeName + "\"");
388                 classDesc.addMember(constant);
389             }
390         }
391 
392         //-- Generate code to new XMLFieldDescriptorImpl instance
393         jsc.add("desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(");
394         jsc.append(classType(xsType.getJType()));
395         jsc.append(", \"");
396         jsc.append(member.getName());
397         jsc.append("\", ");
398         if (nodeNameParam != null) {
399             jsc.append(nodeNameParam);
400         } else if (isText) {
401             jsc.append("\"PCDATA\"");
402         } else {
403             jsc.append("(java.lang.String) null");
404         }
405 
406         if (isElement) {
407             jsc.append(", org.exolab.castor.xml.NodeType.Element);");
408         } else if (isAttribute) {
409             jsc.append(", org.exolab.castor.xml.NodeType.Attribute);");
410         } else if (isText) {
411             jsc.append(", org.exolab.castor.xml.NodeType.Text);");
412         }
413 
414         switch (xsType.getType()) {
415             case XSType.STRING_TYPE :
416                 jsc.add("desc.setImmutable(true);");
417                 break;
418                 //only for attributes
419             case XSType.IDREF_TYPE :
420                 jsc.add("desc.setReference(true);");
421                 break;
422             case XSType.ID_TYPE :
423                 jsc.add("this._identity = desc;");
424                 break;
425             case XSType.QNAME_TYPE :
426                 jsc.add("desc.setSchemaType(\"QName\");");
427                 break;
428             default :
429                 break;
430         } //switch
431 
432         //-- handler access methods
433         if (member.getXMLFieldHandler() != null) {
434             String handler = member.getXMLFieldHandler();
435             jsc.add("handler = new " + handler + "();");
436             jsc.add("//-- test for generalized field handler");
437             jsc.add("if (handler instanceof ");
438             jsc.append(GENERALIZED_FIELD_HANDLER_CLASS.getName());
439             jsc.append(")");
440             jsc.add("{");
441             jsc.indent();
442             jsc.add("//-- save reference to user-specified handler");
443             jsc.add(GENERALIZED_FIELD_HANDLER_CLASS.getName());
444             jsc.append(" gfh = (");
445             jsc.append(GENERALIZED_FIELD_HANDLER_CLASS.getName());
446             jsc.append(") handler;");
447             _xmlFieldHandlerFactory.createXMLFieldHandler(
448                     member, xsType, localClassName, jsc, true);
449             jsc.add("gfh.setFieldHandler(handler);");
450             jsc.add("handler = gfh;");
451             jsc.unindent();
452             jsc.add("}");
453         } else {
454             _xmlFieldHandlerFactory.createXMLFieldHandler(
455                     member, xsType, localClassName, jsc, false);
456             addSpecialHandlerLogic(member, xsType, jsc);
457         }
458 
459         // Add the schema type as defined in the schema
460         if (xsCollectionType == null) {
461             jsc.add("desc.setSchemaType(\"" + xsType.getName() + "\");");
462         } else {
463             jsc.add("desc.setSchemaType(\"list\");");
464             jsc.add("desc.setComponentType(\"" + xsType.getName() + "\");");
465             if (xsCollectionType instanceof XSList && ((XSList) xsCollectionType).isDerivedFromXSList()) {
466                 jsc.add("desc.setDerivedFromXSList(true);");
467             }
468         }
469         jsc.add("desc.setHandler(handler);");
470 
471         //-- container
472         if (member.isContainer()) {
473             jsc.add("desc.setContainer(true);");
474             String className = xsType.getName(); //set the class descriptor
475             //Try to prevent endless loop. Note: we only compare the localClassName.
476             //If the packages are different an error can happen here
477             if (className.equals(localClassName)) {
478                 jsc.add("desc.setClassDescriptor(this);");
479             } else {
480                 String descriptorClassName = getQualifiedDescriptorClassName(className);
481                 jsc.add("desc.setClassDescriptor(new " + descriptorClassName + "());");
482             }
483         }
484 
485         //-- Handle namespaces
486         //-- FieldInfo namespace has higher priority than ClassInfo namespace.
487         if (xmlNature.getNamespaceURI() != null) {
488             jsc.add("desc.setNameSpaceURI(\"");
489             jsc.append(xmlNature.getNamespaceURI());
490             jsc.append("\");");
491         }
492 
493         //-- required
494         if (xmlNature.isRequired()) {
495             jsc.add("desc.setRequired(true);");
496         }
497 
498         //-- nillable
499         if (member.isNillable()) {
500            jsc.add("desc.setNillable(true);");
501         }
502 
503         //-- if any it can match all the names
504         if (any) {
505             jsc.add("desc.setMatches(\"*\");");
506         }
507 
508         //-- mark as multi or single valued for elements
509         if (isElement || isAttribute) {
510             jsc.add("desc.setMultivalued(" + xmlNature.isMultivalued());
511             jsc.append(");");
512         }
513 
514         jsc.add("addFieldDescriptor(desc);");
515         if (isElement) {
516             jsc.add("addSequenceElement(desc);");
517         }
518         jsc.add("");
519         
520         if (isElement) {
521             // handle substitution groups
522             addSubstitutionGroups(member, jsc);
523         }
524         
525         //-- Add Validation Code
526         addValidationCode(member, jsc);
527     }
528 
529     /**
530      * Adds substitution groups to the {@link XMLFieldDescriptor} instance .
531      * @param member The {@link FieldInfo} instance holding substitution group information
532      * @param jsc The {@link JSourceCode} instance to write the substitution groups to.
533      */
534     private void addSubstitutionGroups(final FieldInfo member, final JSourceCode jsc) {
535         List<String> substitutionGroupMembers = member.getSubstitutionGroupMembers();
536         if (!substitutionGroupMembers.isEmpty()) {
537             jsc.add("// set possible substitutes for member " + member.getName());
538             jsc.add("java.util.List substitutionGroups" + member.getName() 
539                     + " = new java.util.ArrayList();");
540             Iterator<String> substitutionGroupIter = substitutionGroupMembers.iterator();
541             while (substitutionGroupIter.hasNext()) {
542                 String substitutionGroup = substitutionGroupIter.next();
543                 jsc.add("substitutionGroups" + member.getName() + ".add(\"");
544                 jsc.append(substitutionGroup);
545                 jsc.append("\");");
546             }
547             jsc.add("desc.setSubstitutes(substitutionGroups" + member.getName() + ");");
548         }
549     }
550 
551     /**
552      * Adds additional logic or wrappers around the core handler for special
553      * types such as dates, enumerated types, collections, etc.
554      *
555      * @param member the member for which extra special handler logic may be created
556      * @param xsType the field type for which extra special handler logic may be created
557      * @param jsc the java source code to which this will be written
558      */
559     private void addSpecialHandlerLogic(final FieldInfo member, final XSType xsType,
560                                         final JSourceCode jsc) {
561         XMLInfoNature xmlNature = new XMLInfoNature(member);
562         
563         if (xsType.isEnumerated()) {
564             jsc.add("handler = new org.exolab.castor.xml.handlers.EnumFieldHandler(");
565             jsc.append(classType(xsType.getJType()));
566             jsc.append(", handler);");
567             jsc.add("desc.setImmutable(true);");
568         } else if (xsType.getType() == XSType.DATETIME_TYPE) {
569             jsc.add("handler = new org.exolab.castor.xml.handlers.DateFieldHandler(");
570             jsc.append("handler);");
571             jsc.add("desc.setImmutable(true);");
572         } else if (xsType.getType() == XSType.DECIMAL_TYPE) {
573             jsc.add("desc.setImmutable(true);");
574         } else if (xmlNature.getSchemaType().isCollection()) {
575             //-- Handle special Collection Types such as NMTOKENS and IDREFS
576             switch (xsType.getType()) {
577                 case XSType.NMTOKEN_TYPE:
578                 case XSType.NMTOKENS_TYPE:
579                     //-- use CollectionFieldHandler
580                     jsc.add("handler = new org.exolab.castor.xml.handlers.CollectionFieldHandler(");
581                     jsc.append("handler, new org.exolab.castor.xml.validators.NameValidator(");
582                     jsc.append("org.exolab.castor.xml.XMLConstants.NAME_TYPE_NMTOKEN));");
583                     break;
584                 case XSType.QNAME_TYPE:
585                     //-- use CollectionFieldHandler
586                     jsc.add("handler = new org.exolab.castor.xml.handlers.CollectionFieldHandler(");
587                     jsc.append("handler, null);");
588                     break;
589                 case XSType.IDREF_TYPE:
590                 case XSType.IDREFS_TYPE:
591                     //-- uses special code in UnmarshalHandler
592                     //-- see UnmarshalHandler#processIDREF
593                     jsc.add("desc.setMultivalued(");
594                     jsc.append("" + xmlNature.isMultivalued());
595                     jsc.append(");");
596                     break;
597                 default:
598                     break;
599             }
600         }
601     } //-- addSpecialHandlerLogic
602 
603     /**
604      * Creates the validation code for a given member. This code will be
605      * appended to the given JSourceCode.
606      *
607      * @param member the member for which to create the validation code.
608      * @param jsc the JSourceCode to fill in.
609      */
610     private static void addValidationCode(final FieldInfo member, final JSourceCode jsc) {
611         if (member == null || jsc == null) {
612             return;
613         }
614 
615         jsc.add("//-- validation code for: ");
616         jsc.append(member.getName());
617         String validator = member.getValidator();
618         if (validator != null && validator.length() > 0) {
619             jsc.add("fieldValidator = new " + validator + "();");
620         } else {
621             jsc.add("fieldValidator = new org.exolab.castor.xml.FieldValidator();");
622 
623             //-- a hack, I know, I will change later
624             if (member.getName().equals("_anyObject")) {
625                 jsc.add("desc.setValidator(fieldValidator);");
626                 return;
627             }
628 
629             XMLInfoNature xmlNature = new XMLInfoNature(member);
630             XSType xsType = xmlNature.getSchemaType();
631             //--handle collections
632             if (xsType.isCollection()) {
633                 XSListType xsList = (XSListType) xsType;
634 
635                 jsc.add("fieldValidator.setMinOccurs(");
636                 jsc.append(Integer.toString(xsList.getMinimumSize()));
637                 jsc.append(");");
638                 if (xsList.getMaximumSize() > 0) {
639                     jsc.add("fieldValidator.setMaxOccurs(");
640                     jsc.append(Integer.toString(xsList.getMaximumSize()));
641                     jsc.append(");");
642                 }
643             } else if (xmlNature.isRequired()) {
644                 jsc.add("fieldValidator.setMinOccurs(1);");
645             }
646 
647             jsc.add("{ //-- local scope");
648             jsc.indent();
649             xsType.validationCode(jsc, member.getFixedValue(), FIELD_VALIDATOR_NAME);
650             jsc.unindent();
651             jsc.add("}");
652         }
653         jsc.add("desc.setValidator(fieldValidator);");
654     }
655 
656     /**
657      * Returns the Class type (as a String) for the given XSType.
658      * @param jType the JType whose Class type will be returned
659      * @return the Class type (as a String) for the given XSType.
660      */
661     private static String classType(final JType jType) {
662         if (jType.isPrimitive()) {
663             JPrimitiveType primitive = (JPrimitiveType) jType;
664             return primitive.getWrapperName() + ".TYPE";
665         }
666         return jType.toString() + ".class";
667     } //-- classType
668 
669 } //-- DescriptorSourceFactory