View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 1999-2004 (C) Intalio Inc. All Rights Reserved.
32   *
33   * This file was originally developed by Keith Visco during the course of employment at Intalio Inc.
34   * Portions of this file developed by Keith Visco after Jan 19 2005 are Copyright (C) 2005 Keith
35   * Visco. All Rights Reserverd.
36   *
37   * $Id$
38   */
39  package org.exolab.castor.builder.factory;
40  
41  import java.util.ArrayList;
42  import java.util.Enumeration;
43  import java.util.Iterator;
44  import java.util.LinkedList;
45  import java.util.List;
46  
47  import org.apache.commons.lang3.StringUtils;
48  import org.castor.core.util.StringUtil;
49  import org.exolab.castor.builder.AnnotationBuilder;
50  import org.exolab.castor.builder.BuilderConfiguration;
51  import org.exolab.castor.builder.FactoryState;
52  import org.exolab.castor.builder.GroupNaming;
53  import org.exolab.castor.builder.SGStateInfo;
54  import org.exolab.castor.builder.SGTypes;
55  import org.exolab.castor.builder.SourceGenerator;
56  import org.exolab.castor.builder.SourceGeneratorConstants;
57  import org.exolab.castor.builder.TypeConversion;
58  import org.exolab.castor.builder.binding.ExtendedBinding;
59  import org.exolab.castor.builder.binding.XMLBindingComponent;
60  import org.exolab.castor.builder.info.ClassInfo;
61  import org.exolab.castor.builder.info.FieldInfo;
62  import org.exolab.castor.builder.info.GroupInfo;
63  import org.exolab.castor.builder.info.XMLInfo;
64  import org.exolab.castor.builder.info.nature.JDOClassInfoNature;
65  import org.exolab.castor.builder.info.nature.JDOFieldInfoNature;
66  import org.exolab.castor.builder.info.nature.SolrjFieldInfoNature;
67  import org.exolab.castor.builder.info.nature.XMLInfoNature;
68  import org.exolab.castor.builder.info.nature.relation.JDOOneToManyNature;
69  import org.exolab.castor.builder.info.nature.relation.JDOOneToOneNature;
70  import org.exolab.castor.builder.types.XSClass;
71  import org.exolab.castor.builder.types.XSString;
72  import org.exolab.castor.builder.types.XSType;
73  import org.exolab.castor.mapping.AccessMode;
74  import org.exolab.castor.xml.schema.Annotated;
75  import org.exolab.castor.xml.schema.Annotation;
76  import org.exolab.castor.xml.schema.AppInfo;
77  import org.exolab.castor.xml.schema.AppInfoJpaNature;
78  import org.exolab.castor.xml.schema.AppInfoSolrjNature;
79  import org.exolab.castor.xml.schema.AttributeDecl;
80  import org.exolab.castor.xml.schema.AttributeGroupDecl;
81  import org.exolab.castor.xml.schema.ComplexType;
82  import org.exolab.castor.xml.schema.ContentModelGroup;
83  import org.exolab.castor.xml.schema.ContentType;
84  import org.exolab.castor.xml.schema.Documentation;
85  import org.exolab.castor.xml.schema.ElementDecl;
86  import org.exolab.castor.xml.schema.Facet;
87  import org.exolab.castor.xml.schema.Group;
88  import org.exolab.castor.xml.schema.ModelGroup;
89  import org.exolab.castor.xml.schema.Order;
90  import org.exolab.castor.xml.schema.Particle;
91  import org.exolab.castor.xml.schema.Schema;
92  import org.exolab.castor.xml.schema.SimpleContent;
93  import org.exolab.castor.xml.schema.SimpleType;
94  import org.exolab.castor.xml.schema.SimpleTypesFactory;
95  import org.exolab.castor.xml.schema.Structure;
96  import org.exolab.castor.xml.schema.Wildcard;
97  import org.exolab.castor.xml.schema.XMLType;
98  import org.exolab.castor.xml.schema.annotations.jdo.Column;
99  import org.exolab.castor.xml.schema.annotations.jdo.OneToMany;
100 import org.exolab.castor.xml.schema.annotations.jdo.OneToOne;
101 import org.exolab.castor.xml.schema.annotations.jdo.PrimaryKey;
102 import org.exolab.castor.xml.schema.annotations.jdo.Table;
103 import org.exolab.castor.xml.schema.annotations.solrj.Field;
104 // import org.exolab.castor.xml.schema.annotations.solrj.Id;
105 import org.exolab.javasource.JAnnotation;
106 import org.exolab.javasource.JAnnotationType;
107 import org.exolab.javasource.JClass;
108 import org.exolab.javasource.JCollectionType;
109 import org.exolab.javasource.JConstructor;
110 import org.exolab.javasource.JDocComment;
111 import org.exolab.javasource.JDocDescriptor;
112 import org.exolab.javasource.JEnum;
113 import org.exolab.javasource.JField;
114 import org.exolab.javasource.JMethod;
115 import org.exolab.javasource.JParameter;
116 import org.exolab.javasource.JSourceCode;
117 import org.exolab.javasource.JType;
118 
119 /**
120  * Creates the Java Source classes for Schema components.
121  *
122  * @author <a href="mailto:keith AT kvisco DOT com">Keith Visco</a>
123  * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
124  * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
125  */
126 public final class SourceFactory extends BaseFactory {
127   private static final String ENUM_ACCESS_INTERFACE =
128       "org.exolab.castor.types.EnumeratedTypeAccess";
129 
130   private static final short BASE_TYPE_ENUMERATION = 0;
131   private static final short OBJECT_TYPE_ENUMERATION = 1;
132 
133   private static final String CLASS_METHOD_SUFFIX = "Class";
134   private static final String CLASS_KEYWORD = "class";
135   private static final String ITEM_NAME = "Item";
136 
137   /** The current Binding for which we are creating classes. */
138   private ExtendedBinding _binding = null;
139   /** The member factory. */
140   private MemberFactory _memberFactory = null;
141 
142   private short _enumerationType = OBJECT_TYPE_ENUMERATION;
143 
144   /**
145    * A flag indicating whether or not to generate XML marshaling framework specific methods.
146    */
147   private boolean _createMarshalMethods = true;
148 
149   /**
150    * A flag indicating whether or not to implement CastorTestable (used by the Castor Testing
151    * Framework).
152    */
153   private boolean _testable = false;
154   /** A flag indicating that SAX1 should be used when generating the source. */
155   private boolean _sax1 = false;
156   /** The TypeConversion instance to use for mapping SimpleTypes into XSTypes. */
157   private TypeConversion _typeConversion = null;
158   /** Enumeration factory used to create code for enumerations. */
159   private final EnumerationFactory _enumerationFactory;
160 
161   /**
162    * Creates a new SourceFactory with the given FieldInfoFactory.
163    *
164    * @param config the BuilderConfiguration instance (must not be null).
165    * @param infoFactory the FieldInfoFactory to use
166    * @param groupNaming Group naming scheme to be used.
167    * @param sourceGenerator the calling source generator.
168    */
169   public SourceFactory(final BuilderConfiguration config, final FieldInfoFactory infoFactory,
170       final GroupNaming groupNaming, final SourceGenerator sourceGenerator) {
171     super(config, infoFactory, groupNaming, sourceGenerator);
172 
173     // set the config into the info factory (CASTOR-1346)
174     infoFactory.setBoundProperties(config.boundPropertiesEnabled());
175 
176     this._memberFactory = new MemberFactory(config, infoFactory, getGroupNaming(), sourceGenerator);
177     this._typeConversion = new TypeConversion(getConfig());
178     this._enumerationFactory =
179         new EnumerationFactory(getConfig(), getGroupNaming(), sourceGenerator);
180   } // -- SourceFactory
181 
182   /**
183    * Sets whether or not to create the XML marshaling framework specific methods (marshal,
184    * unmarshal, validate) in the generated classes. By default, these methods are generated.
185    *
186    * @param createMarshalMethods a boolean, when true indicates to generated the marshaling
187    *        framework methods
188    *
189    */
190   public void setCreateMarshalMethods(final boolean createMarshalMethods) {
191     _createMarshalMethods = createMarshalMethods;
192   } // -- setCreateMarshalMethpds
193 
194   /**
195    * Sets whether or not to create extra collection methods for accessing the actual collection.
196    *
197    * @param extraMethods a boolean that when true indicates that extra collection accessor methods
198    *        should be created. False by default.
199    * @see org.exolab.castor.builder.SourceFactory#setReferenceMethodSuffix
200    */
201   public void setCreateExtraMethods(final boolean extraMethods) {
202     getInfoFactory().setCreateExtraMethods(extraMethods);
203   } // -- setCreateExtraMethods
204 
205   /**
206    * Sets the method suffix (ending) to use when creating the extra collection methods.
207    *
208    * @param suffix the method suffix to use when creating the extra collection methods. If null or
209    *        emtpty the default value, as specified in CollectionInfo will be used.
210    * @see org.exolab.castor.builder.SourceFactory#setCreateExtraMethods
211    */
212   public void setReferenceMethodSuffix(final String suffix) {
213     getInfoFactory().setReferenceMethodSuffix(suffix);
214   } // -- setReferenceMethodSuffix
215 
216   /**
217    * Sets whether or not to implement CastorTestable.
218    *
219    * @param testable if true, indicates to implement CastorTestable
220    */
221   public void setTestable(final boolean testable) {
222     _testable = testable;
223   }
224 
225   /**
226    * Sets to true if SAX1 should be used in the marshall method.
227    *
228    * @param sax1 true if SAX1 should be used.
229    */
230   public void setSAX1(final boolean sax1) {
231     _sax1 = sax1;
232   }
233 
234   /**
235    * Set to true if enumerated type lookups should be performed in a case insensitive manner.
236    *
237    * @param caseInsensitive when true
238    */
239   public void setCaseInsensitive(final boolean caseInsensitive) {
240     _enumerationFactory.setCaseInsensitive(caseInsensitive);
241   }
242 
243   // ------------------/
244   // - Public Methods -/
245   // ------------------/
246 
247   /**
248    * Creates a new ClassInfo for the given XMLBindingComponent.
249    *
250    * @param component the XMLBindingComponent that abstracts all XML Schema definition for a XML
251    *        Schema component.
252    * @param sgState The given state of the SourceGenerator.
253    * @return an array of JClasses reflecting the given XMLBindingComponent.
254    */
255   public JClass[] createSourceCode(final XMLBindingComponent component, final SGStateInfo sgState) {
256     if (component == null) {
257       throw new IllegalStateException("XMLBindingComponent may not be null.");
258     }
259     if (sgState == null) {
260       throw new IllegalStateException("SGStateInfo may not be null.");
261     }
262 
263     // -- check for previous JClass bindings
264     JClass[] classes = sgState.getSourceCode(component.getAnnotated());
265     if (classes != null) {
266       return classes;
267     }
268 
269     _binding = component.getBinding();
270 
271     if (sgState.verbose()) {
272       String name = component.getXMLName();
273       if (name == null) {
274         name = component.getJavaClassName();
275       }
276       String msg = "Creating classes for: " + name;
277       sgState.getDialog().notify(msg);
278     }
279 
280     // 0-- set the packageName
281     String packageName = component.getJavaPackage();
282     if (packageName == null || packageName.length() == 0) {
283       packageName = sgState.getPackageName();
284     }
285 
286     // 1-- get the name
287     // --if no package used then try to append the default package
288     // --used in the SourceGenerator
289     String className = component.getQualifiedName();
290     if (className.indexOf('.') == -1) {
291       // --be sure it is a valid className
292       className = getJavaNaming().toJavaClassName(className);
293       className = resolveClassName(className, packageName);
294     }
295 
296     // 2-- check if we have to create an Item class
297     boolean createGroupItem = component.createGroupItem();
298     if (createGroupItem) {
299       className += ITEM_NAME;
300       classes = new JClass[2];
301     } else {
302       classes = new JClass[1];
303     }
304 
305     // 3-- Create factoryState and chain it to sgState to prevent endless loop
306     FactoryState state = new FactoryState(className, sgState, packageName, component);
307     state.setCreateGroupItem(createGroupItem);
308     if (sgState.getCurrentFactoryState() != null) {
309       state.setParent(sgState.getCurrentFactoryState());
310     }
311     sgState.setCurrentFactoryState(state);
312 
313     // --Prevent endless loop
314     if (state.processed(component.getAnnotated())) {
315       return new JClass[0];
316     }
317 
318     // -- Mark the enclosed annotated structure as processed in the
319     // -- current FactoryState for preventing endless loop.
320     state.markAsProcessed(component.getAnnotated());
321 
322     //////////////////////////////////////////////////////
323     // NOTE: check that the component is not referring to
324     // an imported schema to prevent class creation
325     //////////////////////////////////////////////////////
326 
327     // 4-- intialization of the JClass
328     ClassInfo classInfo = state.getClassInfo();
329     JClass jClass = state.getJClass();
330     initialize(jClass);
331 
332     if (classInfo.hasNature(XMLInfoNature.class.getName())) {
333       final XMLInfoNature xmlNature = new XMLInfoNature(classInfo);
334 
335       // -- name information
336       xmlNature.setNodeName(component.getXMLName());
337 
338       // -- namespace information
339       xmlNature.setNamespaceURI(component.getTargetNamespace());
340 
341       // 5--processing the type
342       XMLType type = component.getXMLType();
343       boolean createForSingleGroup = false;
344       boolean creatingForAnElement =
345           (component.getAnnotated().getStructureType() == Structure.ELEMENT);
346 
347       // -- created from element definition information
348       xmlNature.setElementDefinition(creatingForAnElement);
349 
350       // deal with substitution groups
351       if (creatingForAnElement) {
352         ElementDecl elementDeclaration = (ElementDecl) component.getAnnotated();
353         Enumeration<ElementDecl> possibleSubstitutes =
354             elementDeclaration.getSubstitutionGroupMembers();
355         if (possibleSubstitutes.hasMoreElements()) {
356           List<String> substitutionGroupMembers = new ArrayList<String>();
357           while (possibleSubstitutes.hasMoreElements()) {
358             ElementDecl substitute = possibleSubstitutes.nextElement();
359             substitutionGroupMembers.add(substitute.getName());
360           }
361           xmlNature.setSubstitutionGroups(substitutionGroupMembers);
362         }
363       }
364 
365       if (type != null) {
366         if (type.isComplexType()) {
367           processComplexType(component, sgState, state);
368         } else if (type.isSimpleType()) {
369           SimpleType simpleType = (SimpleType) type;
370           // -- handle our special case for enumerated types
371           if (simpleType.hasFacet(Facet.ENUMERATION)) {
372             processSimpleTypeEnumeration(component, sgState, classInfo, simpleType);
373           } else {
374             //////////////////////////////////////////////////////////
375             // NOTE: generate sources if the flag for generating sources
376             // from imported schemas is on
377             //////////////////////////////////////////////////////////
378             return new JClass[0];
379           }
380         } else if (type.isAnyType()) {
381           // -- Do not create classes for AnyType
382           xmlNature.setSchemaType(new XSClass(SGTypes.OBJECT));
383           return new JClass[0];
384         }
385       } else {
386         // --no type we must be facing an XML schema group
387         // --MODEL GROUP OR GROUP
388         createForSingleGroup = processSchemaGroup(component, state, classInfo);
389       }
390 
391       // 6--createGroupItem
392       if (createGroupItem) {
393         // -- create Bound Properties code
394         if (component.hasBoundProperties()) {
395           createPropertyChangeMethods(jClass);
396         }
397 
398         sgState.bindReference(jClass, classInfo);
399 
400         classes[1] = jClass;
401 
402         // -- create main group class
403         String fname = component.getJavaClassName() + ITEM_NAME;
404         fname = getJavaNaming().toJavaMemberName(fname, false);
405 
406         FieldInfo fInfo = null;
407         if (createForSingleGroup) {
408           // By default a nested group Item can occur only once
409           fInfo = getInfoFactory().createFieldInfo(new XSClass(jClass), fname);
410         } else {
411           fInfo = getInfoFactory().createCollection(new XSClass(jClass), "_items", fname,
412               getJavaNaming(), getConfig().useJava50());
413         }
414         fInfo.setContainer(true);
415         String newClassName = className.substring(0, className.length() - 4);
416         state = new FactoryState(newClassName, sgState, packageName, component);
417         classInfo = state.getClassInfo();
418         jClass = state.getJClass();
419         initialize(jClass);
420         if (type != null && type.isComplexType()) {
421           ComplexType complexType = (ComplexType) type;
422           if (complexType.isTopLevel() ^ creatingForAnElement) {
423             // process attributes and content type since it has not be performed before
424             Annotated saved = component.getAnnotated();
425             processAttributes(component.getBinding(), complexType, state);
426             component.setView(saved);
427             if (complexType.getContentType() == ContentType.mixed) {
428               FieldInfo fieldInfo = _memberFactory.createFieldInfoForContent(component,
429                   new XSString(), getConfig().useJava50());
430               handleField(fieldInfo, state, component);
431             } else if (complexType.getContentType().getType() == ContentType.SIMPLE) {
432               SimpleContent simpleContent = (SimpleContent) complexType.getContentType();
433               SimpleType temp = simpleContent.getSimpleType();
434               XSType xsType =
435                   _typeConversion.convertType(temp, packageName, getConfig().useJava50());
436               FieldInfo fieldInfo = _memberFactory.createFieldInfoForContent(component, xsType,
437                   getConfig().useJava50());
438               handleField(fieldInfo, state, component);
439               temp = null;
440             } else {
441               // handle multi-valued choice group
442               xmlNature.setSchemaType(new XSClass(jClass));
443             }
444           }
445         }
446 
447         classInfo.addFieldInfo(fInfo);
448         fInfo.getMemberAndAccessorFactory().createJavaField(fInfo, jClass);
449         fInfo.getMemberAndAccessorFactory().createAccessMethods(fInfo, jClass,
450             getConfig().useJava50(), getConfig().getAnnotationBuilders());
451         fInfo.getMemberAndAccessorFactory().generateInitializerCode(fInfo,
452             jClass.getConstructor(0).getSourceCode());
453 
454         // -- name information
455         XMLInfoNature xmlInfoNature = new XMLInfoNature(classInfo);
456         xmlInfoNature.setNodeName(component.getXMLName());
457 
458         // -- mark as a container
459         xmlInfoNature.setContainer(true);
460 
461         // -- if we have a superclass, make sure that the actual type extends it, not the
462         // xxxItem holder class.
463         String actSuperClass = classes[1].getSuperClassQualifiedName();
464         jClass.setSuperClass(actSuperClass);
465         classes[1].setSuperClass(null);
466       }
467 
468     }
469 
470     classes[0] = jClass;
471 
472     // 7--set the class information given the component information
473     // --base class
474     String baseClass = component.getExtends();
475     if (baseClass != null && baseClass.length() > 0) {
476       // -- at this point if a base class has been set
477       // -- it means that it is a class generated for an element
478       // -- that extends a class generated for a complexType. Thus
479       // -- no change is possible
480       if (jClass.getSuperClassQualifiedName() == null) {
481         jClass.setSuperClass(baseClass);
482       }
483     }
484 
485     // --interface implemented
486     String[] implemented = component.getImplements();
487     if (implemented != null) {
488       for (String interfaceName : implemented) {
489         if ((interfaceName != null) && (interfaceName.length() > 0)) {
490           jClass.addInterface(interfaceName);
491         }
492       }
493     }
494 
495     // --final
496     jClass.getModifiers().setFinal(component.isFinal());
497 
498     // --abstract
499     if (component.isAbstract()) {
500       jClass.getModifiers().setAbstract(true);
501       classInfo.setAbstract(true);
502     }
503 
504     processAppInfo(component.getAnnotated(), classInfo);
505     extractAnnotations(component.getAnnotated(), jClass);
506 
507     createContructorForDefaultValueForSimpleContent(component.getAnnotated(), classInfo, sgState);
508     makeMethods(component, sgState, state, jClass, baseClass);
509 
510     if (classInfo.hasNature(JDOClassInfoNature.class.getName())) {
511       JDOClassInfoNature jdoNature = new JDOClassInfoNature(classInfo);
512       if (jdoNature.getDetachable()) {
513         createJdoTimestampImplementations(jClass);
514       }
515     }
516 
517     sgState.bindReference(jClass, classInfo);
518     sgState.bindReference(component.getAnnotated(), classInfo);
519 
520     // -- Save source code bindings to prevent duplicate code generation
521     sgState.bindSourceCode(component.getAnnotated(), classes);
522 
523     // custom annotations
524     AnnotationBuilder[] annotationBuilders = getConfig().getAnnotationBuilders();
525     for (AnnotationBuilder annotationBuilder : annotationBuilders) {
526       annotationBuilder.addClassAnnotations(classInfo, jClass);
527     }
528 
529     return classes;
530   }
531 
532   /**
533    * This method adds a contructor with a string parameter to set the default value of a simple
534    * content. If the provided class is not a Simple Content class, nothing is done.
535    *
536    * @param annotated type information for the class to potentially create a constructor for
537    * @param classInfo the ClassInfo for the class to potentially create a constructor for
538    */
539   private void createContructorForDefaultValueForSimpleContent(final Annotated annotated,
540       final ClassInfo classInfo, final SGStateInfo sgStateInfo) {
541     FieldInfo textFieldInfo = classInfo.getTextField();
542 
543     boolean generate = false;
544     boolean inherited = false;
545 
546     // check if there is some field inherited from a type
547     if (annotated instanceof ElementDecl) {
548       XMLType type = ((ElementDecl) annotated).getType();
549       ClassInfo typeInfo = sgStateInfo.resolve(type);
550       if (typeInfo != null && typeInfo.getTextField() != null) {
551         textFieldInfo = typeInfo.getTextField();
552         inherited = true;
553       }
554       generate = (type.isComplexType() && ((ComplexType) type).isSimpleContent());
555     }
556 
557     // check if we are a complexType ourself
558     else if (annotated instanceof ComplexType && ((ComplexType) annotated).isSimpleContent()) {
559       generate = true;
560     }
561 
562 
563     // discard primitiv types and collections
564     if (textFieldInfo != null) {
565       XSType textFieldType = new XMLInfoNature(textFieldInfo).getSchemaType();
566       if (textFieldType != null && textFieldType.getJType().isArray()) {
567         generate = false;
568       }
569     }
570 
571     if (!generate) {
572       return;
573     }
574 
575     XMLInfoNature xmlNature = new XMLInfoNature(textFieldInfo);
576 
577     // create constructor
578     JClass jClass = classInfo.getJClass();
579     JParameter parameter = new JParameter(new JClass("java.lang.String"), "defaultValue");
580     JConstructor constructor = jClass.createConstructor(new JParameter[] {parameter});
581     JSourceCode sourceCode = new JSourceCode();
582 
583     if (inherited) {
584       sourceCode.add("super(defaultValue);");
585     } else {
586       sourceCode.add("try {");
587       String defaultValue = xmlNature.getSchemaType().createDefaultValueWithString("defaultValue");
588       sourceCode.addIndented("setContent(" + defaultValue + ");");
589       sourceCode.add(" } catch(Exception e) {");
590       sourceCode.addIndented(
591           "throw new RuntimeException(\"Unable to cast default value for simple content!\");");
592       sourceCode.add(" } ");
593     }
594 
595     constructor.setSourceCode(sourceCode);
596     jClass.addConstructor(constructor);
597   }
598 
599   /**
600    * Extract 'documentation' annotations from the {@link Annotated} instance given.
601    * 
602    * @param annotated {@link Annotated} instance to extract annotations from.
603    * @param jClass {@link JClass} instance to inject annotations into.
604    */
605   private void extractAnnotations(final Annotated annotated, final JClass jClass) {
606     // -- process annotation
607     String comment = extractCommentsFromAnnotations(annotated);
608     if (comment != null) {
609       jClass.getJDocComment().setComment(comment);
610 
611       if (getConfig().generateExtraDocumentationMethods()) {
612         generateExtraDocumentationMethods(annotated, jClass);
613       }
614     }
615   }
616 
617   /**
618    * Creates the #getXmlSchemaDocumentation methods for the given JClass.
619    * 
620    * @param annotated The {@link Annotation} instance to extract the XML schema documentation
621    *        instances from.
622    * @param parent the JClass to create the #getXmlSchemaDocumentation() methods for
623    */
624   private void generateExtraDocumentationMethods(final Annotated annotated, final JClass jClass) {
625     JField documentationsField =
626         new JField(new JClass("java.util.Map"), "_xmlSchemaDocumentations");
627     documentationsField.setComment("The content of the <xsd:documentation> elements");
628     documentationsField.setInitString("new java.util.HashMap()");
629     jClass.addMember(documentationsField);
630 
631     Enumeration<Annotation> annotations = annotated.getAnnotations();
632     while (annotations.hasMoreElements()) {
633       Annotation annotation = annotations.nextElement();
634       Enumeration<Documentation> documentations = annotation.getDocumentation();
635       while (documentations.hasMoreElements()) {
636         Documentation documentation = documentations.nextElement();
637         JConstructor defaultConstructor = jClass.getConstructor(0);
638         String documentationContent = normalize(documentation.getContent());
639         documentationContent = StringUtil.replaceAll(documentationContent, "\n", "\"\n+ \" ");
640         defaultConstructor.getSourceCode().add("_xmlSchemaDocumentations.put(\""
641             + documentation.getSource() + "\", \"" + documentationContent + "\");");
642       }
643     }
644 
645     JMethod aMethod = new JMethod("getXmlSchemaDocumentations", new JClass("java.util.Map"),
646         " A collection of documentation elements.");
647     JSourceCode sourceCode = aMethod.getSourceCode();
648     sourceCode.add("return _xmlSchemaDocumentations;");
649     jClass.addMethod(aMethod);
650 
651     JMethod anotherMethod = new JMethod("getXmlSchemaDocumentation", new JClass("java.lang.String"),
652         " A specific XML schema documentation element.");
653     JParameter parameter = new JParameter(new JClass("java.lang.String"), "source");
654     anotherMethod.addParameter(parameter);
655     sourceCode = anotherMethod.getSourceCode();
656     sourceCode.add("return (java.lang.String) _xmlSchemaDocumentations.get(source);");
657     jClass.addMethod(anotherMethod);
658   }
659 
660   /**
661    * Generate methods for our class.
662    *
663    * @param component
664    * @param sgState
665    * @param state
666    * @param jClass
667    * @param baseClass
668    */
669   private void makeMethods(final XMLBindingComponent component, final SGStateInfo sgState,
670       final FactoryState state, final JClass jClass, final String baseClass) {
671     // NOTE: be careful with the derivation stuff when generating bounds properties
672 
673     if (_createMarshalMethods) {
674       // -- #validate()
675       createValidateMethods(jClass);
676       // --don't generate marshal/unmarshal methods
677       // --for abstract classes
678       if (!component.isAbstract()) {
679         // -- #marshal()
680         createMarshalMethods(jClass);
681         // -- #unmarshal()
682         createUnmarshalMethods(jClass, sgState);
683       }
684     }
685 
686     // create equals() method?
687     if (component.hasEquals()) {
688       createEqualsMethod(jClass);
689       createHashCodeMethod(jClass);
690     }
691 
692     // implements CastorTestable?
693     if (_testable) {
694       createTestableMethods(jClass, state);
695     }
696 
697     // -- This boolean is set to create bound properties
698     // -- even if the user has set the SUPER CLASS property
699     String superclassQualifiedName = jClass.getSuperClassQualifiedName();
700     if (superclassQualifiedName == null || superclassQualifiedName.equals(baseClass)) {
701       // -- create Bound Properties code
702       if (component.hasBoundProperties()) {
703         createPropertyChangeMethods(jClass);
704       }
705     }
706   }
707 
708   private boolean processSchemaGroup(final XMLBindingComponent component, final FactoryState state,
709       final ClassInfo classInfo) {
710     try {
711       Group group = (Group) component.getAnnotated();
712       processContentModel(group, state);
713       component.setView(group);
714 
715       // -- Check Group Type
716       Order order = group.getOrder();
717       GroupInfo groupInfo = new XMLInfoNature(classInfo).getGroupInfo();
718       if (order == Order.choice) {
719         groupInfo.setAsChoice();
720       } else if (order == Order.sequence) {
721         groupInfo.setAsSequence();
722       } else {
723         groupInfo.setAsAll();
724       }
725 
726       return group.getMaxOccurs() == 1;
727     } catch (ClassCastException ce) {
728       // --Should not happen
729       throw new IllegalArgumentException("Illegal binding component: " + ce.getMessage());
730     }
731   }
732 
733   private void processSimpleTypeEnumeration(final XMLBindingComponent component,
734       final SGStateInfo sgState, final ClassInfo classInfo, final SimpleType simpleType) {
735     // -- Don't create source code for simple types that
736     // -- don't belong in the elements target namespace
737     String tns = simpleType.getSchema().getTargetNamespace();
738     boolean create = false;
739     if (tns == null) {
740       create = (component.getTargetNamespace() == null);
741     } else {
742       create = tns.equals(component.getTargetNamespace());
743     }
744 
745     if (create) {
746       ClassInfo tmpInfo = sgState.resolve(simpleType);
747       JClass tmpClass = null;
748       if (tmpInfo != null) {
749         tmpClass = tmpInfo.getJClass();
750       } else {
751         tmpClass = createSourceCode(component.getBinding(), simpleType, sgState);
752       }
753       XMLInfoNature xmlNature = new XMLInfoNature(classInfo);
754       xmlNature.setSchemaType(new XSClass(tmpClass));
755     }
756   }
757 
758   private void processComplexType(final XMLBindingComponent component, final SGStateInfo sgState,
759       final FactoryState state) {
760     XMLType type = component.getXMLType();
761     ClassInfo classInfo = state.getClassInfo();
762     JClass jClass = state.getJClass();
763     boolean creatingForAnElement =
764         (component.getAnnotated().getStructureType() == Structure.ELEMENT);
765 
766     ComplexType complexType = (ComplexType) type;
767     if (complexType.isTopLevel() && creatingForAnElement) {
768       // --move the view and keep the structure
769       Annotated saved = component.getAnnotated();
770       String previousPackage = component.getJavaPackage();
771       XMLBindingComponent baseComponent = new XMLBindingComponent(getConfig(), getGroupNaming());
772       baseComponent.setBinding(component.getBinding());
773       baseComponent.setView(complexType);
774       // -- call createSourceCode to pre-process the complexType
775       createSourceCode(baseComponent, sgState);
776       String baseClassName = null;
777       String basePackage = baseComponent.getJavaPackage();
778       // --if the base class is not in the same package
779       // --of the current class then we have to qualify the base
780       // --class
781       if (basePackage != null && !basePackage.equals(previousPackage)) {
782         baseClassName = baseComponent.getQualifiedName();
783         if (baseClassName.indexOf('.') == -1) {
784           // --be sure it is a valid className
785           baseClassName = getJavaNaming().toJavaClassName(baseClassName);
786         }
787       } else {
788         baseClassName = baseComponent.getJavaClassName();
789       }
790       jClass.setSuperClass(baseClassName);
791       basePackage = null;
792       baseClassName = null;
793       component.setView(saved);
794       saved = null;
795     } else if (complexType.isTopLevel() || creatingForAnElement) {
796       // generate class if the complexType is anonymous and we create classes
797       // for an element OR if the complexType is top-level and we create
798       // classes for it.
799 
800       // -- check Group type
801       if (complexType.getParticleCount() == 1) {
802         Particle particle = complexType.getParticle(0);
803         if (particle.getStructureType() == Structure.GROUP) {
804           Group group = (Group) particle;
805           if (group.getOrder() == Order.choice) {
806             new XMLInfoNature(classInfo).getGroupInfo().setAsChoice();
807           }
808         }
809       }
810       Annotated saved = component.getAnnotated();
811       processComplexType(complexType, state);
812       component.setView(saved);
813       saved = null;
814     }
815   }
816 
817   /**
818    * Creates the Java source code to support the given Simpletype.
819    *
820    * @param binding Current XML binding
821    * @param simpleType the Simpletype to create the Java source for
822    * @param sgState the current SGStateInfo (cannot be null).
823    * @return the JClass representation of the given Simpletype
824    */
825   public JClass createSourceCode(final ExtendedBinding binding, final SimpleType simpleType,
826       final SGStateInfo sgState) {
827     if (SimpleTypesFactory.isBuiltInType(simpleType.getTypeCode())) {
828       String err = "You cannot construct a ClassInfo for a built-in SimpleType.";
829       throw new IllegalArgumentException(err);
830     }
831     if (sgState == null) {
832       throw new IllegalArgumentException("SGStateInfo cannot be null.");
833     }
834 
835     // -- Unions are currently processed as the built-in
836     // -- basetype for the member types of the Union, so
837     // -- do nothing for now...however we can warn
838     // -- user that no validation will be peformed on the
839     // -- union
840     if (simpleType.getStructureType() == Structure.UNION) {
841       if (!sgState.getSuppressNonFatalWarnings()) {
842         String message = "warning: support for unions is incomplete.";
843         sgState.getDialog().notify(message);
844       }
845       return null;
846     }
847 
848     ClassInfo cInfo = sgState.resolve(simpleType);
849     if (cInfo != null) {
850       return cInfo.getJClass();
851     }
852 
853     boolean enumeration = false;
854 
855     // -- class name information
856     String typeName = simpleType.getName();
857     if (typeName == null) {
858       Structure struct = simpleType.getParent();
859       FactoryState fstate = null;
860       switch (struct.getStructureType()) {
861         case Structure.ATTRIBUTE:
862           typeName = ((AttributeDecl) struct).getName();
863           fstate = sgState.getCurrentFactoryState();
864           break;
865         case Structure.ELEMENT:
866           typeName = ((ElementDecl) struct).getName();
867           break;
868         default:
869           // Nothing to do
870           break;
871       }
872       // -- In case of naming collision we append current class name
873       if (fstate != null) {
874         typeName = getJavaNaming().toJavaClassName(typeName);
875         Structure attrDeclParent = ((AttributeDecl) struct).getParent();
876         if (attrDeclParent != null
877             && attrDeclParent.getStructureType() == Structure.ATTRIBUTE_GROUP) {
878           typeName = getJavaNaming()
879               .toJavaClassName(((AttributeGroupDecl) attrDeclParent).getName() + typeName);
880         } else {
881           typeName = fstate.getJClass().getLocalName() + typeName;
882         }
883       }
884       // -- otherwise (???) just append "Type"
885       typeName += "Type";
886     }
887 
888     String className = getJavaNaming().toJavaClassName(typeName);
889 
890     // --XMLBindingComponent is only used to retrieve the java package
891     // -- we need to optimize it by enabling the binding of simpleTypes.
892     XMLBindingComponent comp = new XMLBindingComponent(getConfig(), getGroupNaming());
893     if (binding != null) {
894       comp.setBinding(binding);
895     }
896 
897     // set component view for anonymous simple types to parent
898     if (simpleType.getName() == null) {
899       Annotated annotated = (Annotated) simpleType.getParent();
900       comp.setView(annotated);
901     } else {
902       comp.setView(simpleType);
903     }
904 
905     String packageName = comp.getJavaPackage();
906     if ((packageName == null) || (packageName.length() == 0)) {
907       packageName = sgState.getPackageName();
908     }
909 
910     // reset component view for anonymous simple types
911     if (simpleType.getName() == null) {
912       comp.setView(simpleType);
913     }
914 
915     if (simpleType.hasFacet(Facet.ENUMERATION)) {
916       enumeration = true;
917       // Fix packageName
918       // TODO this is a hack I know, we should change this
919       if ((packageName != null) && (packageName.length() > 0)) {
920         packageName = packageName + "." + SourceGeneratorConstants.TYPES_PACKAGE;
921       } else {
922         packageName = SourceGeneratorConstants.TYPES_PACKAGE;
923       }
924     }
925 
926     String boundClassName = comp.getJavaClassName();
927     if ((boundClassName != null) && (boundClassName.length() > 0)) {
928       className = boundClassName;
929       typeName = boundClassName;
930     }
931 
932     className = resolveClassName(className, packageName);
933 
934     FactoryState state = new FactoryState(className, sgState, packageName, comp,
935         (enumeration && getConfig().useJava5Enums()));
936 
937     state.setParent(sgState.getCurrentFactoryState());
938 
939     ClassInfo classInfo = state.getClassInfo();
940     JClass jClass = state.getJClass();
941 
942     initialize(jClass);
943 
944     // -- XML information
945     Schema schema = simpleType.getSchema();
946     XMLInfoNature xmlNature = new XMLInfoNature(classInfo);
947     xmlNature.setNamespaceURI(schema.getTargetNamespace());
948     xmlNature.setNodeName(typeName);
949 
950     extractAnnotations(simpleType, jClass);
951 
952     XSClass xsClass = new XSClass(jClass, typeName);
953 
954     xmlNature.setSchemaType(xsClass);
955 
956     // -- handle enumerated types
957     if (enumeration) {
958       xsClass.setAsEnumerated(true);
959       processEnumeration(binding, simpleType, state);
960     }
961 
962     // -- create Bound Properties code
963     if (state.hasBoundProperties() && !enumeration) {
964       createPropertyChangeMethods(jClass);
965     }
966 
967     if (classInfo.hasNature(JDOClassInfoNature.class.getName())) {
968       JDOClassInfoNature jdoNature = new JDOClassInfoNature(classInfo);
969       if (jdoNature.getDetachable()) {
970         createJdoTimestampImplementations(jClass);
971       }
972     }
973 
974     sgState.bindReference(jClass, classInfo);
975     sgState.bindReference(simpleType, classInfo);
976 
977     return jClass;
978   }
979 
980   private void createJdoTimestampImplementations(final JClass jClass) {
981 
982     jClass.addInterface("org.exolab.castor.jdo.TimeStampable");
983 
984     JField jdoTimestamp = new JField(JType.LONG, "_jdoTimeStamp");
985     jClass.addField(jdoTimestamp);
986 
987     JMethod getTSMethod =
988         new JMethod("jdoGetTimeStamp", JType.LONG, "returns the current time stamp");
989     JSourceCode getSourceCode = getTSMethod.getSourceCode();
990     getSourceCode.addIndented("return _jdoTimeStamp;");
991     jClass.addMethod(getTSMethod);
992 
993     JMethod setTSMethod = new JMethod("jdoSetTimeStamp");
994     JParameter parameter = new JParameter(JType.LONG, "jdoTimeStamp");
995     setTSMethod.addParameter(parameter);
996     JSourceCode setSourceCode = setTSMethod.getSourceCode();
997     setSourceCode.addIndented("this._jdoTimeStamp = jdoTimeStamp;");
998     jClass.addMethod(setTSMethod);
999   }
1000 
1001 
1002   // -------------------/
1003   // - Private Methods -/
1004   // -------------------/
1005 
1006   /**
1007    * Initializes the given JClass.
1008    * 
1009    * @param jClass the JClass to initialize
1010    */
1011   private void initialize(final JClass jClass) {
1012     jClass.addInterface("java.io.Serializable");
1013 
1014     if (getConfig().useJava50()) {
1015       JAnnotation serial = new JAnnotation(new JAnnotationType("SuppressWarnings"));
1016       serial.setValue(new String[] {"\"serial\""});
1017       jClass.addAnnotation(serial);
1018     }
1019 
1020     // -- add default constructor
1021     JConstructor con = jClass.createConstructor();
1022     jClass.addConstructor(con);
1023     con.getSourceCode().add("super();");
1024   } // -- initialize
1025 
1026   /**
1027    * Creates the #marshal methods for the given JClass.
1028    * 
1029    * @param parent the JClass to create the #marshal methods for
1030    */
1031   private void createPropertyChangeMethods(final JClass parent) {
1032     // -- add vector to hold listeners
1033     String vName = "propertyChangeSupport";
1034     JField field = new JField(SGTypes.PROPERTY_CHANGE_SUPPORT, vName);
1035     field.getModifiers().makePrivate();
1036     parent.addField(field);
1037 
1038     // ---------------------------------/
1039     // - notifyPropertyChangeListeners -/
1040     // ---------------------------------/
1041 
1042     JMethod jMethod = new JMethod("notifyPropertyChangeListeners");
1043     jMethod.getModifiers().makeProtected();
1044 
1045     JDocComment jdc = jMethod.getJDocComment();
1046     JDocDescriptor jdDesc = null;
1047     String desc = null;
1048 
1049     desc = "Notifies all registered PropertyChangeListeners "
1050         + "when a bound property's value changes.";
1051     jdc.appendComment(desc);
1052 
1053     jMethod.addParameter(new JParameter(SGTypes.STRING, "fieldName"));
1054     jdDesc = jdc.getParamDescriptor("fieldName");
1055     jdDesc.setDescription("the name of the property that has changed.");
1056 
1057     jMethod.addParameter(new JParameter(SGTypes.OBJECT, "oldValue"));
1058     jdDesc = jdc.getParamDescriptor("oldValue");
1059     jdDesc.setDescription("the old value of the property.");
1060 
1061     jMethod.addParameter(new JParameter(SGTypes.OBJECT, "newValue"));
1062     jdDesc = jdc.getParamDescriptor("newValue");
1063     jdDesc.setDescription("the new value of the property.");
1064 
1065     parent.addMethod(jMethod);
1066     JSourceCode jsc = jMethod.getSourceCode();
1067     // --fix for bug 1026
1068     jsc.add("if (");
1069     jsc.append(vName);
1070     jsc.append(" == null) return;");
1071 
1072     jsc.add(vName);
1073     jsc.append(".firePropertyChange(fieldName,oldValue,newValue);");
1074 
1075     // -----------------------------/
1076     // - addPropertyChangeListener -/
1077     // -----------------------------/
1078 
1079     JType jType = new JClass("java.beans.PropertyChangeListener");
1080     jMethod = new JMethod("addPropertyChangeListener");
1081 
1082     desc = "Registers a PropertyChangeListener with this class.";
1083     jdc = jMethod.getJDocComment();
1084     jdc.appendComment(desc);
1085 
1086     jMethod.addParameter(new JParameter(jType, "pcl"));
1087     desc = "The PropertyChangeListener to register.";
1088     jdDesc = jdc.getParamDescriptor("pcl");
1089     jdDesc.setDescription(desc);
1090 
1091     parent.addMethod(jMethod);
1092 
1093     jsc = jMethod.getSourceCode();
1094 
1095     jsc.add("if (");
1096     jsc.append(vName);
1097     jsc.append(" == null) {");
1098     jsc.addIndented(vName + " = new java.beans.PropertyChangeSupport(this);");
1099     jsc.add("}");
1100     jsc.add(vName);
1101     jsc.append(".addPropertyChangeListener(pcl);");
1102 
1103     // --------------------------------/
1104     // - removePropertyChangeListener -/
1105     // --------------------------------/
1106 
1107     jMethod = new JMethod("removePropertyChangeListener", JType.BOOLEAN,
1108         "always returns true if pcl != null");
1109 
1110     desc = "Removes the given PropertyChangeListener "
1111         + "from this classes list of ProperyChangeListeners.";
1112     jdc = jMethod.getJDocComment();
1113     jdc.appendComment(desc);
1114 
1115     jMethod.addParameter(new JParameter(jType, "pcl"));
1116     desc = "The PropertyChangeListener to remove.";
1117     jdDesc = jdc.getParamDescriptor("pcl");
1118     jdDesc.setDescription(desc);
1119 
1120     parent.addMethod(jMethod);
1121 
1122     jsc = jMethod.getSourceCode();
1123     jsc.add("if (");
1124     jsc.append(vName);
1125     jsc.append(" == null) return false;");
1126 
1127     jsc.add(vName);
1128     jsc.append(".removePropertyChangeListener(pcl);");
1129     jsc.add("return true;");
1130   } // -- createPropertyChangeMethods
1131 
1132   /**
1133    * Creates the #marshal methods for the given JClass.
1134    * 
1135    * @param parent the JClass to create the #marshal methods for
1136    */
1137   private void createMarshalMethods(final JClass parent) {
1138     createMarshalMethods(parent, false);
1139   } // -- createMarshalMethods
1140 
1141   /**
1142    * Creates the #marshal methods for the given JClass.
1143    * 
1144    * @param parent the JClass to create the #marshal methods for
1145    * @param isAbstract true if the generated Class should be marked abstract
1146    */
1147   private void createMarshalMethods(final JClass parent, final boolean isAbstract) {
1148     // -- create main marshal method
1149     JMethod jMethod = new JMethod("marshal");
1150     jMethod.addException(SGTypes.MARSHAL_EXCEPTION,
1151         "if object is null or if any SAXException is thrown during marshaling");
1152     jMethod.addException(SGTypes.VALIDATION_EXCEPTION,
1153         "if this object is an invalid instance according to the schema");
1154     jMethod.addParameter(new JParameter(SGTypes.WRITER, "out"));
1155 
1156     // if (_config.useJava50()) {
1157     // jMethod.addAnnotation(new JAnnotation(new JAnnotationType("Override")));
1158     // }
1159 
1160     parent.addMethod(jMethod);
1161 
1162     if (isAbstract) {
1163       jMethod.getModifiers().setAbstract(true);
1164     } else {
1165       JSourceCode jsc = jMethod.getSourceCode();
1166       jsc.add("org.exolab.castor.xml.Marshaller.marshal(this, out);");
1167     }
1168 
1169     // -- create helper marshal method
1170     // -- start helper marshal method, this method will
1171     // -- be built up as we process the given ElementDecl
1172     jMethod = new JMethod("marshal");
1173     JClass jc = null;
1174     if (_sax1) {
1175       jc = new JClass("org.xml.sax.DocumentHandler");
1176     } else {
1177       jc = new JClass("org.xml.sax.ContentHandler");
1178       jMethod.addException(SGTypes.IO_EXCEPTION, "if an IOException occurs during marshaling");
1179     }
1180     jMethod.addException(SGTypes.MARSHAL_EXCEPTION,
1181         "if object is null or if any SAXException is thrown during marshaling");
1182     jMethod.addException(SGTypes.VALIDATION_EXCEPTION,
1183         "if this object is an invalid instance according to the schema");
1184     jMethod.addParameter(new JParameter(jc, "handler"));
1185     parent.addMethod(jMethod);
1186 
1187     if (isAbstract) {
1188       jMethod.getModifiers().setAbstract(true);
1189     } else {
1190       JSourceCode jsc = jMethod.getSourceCode();
1191       jsc.add("org.exolab.castor.xml.Marshaller.marshal(this, handler);");
1192     }
1193   } // -- createMarshalMethods
1194 
1195   private void createUnmarshalMethods(final JClass parent, final SGStateInfo sgState) {
1196     // -- mangle method name to avoid compiler errors when this class is extended
1197     String methodName = "unmarshal";
1198     if (sgState.getSourceGenerator().mappingSchemaType2Java()) {
1199       methodName += parent.getLocalName();
1200     }
1201 
1202     // -- create main unmarshal method
1203 
1204     // -- search for proper base class
1205     // TODO[WG]: java 5.0 allows different types for unmarshal method in extension hierarchy
1206     JClass returnType;
1207     if (!getConfig().useJava50()) {
1208       returnType = findBaseClass(parent, sgState);
1209     } else {
1210       returnType = parent;
1211     }
1212 
1213     JMethod jMethod = new JMethod(methodName, returnType, "the unmarshaled " + returnType);
1214     jMethod.getModifiers().setStatic(true);
1215     jMethod.addException(SGTypes.MARSHAL_EXCEPTION,
1216         "if object is null or if any SAXException is thrown during marshaling");
1217     jMethod.addException(SGTypes.VALIDATION_EXCEPTION,
1218         "if this object is an invalid instance according to the schema");
1219     jMethod.addParameter(new JParameter(SGTypes.READER, "reader"));
1220     parent.addMethod(jMethod);
1221 
1222     JSourceCode jsc = jMethod.getSourceCode();
1223     jsc.add("return (");
1224     jsc.append(returnType.getName());
1225     jsc.append(") org.exolab.castor.xml.Unmarshaller.unmarshal(");
1226     jsc.append(parent.getName());
1227     jsc.append(".class, reader);");
1228   } // -- createUnmarshalMethods
1229 
1230   /**
1231    * Returns the base class (as found in the schema) of the provided class. Climbs the inheritence
1232    * tree of the provided class to find and return the base class of the provided class.
1233    *
1234    * @param jClass class to find the base class of
1235    * @param sgState current state of source generation
1236    * @return the base class of the provided class.
1237    */
1238   private JClass findBaseClass(final JClass jClass, final SGStateInfo sgState) {
1239     JClass returnType = jClass;
1240 
1241     List<JClass> classes = new LinkedList<JClass>();
1242     classes.add(returnType);
1243 
1244     while (returnType.getSuperClassQualifiedName() != null) {
1245       String superClassName = returnType.getSuperClassQualifiedName();
1246       JClass superClass = sgState.getSourceCode(superClassName);
1247       if (superClass == null) {
1248         superClass = sgState.getImportedSourceCode(superClassName);
1249       }
1250 
1251       // A binding can cause us to have to look for the superclass class in
1252       // the package of the current class
1253       if (superClass == null && superClassName.indexOf('.') < 0) {
1254         String pkgName = returnType.getPackageName();
1255         if (pkgName != null && pkgName.length() > 0) {
1256           superClassName = pkgName + "." + superClassName;
1257           superClass = sgState.getSourceCode(superClassName);
1258         }
1259       }
1260 
1261       // If returnClass has no superclass then it is the base class
1262       if (superClass == null) {
1263         break;
1264       }
1265 
1266       // Prevent inheritance loops from causing infinite loops
1267       if (classes.contains(superClass)) {
1268         StringBuilder buffer = new StringBuilder(64);
1269         buffer.append("Loop found in class hierarchy: ");
1270         for (Iterator<JClass> i = classes.iterator(); i.hasNext();) {
1271           JClass element = i.next();
1272           // If JClass told us the source of the class (ComplexType, Element, ...
1273           // then we could report that to and make name conflicts more obvious.
1274           buffer.append(element.getName());
1275           buffer.append(" -> ");
1276         }
1277         buffer.append(superClass.getName());
1278         sgState.getDialog().notify(buffer.toString());
1279         // FIXME We should probably throw an exception here
1280         break;
1281       }
1282 
1283       classes.add(superClass);
1284       returnType = superClass;
1285     }
1286 
1287     classes.clear();
1288     return returnType;
1289   }
1290 
1291   /**
1292    * Create an "hashCode" method on the given JClass.
1293    *
1294    * @param jclass the JClass in wich we create the hashCode method.
1295    */
1296   public void createHashCodeMethod(final JClass jclass) {
1297     if (jclass == null) {
1298       throw new IllegalArgumentException("JClass must not be null");
1299     }
1300 
1301     // The argument is not null
1302     JField[] fields = jclass.getFields();
1303 
1304     // Creates the method signature
1305     JMethod jMethod = new JMethod("hashCode", JType.INT, "a hash code value for the object.");
1306     jMethod.setComment("Overrides the java.lang.Object.hashCode method.\n" + "<p>\n"
1307         + "The following steps came from " + "<b>Effective Java Programming Language Guide</b> "
1308         + "by Joshua Bloch, Chapter 3");
1309 
1310     // The hashCode method has no arguments
1311     jclass.addMethod(jMethod);
1312 
1313     JSourceCode jsc = jMethod.getSourceCode();
1314     if (jclass.getSuperClassQualifiedName() == null) {
1315       jsc.add("int resultHc = 17;");
1316     } else {
1317       jsc.add("int resultHc = super.hashCode();");
1318     }
1319     jsc.add("");
1320     jsc.add("long tmp;");
1321 
1322     for (JField temp : fields) {
1323       // If the field is an object the hashCode method is called recursively
1324 
1325       JType type = temp.getType();
1326       String name = temp.getName();
1327       if (type.isPrimitive()) {
1328         if (type == JType.BOOLEAN) {
1329           // Skip the _has_* variables only if they represent
1330           // a primitive that may or may not be present
1331           if (!name.startsWith("_has_") || jclass.getField(name.substring(5)) != null) {
1332             jsc.add("resultHc = 37 * resultHc + (" + name + "?0:1);");
1333           }
1334         } else if (type == JType.BYTE || type == JType.INT || type == JType.SHORT) {
1335           jsc.add("resultHc = 37 * resultHc + " + name + ";");
1336         } else if (type == JType.LONG) {
1337           jsc.add("resultHc = 37 * resultHc + (int)(" + name + "^(" + name + ">>>32));");
1338         } else if (type == JType.FLOAT) {
1339           jsc.add("resultHc = 37 * resultHc + java.lang.Float.floatToIntBits(" + name + ");");
1340         } else if (type == JType.DOUBLE) {
1341           jsc.add("tmp = java.lang.Double.doubleToLongBits(" + name + ");");
1342           jsc.add("resultHc = 37 * resultHc + (int)(tmp^(tmp>>>32));");
1343         }
1344       } else {
1345         if (getConfig().useCycleBreaker()) {
1346           // Calculates hashCode in an acyclic recursive manner
1347           jsc.add("if (" + name + " != null");
1348           jsc.add("       && !org.castor.core.util.CycleBreaker.startingToCycle(" + name + ")) {");
1349         } else {
1350           // Calculates hashCode in a recursive manner
1351           jsc.add("if (" + name + " != null) {");
1352         }
1353         jsc.add("   resultHc = 37 * resultHc + " + name + ".hashCode();");
1354 
1355         if (getConfig().useCycleBreaker()) {
1356           // Calculates hashCode in an acyclic recursive manner
1357           jsc.add("   org.castor.core.util.CycleBreaker.releaseCycleHandle(" + name + ");");
1358         }
1359         jsc.add("}");
1360       }
1361     }
1362     jsc.add("");
1363     jsc.add("return resultHc;");
1364   } // createHashCodeMethod
1365 
1366   /**
1367    * Create an 'equals' method on the given JClass.
1368    *
1369    * @param jclass the Jclass in which we create the equals method
1370    */
1371   public void createEqualsMethod(final JClass jclass) {
1372     if (jclass == null) {
1373       throw new IllegalArgumentException("JClass must not be null");
1374     }
1375 
1376     JField[] fields = jclass.getFields();
1377     JMethod jMethod = new JMethod("equals", JType.BOOLEAN, "true if the objects are equal.");
1378     jMethod.setComment("Overrides the java.lang.Object.equals method.");
1379     jMethod.addParameter(new JParameter(SGTypes.OBJECT, "obj"));
1380 
1381     if (getConfig().useJava50()) {
1382       jMethod.addAnnotation(new JAnnotation(new JAnnotationType("Override")));
1383     }
1384 
1385     jclass.addMethod(jMethod);
1386     JSourceCode jsc = jMethod.getSourceCode();
1387     jsc.add("if ( this == obj )");
1388     jsc.indent();
1389     jsc.add("return true;");
1390     jsc.unindent();
1391     if (jclass.getSuperClassQualifiedName() != null) {
1392       jsc.add("");
1393       jsc.add("if (super.equals(obj)==false)");
1394       jsc.indent();
1395       jsc.add("return false;");
1396       jsc.unindent();
1397     }
1398     jsc.add("");
1399     jsc.add("if (obj instanceof ");
1400     jsc.append(jclass.getLocalName());
1401     jsc.append(") {");
1402     jsc.add("");
1403     if (fields.length > 0) {
1404       jsc.indent();
1405       jsc.add(jclass.getLocalName());
1406       jsc.append(" temp = (");
1407       jsc.append(jclass.getLocalName());
1408       jsc.append(")obj;");
1409 
1410       if (getConfig().useCycleBreaker()) {
1411         jsc.add("boolean thcycle;");
1412         jsc.add("boolean tmcycle;");
1413       }
1414     }
1415     for (JField temp : fields) {
1416       // Be careful to arrayList....
1417 
1418       String name = temp.getName();
1419       if (temp.getType().isPrimitive()) {
1420         jsc.add("if (this.");
1421         jsc.append(name);
1422         jsc.append(" != temp.");
1423         jsc.append(name);
1424         jsc.append(")");
1425       } else {
1426         // -- Check first if the field is not null. This can occur while comparing
1427         // -- two objects that contains non-mandatory fields. We only have to check
1428         // -- one field since x.equals(null) should return false when equals() is
1429         // -- correctly implemented.
1430         jsc.add("if (this.");
1431         jsc.append(name);
1432         jsc.append(" != null) {");
1433         jsc.indent();
1434         jsc.add("if (temp.");
1435         jsc.append(name);
1436         jsc.append(" == null) ");
1437         jsc.indent();
1438         jsc.append("return false;");
1439         jsc.unindent();
1440 
1441         if (getConfig().useCycleBreaker()) {
1442           jsc.add("if (this.");
1443           jsc.append(name);
1444           jsc.append(" != temp.");
1445           jsc.append(name);
1446           jsc.append(") {");
1447           // This prevents string constants and improper DOM subtree self comparisons
1448           // (where Q(A(B)) and Q'(C(B)) are compared) screwing up cycle detection
1449           jsc.indent();
1450           jsc.add("thcycle=org.castor.core.util.CycleBreaker.startingToCycle(this." + name + ");");
1451           jsc.add("tmcycle=org.castor.core.util.CycleBreaker.startingToCycle(temp." + name + ");");
1452           // equivalent objects *will* cycle at the same time
1453           jsc.add("if (thcycle!=tmcycle) {");
1454           jsc.indent();
1455           jsc.add("if (!thcycle) { org.castor.core.util.CycleBreaker.releaseCycleHandle(this."
1456               + name + "); };");
1457           jsc.add("if (!tmcycle) { org.castor.core.util.CycleBreaker.releaseCycleHandle(temp."
1458               + name + "); };");
1459           jsc.add("return false;");
1460           jsc.unindent();
1461           jsc.add("}"); // end of unequal cycle point test
1462           jsc.add("if (!thcycle) {");
1463           jsc.indent();
1464         }
1465         jsc.add("if (!");
1466 
1467         // Special handling for comparing arrays
1468         if (temp.getType().isArray()) {
1469           jsc.append("java.util.Arrays.equals(this.");
1470           jsc.append(name);
1471           jsc.append(", temp.");
1472           jsc.append(name);
1473           jsc.append(")");
1474         } else {
1475           jsc.append("this.");
1476           jsc.append(name);
1477           jsc.append(".equals(temp.");
1478           jsc.append(name);
1479           jsc.append(")");
1480         }
1481 
1482         if (getConfig().useCycleBreaker()) {
1483           jsc.append(") {");
1484         } else {
1485           jsc.append(") ");
1486         }
1487         jsc.indent();
1488 
1489         if (getConfig().useCycleBreaker()) {
1490           jsc.add("org.castor.core.util.CycleBreaker.releaseCycleHandle(this." + name + ");");
1491           jsc.add("org.castor.core.util.CycleBreaker.releaseCycleHandle(temp." + name + ");");
1492         }
1493         jsc.add("return false;");
1494         jsc.unindent();
1495 
1496         if (getConfig().useCycleBreaker()) {
1497           jsc.add("}");
1498 
1499           jsc.add("org.castor.core.util.CycleBreaker.releaseCycleHandle(this." + name + ");");
1500           jsc.add("org.castor.core.util.CycleBreaker.releaseCycleHandle(temp." + name + ");");
1501         }
1502 
1503         jsc.unindent();
1504 
1505         if (getConfig().useCycleBreaker()) {
1506           jsc.add("}"); // end of !thcycle
1507           jsc.unindent();
1508           jsc.add("}"); // end of this.name != that.name object constant check
1509           jsc.unindent();
1510         }
1511         jsc.add("} else if (temp."); // end of != null
1512         jsc.append(name);
1513         jsc.append(" != null)");
1514       }
1515       jsc.indent();
1516       jsc.add("return false;");
1517       jsc.unindent();
1518     }
1519     jsc.add("return true;");
1520     jsc.unindent();
1521     jsc.add("}");
1522     jsc.add("return false;");
1523   } // CreateEqualsMethod
1524 
1525   /**
1526    * Implement org.castor.xmlctf.CastorTestable im the given JClass.
1527    *
1528    * @param jclass The JClass which will implement the CastorTestable Interface.
1529    * @param state our state, e.g., state of this Factory instance.
1530    */
1531   public void createTestableMethods(final JClass jclass, final FactoryState state) {
1532     if (jclass == null) {
1533       throw new IllegalArgumentException("JClass must not be null");
1534     }
1535 
1536     jclass.addInterface("org.castor.xmlctf.CastorTestable");
1537     jclass.addImport("org.castor.xmlctf.CastorTestable");
1538     jclass.addImport("org.castor.xmlctf.RandomHelper");
1539 
1540     createRandomizeFields(jclass, state); // implementation of randomizeFields
1541     createDumpFields(jclass); // implementation of dumpFields
1542   } // CreateTestableMethods
1543 
1544   /**
1545    * Creates the randomizeFields method for a class that implements the interface
1546    * org.castor.xmlctf.CastorTestable.
1547    *
1548    * @param jclass The JClass which will implement the CastorTestable Interface.
1549    * @param state
1550    */
1551   private void createRandomizeFields(final JClass jclass, final FactoryState state) {
1552     JMethod jMethod = new JMethod("randomizeFields");
1553     jMethod.addException(new JClass("InstantiationException"),
1554         "if we try to instantiate an abstract class or interface");
1555     jMethod.addException(new JClass("IllegalAccessException"),
1556         "if we do not have access to the field, for example if it is private");
1557     jMethod.setComment("implementation of org.castor.xmlctf.CastorTestable");
1558     jclass.addMethod(jMethod);
1559     JSourceCode jsc = jMethod.getSourceCode();
1560     JField[] fields = jclass.getFields();
1561 
1562     for (JField temp : fields) {
1563       JType type = temp.getType();
1564       String name = temp.getName();
1565 
1566       if (state.getFieldInfoForChoice() != null
1567           && name.equals(state.getFieldInfoForChoice().getName())) {
1568         continue;
1569       }
1570 
1571       if (name.startsWith("enumConstants")) {
1572         continue;
1573       }
1574 
1575       if (name.startsWith("_")) {
1576         name = getJavaNaming().toJavaClassName(name.substring(1));
1577       } else {
1578         name = getJavaNaming().toJavaClassName(name);
1579       }
1580 
1581       String setName = "set" + name;
1582       if (name.indexOf("Has") == -1) {
1583         if (type instanceof JCollectionType) {
1584           // Collection needs a specific handling
1585           int listLocat = name.lastIndexOf("List");
1586           String tempName = name;
1587           if (listLocat != -1) {
1588             tempName = tempName.substring(0, listLocat);
1589           }
1590           String methodName = getJavaNaming().toJavaClassName(tempName);
1591           methodName = "get" + methodName;
1592           JMethod method = jclass.getMethod(methodName, 0);
1593           // TODO handle the Item introduced in with the group handling
1594           if (method == null) {
1595             continue;
1596           }
1597 
1598           String componentName = method.getReturnType().getName();
1599 
1600           jsc.add(temp.getName());
1601           jsc.append(" = RandomHelper.getRandom(");
1602           jsc.append(temp.getName());
1603           jsc.append(", ");
1604           jsc.append(componentName);
1605           jsc.append(".class);");
1606         } else if (type.isPrimitive()) {
1607           // Primitive
1608           jsc.add(setName);
1609           jsc.append("(RandomHelper.getRandom(");
1610           jsc.append(temp.getName());
1611           jsc.append("));");
1612         } else if (type.isArray()) {
1613           // Array
1614           jsc.add(setName);
1615           jsc.append("((");
1616           jsc.append(type.toString());
1617           jsc.append(")RandomHelper.getRandom(");
1618           jsc.append(temp.getName());
1619           // Any Class will do, but Array.class seems appropriate
1620           jsc.append(", java.lang.reflect.Array.class));");
1621         } else {
1622           // Object
1623           jsc.add(setName);
1624           jsc.append("((");
1625           jsc.append(type.getName());
1626           jsc.append(")RandomHelper.getRandom(");
1627           jsc.append(temp.getName());
1628           jsc.append(", ");
1629           jsc.append(type.getName());
1630           jsc.append(".class));");
1631         }
1632         jsc.add("");
1633       }
1634     }
1635   }
1636 
1637   /**
1638    * Creates the dumpFields method for a class that implements the interface
1639    * org.castor.xmlctf.CastorTestable.
1640    *
1641    * @param jclass The JClass which will implement the CastorTestable Interface.
1642    */
1643   private void createDumpFields(final JClass jclass) {
1644     JMethod jMethod = new JMethod("dumpFields", SGTypes.STRING,
1645         "a String representation of all of the fields for " + jclass.getName());
1646     jMethod.setComment("implementation of org.castor.xmlctf.CastorTestable");
1647     jclass.addMethod(jMethod);
1648     JSourceCode jsc = jMethod.getSourceCode();
1649     jsc.add("StringBuffer stringBuffer = new StringBuffer(\"DumpFields() for element: ");
1650     jsc.append(jclass.getName());
1651     jsc.append("\\n\");");
1652 
1653     for (JField temp : jclass.getFields()) {
1654       String name = temp.getName();
1655       if ((temp.getType().isPrimitive()) || temp.getType().getName().startsWith("java.lang.")) {
1656         // hack when using the option 'primitivetowrapper'
1657         // this should not interfere with other cases
1658         jsc.add("stringBuffer.append(\"Field ");
1659         jsc.append(name);
1660         jsc.append(":\" +");
1661         jsc.append(name);
1662         jsc.append("+\"\\n\");");
1663       } else if (temp.getType().isArray()) {
1664         jsc.add("if (");
1665         jsc.append(name);
1666         jsc.append(" != null) {");
1667         jsc.indent();
1668         jsc.add("stringBuffer.append(\"[\");");
1669         jsc.add("for (int i = 0; i < ");
1670         jsc.append(name);
1671         jsc.append(".length; i++) {");
1672         jsc.indent();
1673         jsc.add("stringBuffer.append(");
1674         jsc.append(name);
1675         jsc.append("[i] + \" \");");
1676         jsc.unindent();
1677         jsc.add("}");
1678         jsc.add("stringBuffer.append(\"]\");");
1679         jsc.unindent();
1680         jsc.add("}");
1681       } else {
1682         jsc.add("if ( (");
1683         jsc.append(name);
1684         jsc.append(" != null) && (");
1685         jsc.append(name);
1686         jsc.append(".getClass().isAssignableFrom(CastorTestable.class)))");
1687         jsc.indent();
1688         jsc.add("stringBuffer.append(((CastorTestable)");
1689         jsc.append(name);
1690         jsc.append(").dumpFields());");
1691         jsc.unindent();
1692         jsc.add("else stringBuffer.append(\"Field ");
1693         jsc.append(name);
1694         jsc.append(":\" +");
1695         jsc.append(name);
1696         jsc.append("+\"\\n\");");
1697       }
1698       jsc.add("");
1699     }
1700     jsc.add("");
1701     jsc.add("return stringBuffer.toString();");
1702   }
1703 
1704   /**
1705    * Creates the Validate methods for the given JClass.
1706    * 
1707    * @param jClass the JClass to create the Validate methods for
1708    */
1709   private void createValidateMethods(final JClass jClass) {
1710     JMethod jMethod = null;
1711     JSourceCode jsc = null;
1712 
1713     // -- #validate
1714     jMethod = new JMethod("validate");
1715     jMethod.addException(SGTypes.VALIDATION_EXCEPTION,
1716         "if this object is an invalid instance according to the schema");
1717 
1718     jClass.addMethod(jMethod);
1719     jsc = jMethod.getSourceCode();
1720     jsc.add("org.exolab.castor.xml.Validator validator = new ");
1721     jsc.append("org.exolab.castor.xml.Validator();");
1722     jsc.add("validator.validate(this);");
1723 
1724     // -- #isValid
1725     jMethod = new JMethod("isValid", JType.BOOLEAN,
1726         "true if this object is valid according to the schema");
1727     jsc = jMethod.getSourceCode();
1728     jsc.add("try {");
1729     jsc.indent();
1730     jsc.add("validate();");
1731     jsc.unindent();
1732     jsc.add("} catch (org.exolab.castor.xml.ValidationException vex) {");
1733     jsc.indent();
1734     jsc.add("return false;");
1735     jsc.unindent();
1736     jsc.add("}");
1737     jsc.add("return true;");
1738     jClass.addMethod(jMethod);
1739   } // -- createValidateMethods
1740 
1741   // -------------------/
1742   // - Private Methods -/
1743   // -------------------/
1744 
1745   /**
1746    * Resolves the className out of the given name and the packageName.
1747    *
1748    * @param name the class name
1749    * @param packageName the package name
1750    * @return the full qualified class name.
1751    */
1752   private String resolveClassName(final String name, final String packageName) {
1753     if ((packageName != null) && (packageName.length() > 0)) {
1754       return packageName + "." + name;
1755     }
1756     return name;
1757   } // -- resolveClassName
1758 
1759   //////////////////////////////////////////////
1760   // Process XML Schema structures
1761   // Note: This code is XML specific, it has to be moved somehow in XMLBindingComponent.
1762   // The aim of the SourceFactory is to generate code from a BindingComponent.
1763   ///////////////////////////////////////////////
1764 
1765   /**
1766    * This method handles the processing of AppInfo elements from the input schema and adds the
1767    * information found therein to the specified {@link ClassInfo}.
1768    * 
1769    * @param annotated the {@link org.exolab.castor.xml.schema.Structure} holding the {@link AppInfo}
1770    *        annotations.
1771    * @param cInfo the {@link ClassInfo} to store the information found in the AppInfos in.
1772    * @see ClassInfo
1773    * @see AppInfo
1774    * @see Annotated
1775    */
1776   private void processAppInfo(final Annotated annotated, final ClassInfo cInfo) {
1777     Enumeration<Annotation> annotations = annotated.getAnnotations();
1778     while (annotations.hasMoreElements()) {
1779       Annotation ann = annotations.nextElement();
1780       Enumeration<AppInfo> appInfos = ann.getAppInfo();
1781       while (appInfos.hasMoreElements()) {
1782         AppInfo appInfo = appInfos.nextElement();
1783         if (appInfo.hasNature(AppInfoJpaNature.class.getName())) {
1784           AppInfoJpaNature nature = new AppInfoJpaNature(appInfo);
1785           List<?> content = nature.getContent();
1786           if (content != null && !content.isEmpty()) {
1787             Iterator<?> it = content.iterator();
1788             if (it.hasNext()) {
1789               cInfo.addNature(JDOClassInfoNature.class.getName());
1790               JDOClassInfoNature cNature = new JDOClassInfoNature(cInfo);
1791               while (it.hasNext()) {
1792                 Object tmpObject = it.next();
1793                 if (tmpObject instanceof Table) {
1794                   Table table = (Table) tmpObject;
1795                   cNature.setTableName(table.getName());
1796                   cNature.setAccessMode(AccessMode.valueOf("shared"));
1797                   cNature.setDetachable(table.isDetachable());
1798                   // TODO: Uncomment next line as soon as Annotation Classes have been updated!
1799                   // cNature.setAccessMode(AccessMode.valueOf(table.getAccessMode().toString()));
1800                   PrimaryKey pk = table.getPrimaryKey();
1801                   Iterator<? extends String> pIt = pk.iterateKey();
1802                   while (pIt.hasNext()) {
1803                     cNature.addPrimaryKey(pIt.next());
1804                   }
1805                 }
1806               }
1807             }
1808           }
1809         }
1810       }
1811     }
1812   }
1813 
1814   /**
1815    * This method handles the processing of AppInfo elements from the input schema and adds the
1816    * information found therein to the specified {@link FieldInfo}.
1817    * 
1818    * @param annotated the {@link org.exolab.castor.xml.schema.Structure} holding the {@link AppInfo}
1819    *        annotations.
1820    * @param fInfo the {@link ClassInfo} to store the information found in the AppInfos in.
1821    * @see FieldInfo
1822    * @see AppInfo
1823    * @see Annotated
1824    */
1825   private void processAppInfo(final Annotated annotated, final FieldInfo fInfo) {
1826     Enumeration<Annotation> annotations = annotated.getAnnotations();
1827 
1828     while (annotations.hasMoreElements()) {
1829       Annotation ann = annotations.nextElement();
1830       Enumeration<AppInfo> appInfos = ann.getAppInfo();
1831       while (appInfos.hasMoreElements()) {
1832         AppInfo appInfo = appInfos.nextElement();
1833 
1834         if (appInfo.hasNature(AppInfoSolrjNature.class.getName())) {
1835           AppInfoSolrjNature nature = new AppInfoSolrjNature(appInfo);
1836           Object solrjRawContent = nature.getContent();
1837           if (solrjRawContent != null) {
1838             fInfo.addNature(SolrjFieldInfoNature.class.getName());
1839             SolrjFieldInfoNature solrjNature = new SolrjFieldInfoNature(fInfo);
1840             if (solrjRawContent instanceof Field) {
1841               Field solrjField = (Field) solrjRawContent;
1842               if (StringUtils.isNotBlank(solrjField.getName())) {
1843                 solrjNature.setFieldName(solrjField.getName());
1844               }
1845             }
1846           }
1847         }
1848 
1849         if (appInfo.hasNature(AppInfoJpaNature.class.getName())) {
1850           AppInfoJpaNature nature = new AppInfoJpaNature(appInfo);
1851           List<?> content = nature.getContent();
1852           if (content != null && !content.isEmpty()) {
1853             Iterator<?> it = content.iterator();
1854             if (it.hasNext()) {
1855               while (it.hasNext()) {
1856                 Object tmpObject = it.next();
1857                 if (tmpObject instanceof Column) {
1858                   fInfo.addNature(JDOFieldInfoNature.class.getName());
1859                   JDOFieldInfoNature fNature = new JDOFieldInfoNature(fInfo);
1860                   Column column = (Column) tmpObject;
1861                   fNature.setColumnName(column.getName());
1862                   fNature.setColumnType(column.getType());
1863                   fNature.setReadOnly(column.isReadOnly());
1864                   fNature.setDirty(false);
1865                   fNature.setDirty(column.getDirty());
1866                 } else if (tmpObject instanceof OneToOne) {
1867                   OneToOne relation = (OneToOne) tmpObject;
1868                   fInfo.addNature(JDOOneToOneNature.class.getName());
1869                   JDOOneToOneNature oneNature = new JDOOneToOneNature(fInfo);
1870                   oneNature.addForeignKey(relation.getName());
1871                   oneNature.setDirty(relation.isDirty());
1872                   oneNature.setReadOnly(relation.isReadOnly());
1873                 } else if (tmpObject instanceof OneToMany) {
1874                   OneToMany relation = (OneToMany) tmpObject;
1875                   fInfo.addNature(JDOOneToManyNature.class.getName());
1876                   JDOOneToManyNature manyNature = new JDOOneToManyNature(fInfo);
1877                   manyNature.addForeignKey(relation.getName());
1878                   manyNature.setDirty(relation.isDirty());
1879                   manyNature.setReadOnly(relation.isReadOnly());
1880                 }
1881               }
1882             }
1883           }
1884         }
1885       }
1886     }
1887   }
1888 
1889   /**
1890    * Process the attributes contained in this complexType.
1891    * 
1892    * @param binding
1893    * @param complexType the given complex type.
1894    * @param state the given FactoryState
1895    */
1896   private void processAttributes(final ExtendedBinding binding, final ComplexType complexType,
1897       final FactoryState state) {
1898     if (complexType == null) {
1899       return;
1900     }
1901 
1902     Enumeration<AttributeDecl> enumeration = complexType.getAttributeDecls();
1903     XMLBindingComponent component = new XMLBindingComponent(getConfig(), getGroupNaming());
1904     if (_binding != null) {
1905       component.setBinding(_binding);
1906     }
1907 
1908     while (enumeration.hasMoreElements()) {
1909       AttributeDecl attr = enumeration.nextElement();
1910 
1911       component.setView(attr);
1912 
1913       // -- if we have a new SimpleType...generate ClassInfo
1914       SimpleType sType = attr.getSimpleType();
1915 
1916       // look for simpleType def in base type(s)
1917       XMLType baseXMLType = complexType.getBaseType();
1918       while (sType == null) {
1919         // If no simple type found: Get the same attribute of the base type.
1920         // If base type is not complex, forget it; break out of loop now.
1921         if (baseXMLType == null || !(baseXMLType instanceof ComplexType)) {
1922           break;
1923         }
1924 
1925         // There's a base complexType; get the attribute with the same name
1926         // as this attribute (=attr) from it
1927         final ComplexType baseComplexType = (ComplexType) baseXMLType;
1928         AttributeDecl baseAttribute = baseComplexType.getAttributeDecl(attr.getName());
1929 
1930         if (baseAttribute != null) {
1931           // See if this one has a simple-type...
1932           sType = baseAttribute.getSimpleType();
1933           if (sType != null) {
1934             attr.setSimpleType(sType);
1935             break;
1936           }
1937         }
1938 
1939         // ... if not, go another step higher in the class hierarchy
1940         baseXMLType = baseXMLType.getBaseType();
1941       }
1942 
1943       // Look for referenced type (if any) for setting type, and use
1944       // it, if found.
1945       if (sType == null && attr.getReference() != null) {
1946         SimpleType referencedSimpleType = attr.getReference().getSimpleType();
1947         attr.setSimpleType(referencedSimpleType);
1948         sType = referencedSimpleType;
1949       }
1950 
1951       if (sType != null && !(SimpleTypesFactory.isBuiltInType(sType.getTypeCode()))) {
1952         if (sType.getSchema() == component.getSchema() && state.resolve(sType) == null) {
1953           if (sType.hasFacet(Facet.ENUMERATION)) {
1954             createSourceCode(component.getBinding(), sType, state.getSGStateInfo());
1955           }
1956         }
1957       }
1958 
1959       FieldInfo fieldInfo =
1960           _memberFactory.createFieldInfo(component, state, getConfig().useJava50());
1961       handleField(fieldInfo, state, component);
1962     }
1963   }
1964 
1965   /**
1966    * @param complexType the ComplexType to process
1967    * @param state the FactoryState.
1968    */
1969   private void processComplexType(final ComplexType complexType, final FactoryState state) {
1970     XMLBindingComponent component = new XMLBindingComponent(getConfig(), getGroupNaming());
1971     if (_binding != null) {
1972       component.setBinding(_binding);
1973     }
1974     component.setView(complexType);
1975 
1976     String typeName = component.getXMLName();
1977 
1978     ClassInfo classInfo = state.getClassInfo();
1979     XMLInfoNature xmlNature = new XMLInfoNature(classInfo);
1980     xmlNature.setSchemaType(new XSClass(state.getJClass(), typeName));
1981 
1982     /// I don't believe this should be here: kv 20030423
1983     /// classInfo.setNamespaceURI(component.getTargetNamespace());
1984 
1985     // - Handle derived types
1986     XMLType base = complexType.getBaseType();
1987 
1988     // -- if the base is a complexType, we need to process it
1989     if (base != null) {
1990       if (base.isComplexType()) {
1991         String baseClassName = null;
1992 
1993         component.setView(base);
1994         // -- Is this base type from the schema we are currently generating source for?
1995         //////////////////////////////////////////////////////////
1996         // NOTE: generate sources if the flag for generating sources
1997         // from imported schemas in on
1998         //////////////////////////////////////////////////////////
1999         if (base.getSchema() == complexType.getSchema()) {
2000           ClassInfo cInfo = state.resolve(base);
2001           // --no classInfo yet so no source code available
2002           // --for the base type: we need to generate it
2003           if (cInfo == null) {
2004             JClass[] classes = createSourceCode(component, state.getSGStateInfo());
2005             cInfo = state.resolve(base);
2006             baseClassName = classes[0].getName();
2007           } else {
2008             baseClassName = cInfo.getJClass().getName();
2009           }
2010           // set the base class
2011           classInfo.setBaseClass(cInfo);
2012         } else {
2013           // -- Create qualified class name for a base type class
2014           // -- from another package
2015           baseClassName = component.getQualifiedName();
2016         }
2017         // -- Set super class
2018         // -- and reset the view on the current ComplexType
2019         component.setView(complexType);
2020         // only set a super class name if the current complexType is not a
2021         // restriction of a simpleContent (--> no object hierarchy, only content hierarchy)
2022         /*
2023          * Note: There are times when a simpleContent restriction needs to extend the hierarchy,
2024          * such as a restriction of a restriction, so I'm commenting out the following line for now.
2025          * see bug 1875 for more details. If this causes any regressions we'll need to find a more
2026          * appropriate solution. if (! ( complexType.isRestricted() &&
2027          * ((ComplexType)base).isSimpleContent() ) )
2028          */
2029         state.getJClass().setSuperClass(baseClassName);
2030       } // --complexType
2031 
2032       // --if the content type is a simpleType create a field info for it.
2033       if (complexType.getContentType().getType() == ContentType.SIMPLE) {
2034         SimpleContent simpleContent = (SimpleContent) complexType.getContentType();
2035         SimpleType temp = simpleContent.getSimpleType();
2036         SimpleType baseType = (SimpleType) temp.getBaseType();
2037         XSType xsType =
2038             _typeConversion.convertType(temp, state.getPackageName(), getConfig().useJava50());
2039 
2040         FieldInfo fieldInfo = null;
2041         if ((baseType != null) && extendsSimpleType(state.getJClass(), baseType, state)) {
2042           if (xsType.isEnumerated()) {
2043             fieldInfo = _memberFactory.createFieldInfoForContent(component, xsType,
2044                 getConfig().useJava50());
2045             fieldInfo.setBound(false);
2046 
2047             handleField(fieldInfo, state, component);
2048 
2049             // -- remove getter since we don't need to override the original getter
2050             String mname = fieldInfo.getReadMethodName();
2051             JClass jClass = state.getJClass();
2052             JMethod method = jClass.getMethod(mname, 0);
2053             jClass.removeMethod(method);
2054 
2055             // -- update setter method
2056             mname = fieldInfo.getWriteMethodName();
2057             method = jClass.getMethod(mname, 0);
2058             JSourceCode jsc = method.getSourceCode();
2059             jsc.add("super.");
2060             jsc.append(mname);
2061             jsc.append("(this.");
2062             jsc.append(fieldInfo.getName());
2063             jsc.append(".toString());");
2064           }
2065           // -- else just use superclass setters/getters
2066           // -- do nothing
2067         } else {
2068           //
2069           while (temp.getBaseType() != null && !temp.isBuiltInType()) {
2070             temp = (SimpleType) temp.getBaseType();
2071           }
2072 
2073           xsType =
2074               _typeConversion.convertType(temp, state.getPackageName(), getConfig().useJava50());
2075           fieldInfo =
2076               _memberFactory.createFieldInfoForContent(component, xsType, getConfig().useJava50());
2077           handleField(fieldInfo, state, component);
2078         }
2079       }
2080     } // --base not null
2081 
2082     // ---------------------/
2083     // - handle attributes -/
2084     // - and mixed content -/
2085     // ---------------------/
2086 
2087     if (!state.isCreateGroupItem()) {
2088       processAttributes(component.getBinding(), complexType, state);
2089       // --reset the view on the current ComplexType
2090       component.setView(complexType);
2091       if (complexType.getContentType() == ContentType.mixed) {
2092         FieldInfo fieldInfo = _memberFactory.createFieldInfoForContent(component, new XSString(),
2093             getConfig().useJava50());
2094         handleField(fieldInfo, state, component);
2095       }
2096     }
2097     // --process the contentModelGroup
2098     processContentModel(complexType, state);
2099   } // -- processComplextype
2100 
2101   /**
2102    * Processes the given ContentModelGroup. This method is responsible for creating FieldInfos (or
2103    * sometimes ClassInfos) for elements and model group contained in the given ContentModelGroup.
2104    *
2105    * @param model the ContentModelGroup to process
2106    * @param state the current FactoryState.
2107    */
2108   private void processContentModel(final ContentModelGroup model, final FactoryState state) {
2109     // ------------------------------/
2110     // - handle elements and groups -/
2111     // ------------------------------/
2112 
2113     ContentModelGroup contentModel = model;
2114     Enumeration<Particle> enumeration = contentModel.enumerate();
2115 
2116     // -- handle choice item
2117     if (new XMLInfoNature(state.getClassInfo()).isChoice()
2118         && state.getFieldInfoForChoice() == null) {
2119       state.setFieldInfoForChoice(_memberFactory.createFieldInfoForChoiceValue());
2120       state.getFieldInfoForChoice().getMemberAndAccessorFactory()
2121           .createJavaField(state.getFieldInfoForChoice(), state.getJClass());
2122       state.getFieldInfoForChoice().getMemberAndAccessorFactory().createAccessMethods(
2123           state.getFieldInfoForChoice(), state.getJClass(), getConfig().useJava50(),
2124           getConfig().getAnnotationBuilders());
2125     }
2126 
2127     FieldInfo fieldInfo = null;
2128     XMLBindingComponent component = new XMLBindingComponent(getConfig(), getGroupNaming());
2129     if (_binding != null) {
2130       component.setBinding(_binding);
2131     }
2132 
2133     while (enumeration.hasMoreElements()) {
2134       Annotated annotated = enumeration.nextElement();
2135       component.setView(annotated);
2136 
2137       switch (annotated.getStructureType()) {
2138         case Structure.ELEMENT: // -- handle element declarations
2139           fieldInfo = _memberFactory.createFieldInfo(component, state, getConfig().useJava50());
2140           // -- Fix for element declarations being used in
2141           // -- a group with minOccurs = 0;
2142           // -- (kvisco - 20021007)
2143           if (contentModel.getMinOccurs() == 0) {
2144             XMLInfoNature xmlNature = new XMLInfoNature(fieldInfo);
2145             xmlNature.setRequired(false);
2146           }
2147 
2148           handleField(fieldInfo, state, component);
2149           break;
2150         case Structure.GROUP: // -- handle groups
2151           Group group = (Group) annotated;
2152           // set the compositor
2153           if ((contentModel instanceof ComplexType) || (contentModel instanceof ModelGroup)) {
2154             if (group.getOrder() == Order.choice) {
2155               new XMLInfoNature(state.getClassInfo()).getGroupInfo().setAsChoice();
2156             } else if (group.getOrder() == Order.all) {
2157               new XMLInfoNature(state.getClassInfo()).getGroupInfo().setAsAll();
2158             } else if (group.getOrder() == Order.sequence) {
2159               new XMLInfoNature(state.getClassInfo()).getGroupInfo().setAsSequence();
2160             }
2161           }
2162 
2163           // -- create class member,if necessary
2164           if (!((contentModel instanceof ComplexType) || (contentModel instanceof ModelGroup))) {
2165             if (contentModel instanceof ModelGroup) {
2166               ModelGroup mg = (ModelGroup) contentModel;
2167               if (mg.isReference()) {
2168                 contentModel = mg.getReference();
2169               }
2170             }
2171 
2172             if (contentModel.getParticleCount() > 0) {
2173               fieldInfo = _memberFactory.createFieldInfo(component, state.getSGStateInfo(),
2174                   getConfig().useJava50());
2175               handleField(fieldInfo, state, component);
2176             }
2177           } else {
2178             // --else we just flatten the group
2179             processContentModel(group, state);
2180           }
2181           break;
2182 
2183         case Structure.MODELGROUP:
2184           ModelGroup modelgroup = (ModelGroup) annotated;
2185           // --a Model Group definition can only referenced
2186           // --another group at this point.
2187           // get the contentModel and proccess it
2188           if (modelgroup.getName() != null) {
2189             // create the field info for the element
2190             // that is referring to a model group in order
2191             // not to loose the Particle information
2192             if (modelgroup.isReference()) {
2193               modelgroup = modelgroup.getReference();
2194             }
2195 
2196             if (modelgroup.getParticleCount() > 0) {
2197               fieldInfo = _memberFactory.createFieldInfo(component, state.getSGStateInfo(),
2198                   getConfig().useJava50());
2199               handleField(fieldInfo, state, component);
2200             }
2201             break;
2202           }
2203           // --else we just flatten the group
2204           processContentModel(modelgroup.getContentModelGroup(), state);
2205           break;
2206 
2207         case Structure.WILDCARD:
2208           Wildcard wildcard = (Wildcard) annotated;
2209           FieldInfo fieldForAny =
2210               _memberFactory.createFieldInfoForAny(wildcard, getConfig().useJava50());
2211           handleField(fieldForAny, state, component);
2212           break;
2213 
2214         default:
2215           break;
2216       }
2217     }
2218 
2219   } // -- process(ContentModelGroup)
2220 
2221   /**
2222    * Creates all the necessary enumeration code from the given Creates all the necessary enumeration
2223    * code from the given SimpleType. Enumerations are handled a couple ways.
2224    *
2225    * @param binding CUrrent XML binding
2226    * @param simpleType the SimpleType we are processing an enumeration for
2227    * @param state our current state
2228    * @see #processEnumerationAsBaseType
2229    */
2230   private void processEnumeration(final ExtendedBinding binding, final SimpleType simpleType,
2231       final FactoryState state) {
2232     // Added by robertlaferla at comcast dot net 01/21/2004
2233     if (getConfig().useEnumeratedTypeInterface()) {
2234       state.getJClass().addInterface(ENUM_ACCESS_INTERFACE);
2235     } // end enumTypeInterface
2236 
2237     switch (_enumerationType) {
2238       case BASE_TYPE_ENUMERATION:
2239         processEnumerationAsBaseType(binding, simpleType, state);
2240         break;
2241       default:
2242         processEnumerationAsNewObject(binding, simpleType, state);
2243         break;
2244     }
2245   } // -- processEnumeration
2246 
2247   /**
2248    * Creates all the necessary enumeration code from the given SimpleType. Delegates to
2249    * EnumerationFactory.
2250    *
2251    * @param binding Current XML binding
2252    * @param simpleType the SimpleType we are processing an enumeration for
2253    * @param state our current state
2254    * @see #processEnumerationAsBaseType
2255    */
2256   private void processEnumerationAsNewObject(final ExtendedBinding binding,
2257       final SimpleType simpleType, final FactoryState state) {
2258     _enumerationFactory.processEnumerationAsNewObject(binding, simpleType, state);
2259     if (_testable && state.getJClass() instanceof JEnum) {
2260       createTestableMethods(state.getJClass(), state);
2261     }
2262   } // -- processEnumerationAsNewObject
2263 
2264   /**
2265    * Delegates creation of enumeration code to EnumerationFactory.
2266    *
2267    * @param binding Current XML binding
2268    * @param simpleType the SimpleType we are processing an enumeration for
2269    * @param state our current state
2270    */
2271   private void processEnumerationAsBaseType(final ExtendedBinding binding,
2272       final SimpleType simpleType, final FactoryState state) {
2273     _enumerationFactory.processEnumerationAsBaseType(binding, simpleType, state);
2274   } // -- processEnumerationAsBaseType
2275 
2276   /**
2277    * Adds a given FieldInfo to the JClass and ClassInfo stored in the given FactoryState.
2278    *
2279    * @param fieldInfo The fieldInfo to add
2280    * @param state the current FactoryState
2281    * @param component the current XMLBindingComponent to read AppInfos from.
2282    */
2283   private void handleField(final FieldInfo fieldInfo, final FactoryState state,
2284       final XMLBindingComponent component) {
2285     if (fieldInfo == null) {
2286       return;
2287     }
2288 
2289     XMLInfoNature xmlNature = new XMLInfoNature(fieldInfo);
2290 
2291     if (CLASS_METHOD_SUFFIX.equals(fieldInfo.getMethodSuffix())) {
2292       SGStateInfo sInfo = state.getSGStateInfo();
2293       if (!sInfo.getSuppressNonFatalWarnings()) {
2294         String warn = "warning a field name conflicts with \"" + CLASS_KEYWORD
2295             + "\", please use a binding file to specify " + "a different name for the "
2296             + xmlNature.getNodeTypeName() + " '" + xmlNature.getNodeName() + "'.";
2297         sInfo.getDialog().notify(warn);
2298       }
2299     } else if (CLASS_KEYWORD.equals(xmlNature.getNodeName())) {
2300       SGStateInfo sInfo = state.getSGStateInfo();
2301       if (!sInfo.getSuppressNonFatalWarnings()) {
2302         String warn = "warning a field name conflicts with \"" + CLASS_KEYWORD
2303             + "\" and is being replaced by \"clazz\". "
2304             + "You may use a binding file to specify a different " + "name for the "
2305             + xmlNature.getNodeTypeName() + " '" + xmlNature.getNodeName() + "'.";
2306         sInfo.getDialog().notify(warn);
2307       }
2308     }
2309 
2310     processAppInfo(component.getAnnotated(), fieldInfo);
2311 
2312     JSourceCode scInitializer = state.getJClass().getConstructor(0).getSourceCode();
2313 
2314     ClassInfo base = state.getClassInfo().getBaseClass();
2315     boolean present = false;
2316     if (base != null) {
2317       switch (new XMLInfoNature(fieldInfo).getNodeType()) {
2318         case ATTRIBUTE:
2319           present = (base.getAttributeField(xmlNature.getNodeName()) != null);
2320           break;
2321         case ELEMENT:
2322           String baseNodeName = xmlNature.getNodeName();
2323           // TODO[WG]: replace this error check with something more meaningful
2324           if (baseNodeName != null
2325               && !(baseNodeName.equals(XMLInfo.CHOICE_NODE_NAME_ERROR_INDICATION))) {
2326             // check whether there is an equally named field in the base
2327             // class, and don't forget to consider the namspaces as well to
2328             // determine whether the fields are really equal.
2329             FieldInfo inheritedFieldInfo = base.getElementField(baseNodeName);
2330 
2331             if (inheritedFieldInfo != null) {
2332               String namespaceURI = xmlNature.getNamespaceURI();
2333               if (namespaceURI != null
2334                   && namespaceURI.equals(new XMLInfoNature(inheritedFieldInfo).getNamespaceURI())) {
2335                 present = true;
2336               }
2337             }
2338           }
2339           break;
2340         default:
2341           break;
2342       }
2343     }
2344 
2345     state.getClassInfo().addFieldInfo(fieldInfo);
2346     present = present && !xmlNature.isMultivalued();
2347     // create the relevant Java fields only if the field
2348     // info is not yet in the base classInfo or if it is not a collection
2349     if (!present) {
2350       if (state.getFieldInfoForChoice() != null) {
2351         if (fieldInfo != state.getFieldInfoForChoice()) {
2352           fieldInfo.setFieldInfoReference(state.getFieldInfoForChoice());
2353         }
2354       }
2355 
2356       fieldInfo.getMemberAndAccessorFactory().createJavaField(fieldInfo, state.getJClass());
2357       // -- do not create access methods for transient fields
2358       if (!fieldInfo.isTransient()) {
2359         fieldInfo.getMemberAndAccessorFactory().createAccessMethods(fieldInfo, state.getJClass(),
2360             getConfig().useJava50(), getConfig().getAnnotationBuilders());
2361         if (fieldInfo.isBound()) {
2362           state.setBoundProperties(true);
2363         }
2364       }
2365     }
2366 
2367     // -- Add initialization code
2368     fieldInfo.getMemberAndAccessorFactory().generateInitializerCode(fieldInfo, scInitializer);
2369   } // -- handleField
2370 
2371   /**
2372    * Returns true if the given JClass extends the class represented by the given SimpleType.
2373    *
2374    * @param jClass the JClass to check
2375    * @param type the SimpleType to check against
2376    * @param state the FactoryState
2377    *
2378    * @return true if the given JClass extends the class associated with the given SimpleType,
2379    *         otherwise false.
2380    */
2381   private boolean extendsSimpleType(final JClass jClass, final SimpleType type,
2382       final FactoryState state) {
2383     String superClassName = jClass.getSuperClassQualifiedName();
2384     if (superClassName != null) {
2385       ClassInfo cInfo = state.resolve(type);
2386       if (cInfo != null) {
2387         return superClassName.equals(cInfo.getJClass().getName());
2388       }
2389     }
2390     return false;
2391   } // -- extendsSimpleType
2392 
2393 } // -- SourceFactory