/* * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain copyright * statements and notices. Redistributions must also contain a * copy of this document. * * 2. Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. The name "Exolab" must not be used to endorse or promote * products derived from this Software without prior written * permission of Intalio, Inc. For written permission, * please contact info@exolab.org. * * 4. Products derived from this Software may not be called "Exolab" * nor may "Exolab" appear in their names without prior written * permission of Intalio, Inc. Exolab is a registered * trademark of Intalio, Inc. * * 5. Due credit should be given to the Exolab Project * (http://www.exolab.org/). * * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Copyright 1999-2004 (C) Intalio Inc. All Rights Reserved. * * This file was originally developed by Keith Visco during the course * of employment at Intalio Inc. * Portions of this file developed by Keith Visco after Jan 19 2005 are * Copyright (C) 2005 Keith Visco. All Rights Reserverd. * * $Id$ */ package org.exolab.castor.builder.factory; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.castor.core.util.StringUtil; import org.exolab.castor.builder.AnnotationBuilder; import org.exolab.castor.builder.BuilderConfiguration; import org.exolab.castor.builder.FactoryState; import org.exolab.castor.builder.GroupNaming; import org.exolab.castor.builder.SGStateInfo; import org.exolab.castor.builder.SGTypes; import org.exolab.castor.builder.SourceGenerator; import org.exolab.castor.builder.SourceGeneratorConstants; import org.exolab.castor.builder.TypeConversion; import org.exolab.castor.builder.binding.ExtendedBinding; import org.exolab.castor.builder.binding.XMLBindingComponent; import org.exolab.castor.builder.info.ClassInfo; import org.exolab.castor.builder.info.FieldInfo; import org.exolab.castor.builder.info.GroupInfo; import org.exolab.castor.builder.info.XMLInfo; import org.exolab.castor.builder.info.nature.JDOClassInfoNature; import org.exolab.castor.builder.info.nature.JDOFieldInfoNature; import org.exolab.castor.builder.info.nature.XMLInfoNature; import org.exolab.castor.builder.info.nature.relation.JDOOneToManyNature; import org.exolab.castor.builder.info.nature.relation.JDOOneToOneNature; import org.exolab.castor.builder.types.XSClass; import org.exolab.castor.builder.types.XSString; import org.exolab.castor.builder.types.XSType; import org.exolab.castor.mapping.AccessMode; import org.exolab.castor.xml.schema.Annotated; import org.exolab.castor.xml.schema.Annotation; import org.exolab.castor.xml.schema.AppInfo; import org.exolab.castor.xml.schema.AttributeDecl; import org.exolab.castor.xml.schema.AttributeGroupDecl; import org.exolab.castor.xml.schema.ComplexType; import org.exolab.castor.xml.schema.ContentModelGroup; import org.exolab.castor.xml.schema.ContentType; import org.exolab.castor.xml.schema.Documentation; import org.exolab.castor.xml.schema.ElementDecl; import org.exolab.castor.xml.schema.Facet; import org.exolab.castor.xml.schema.Group; import org.exolab.castor.xml.schema.ModelGroup; import org.exolab.castor.xml.schema.Order; import org.exolab.castor.xml.schema.Particle; import org.exolab.castor.xml.schema.Schema; import org.exolab.castor.xml.schema.SimpleContent; import org.exolab.castor.xml.schema.SimpleType; import org.exolab.castor.xml.schema.SimpleTypesFactory; import org.exolab.castor.xml.schema.Structure; import org.exolab.castor.xml.schema.Wildcard; import org.exolab.castor.xml.schema.XMLType; import org.exolab.castor.xml.schema.annotations.jdo.Column; import org.exolab.castor.xml.schema.annotations.jdo.OneToMany; import org.exolab.castor.xml.schema.annotations.jdo.OneToOne; import org.exolab.castor.xml.schema.annotations.jdo.PrimaryKey; import org.exolab.castor.xml.schema.annotations.jdo.Table; import org.exolab.javasource.JAnnotation; import org.exolab.javasource.JAnnotationType; import org.exolab.javasource.JClass; import org.exolab.javasource.JCollectionType; import org.exolab.javasource.JConstructor; import org.exolab.javasource.JDocComment; import org.exolab.javasource.JDocDescriptor; import org.exolab.javasource.JEnum; import org.exolab.javasource.JField; import org.exolab.javasource.JMethod; import org.exolab.javasource.JParameter; import org.exolab.javasource.JSourceCode; import org.exolab.javasource.JType; /** * Creates the Java Source classes for Schema components. * * @author <a href="mailto:keith AT kvisco DOT com">Keith Visco</a> * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a> * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ */ public final class SourceFactory extends BaseFactory { private static final String ENUM_ACCESS_INTERFACE = "org.exolab.castor.types.EnumeratedTypeAccess"; private static final short BASE_TYPE_ENUMERATION = 0; private static final short OBJECT_TYPE_ENUMERATION = 1; private static final String CLASS_METHOD_SUFFIX = "Class"; private static final String CLASS_KEYWORD = "class"; private static final String ITEM_NAME = "Item"; /** The current Binding for which we are creating classes. */ private ExtendedBinding _binding = null; /** The member factory. */ private MemberFactory _memberFactory = null; private short _enumerationType = OBJECT_TYPE_ENUMERATION; /** * A flag indicating whether or not to generate XML marshaling framework * specific methods. */ private boolean _createMarshalMethods = true; /** * A flag indicating whether or not to implement CastorTestable (used by the * Castor Testing Framework). */ private boolean _testable = false; /** A flag indicating that SAX1 should be used when generating the source. */ private boolean _sax1 = false; /** The TypeConversion instance to use for mapping SimpleTypes into XSTypes. */ private TypeConversion _typeConversion = null; /** Enumeration factory used to create code for enumerations. */ private final EnumerationFactory _enumerationFactory; /** * Creates a new SourceFactory with the given FieldInfoFactory. * * @param config the BuilderConfiguration instance (must not be null). * @param infoFactory the FieldInfoFactory to use * @param groupNaming Group naming scheme to be used. * @param sourceGenerator the calling source generator. */ public SourceFactory(final BuilderConfiguration config, final FieldInfoFactory infoFactory, final GroupNaming groupNaming, final SourceGenerator sourceGenerator) { super(config, infoFactory, groupNaming, sourceGenerator); // set the config into the info factory (CASTOR-1346) infoFactory.setBoundProperties(config.boundPropertiesEnabled()); this._memberFactory = new MemberFactory(config, infoFactory, getGroupNaming(), sourceGenerator); this._typeConversion = new TypeConversion(getConfig()); this._enumerationFactory = new EnumerationFactory(getConfig(), getGroupNaming(), sourceGenerator); } //-- SourceFactory /** * Sets whether or not to create the XML marshaling framework specific * methods (marshal, unmarshal, validate) in the generated classes. By * default, these methods are generated. * * @param createMarshalMethods * a boolean, when true indicates to generated the marshaling * framework methods * */ public void setCreateMarshalMethods(final boolean createMarshalMethods) { _createMarshalMethods = createMarshalMethods; } //-- setCreateMarshalMethpds /** * Sets whether or not to create extra collection methods for accessing the * actual collection. * * @param extraMethods * a boolean that when true indicates that extra collection * accessor methods should be created. False by default. * @see org.exolab.castor.builder.SourceFactory#setReferenceMethodSuffix */ public void setCreateExtraMethods(final boolean extraMethods) { getInfoFactory().setCreateExtraMethods(extraMethods); } //-- setCreateExtraMethods /** * Sets the method suffix (ending) to use when creating the extra collection * methods. * * @param suffix * the method suffix to use when creating the extra collection * methods. If null or emtpty the default value, as specified in * CollectionInfo will be used. * @see org.exolab.castor.builder.SourceFactory#setCreateExtraMethods */ public void setReferenceMethodSuffix(final String suffix) { getInfoFactory().setReferenceMethodSuffix(suffix); } //-- setReferenceMethodSuffix /** * Sets whether or not to implement CastorTestable. * * @param testable * if true, indicates to implement CastorTestable */ public void setTestable(final boolean testable) { _testable = testable; } /** * Sets to true if SAX1 should be used in the marshall method. * * @param sax1 * true if SAX1 should be used. */ public void setSAX1(final boolean sax1) { _sax1 = sax1; } /** * Set to true if enumerated type lookups should be performed in a case * insensitive manner. * * @param caseInsensitive * when true */ public void setCaseInsensitive(final boolean caseInsensitive) { _enumerationFactory.setCaseInsensitive(caseInsensitive); } //------------------/ //- Public Methods -/ //------------------/ /** * Creates a new ClassInfo for the given XMLBindingComponent. * * @param component the XMLBindingComponent that abstracts all XML Schema * definition for a XML Schema component. * @param sgState The given state of the SourceGenerator. * @return an array of JClasses reflecting the given XMLBindingComponent. */ public JClass[] createSourceCode(final XMLBindingComponent component, final SGStateInfo sgState) { if (component == null) { throw new IllegalStateException("XMLBindingComponent may not be null."); } if (sgState == null) { throw new IllegalStateException("SGStateInfo may not be null."); } //-- check for previous JClass bindings JClass[] classes = sgState.getSourceCode(component.getAnnotated()); if (classes != null) { return classes; } _binding = component.getBinding(); if (sgState.verbose()) { String name = component.getXMLName(); if (name == null) { name = component.getJavaClassName(); } String msg = "Creating classes for: " + name; sgState.getDialog().notify(msg); } //0-- set the packageName String packageName = component.getJavaPackage(); if (packageName == null || packageName.length() == 0) { packageName = sgState.getPackageName(); } //1-- get the name //--if no package used then try to append the default package //--used in the SourceGenerator String className = component.getQualifiedName(); if (className.indexOf('.') == -1) { //--be sure it is a valid className className = getJavaNaming().toJavaClassName(className); className = resolveClassName(className, packageName); } //2-- check if we have to create an Item class boolean createGroupItem = component.createGroupItem(); if (createGroupItem) { className += ITEM_NAME; classes = new JClass[2]; } else { classes = new JClass[1]; } //3-- Create factoryState and chain it to sgState to prevent endless loop FactoryState state = new FactoryState(className, sgState, packageName, component); state.setCreateGroupItem(createGroupItem); if (sgState.getCurrentFactoryState() != null) { state.setParent(sgState.getCurrentFactoryState()); } sgState.setCurrentFactoryState(state); //--Prevent endless loop if (state.processed(component.getAnnotated())) { return new JClass[0]; } //-- Mark the enclosed annotated structure as processed in the //-- current FactoryState for preventing endless loop. state.markAsProcessed(component.getAnnotated()); ////////////////////////////////////////////////////// //NOTE: check that the component is not referring to //an imported schema to prevent class creation ////////////////////////////////////////////////////// //4-- intialization of the JClass ClassInfo classInfo = state.getClassInfo(); JClass jClass = state.getJClass(); initialize(jClass); if (classInfo.hasNature(XMLInfoNature.class.getName())) { final XMLInfoNature xmlNature = new XMLInfoNature(classInfo); //-- name information xmlNature.setNodeName(component.getXMLName()); //-- namespace information xmlNature.setNamespaceURI(component.getTargetNamespace()); //5--processing the type XMLType type = component.getXMLType(); boolean createForSingleGroup = false; boolean creatingForAnElement = (component.getAnnotated().getStructureType() == Structure.ELEMENT); //-- created from element definition information xmlNature.setElementDefinition(creatingForAnElement); // deal with substitution groups if (creatingForAnElement) { ElementDecl elementDeclaration = (ElementDecl) component.getAnnotated(); Enumeration<ElementDecl> possibleSubstitutes = elementDeclaration.getSubstitutionGroupMembers(); if (possibleSubstitutes.hasMoreElements()) { List<String> substitutionGroupMembers = new ArrayList<String>(); while (possibleSubstitutes.hasMoreElements()) { ElementDecl substitute = possibleSubstitutes.nextElement(); substitutionGroupMembers.add(substitute.getName()); } xmlNature.setSubstitutionGroups(substitutionGroupMembers); } } if (type != null) { if (type.isComplexType()) { processComplexType(component, sgState, state); } else if (type.isSimpleType()) { SimpleType simpleType = (SimpleType) type; //-- handle our special case for enumerated types if (simpleType.hasFacet(Facet.ENUMERATION)) { processSimpleTypeEnumeration(component, sgState, classInfo, simpleType); } else { ////////////////////////////////////////////////////////// //NOTE: generate sources if the flag for generating sources //from imported schemas is on ////////////////////////////////////////////////////////// return new JClass[0]; } } else if (type.isAnyType()) { //-- Do not create classes for AnyType xmlNature.setSchemaType(new XSClass(SGTypes.OBJECT)); return new JClass[0]; } } else { //--no type we must be facing an XML schema group //--MODEL GROUP OR GROUP createForSingleGroup = processSchemaGroup(component, state, classInfo); } //6--createGroupItem if (createGroupItem) { //-- create Bound Properties code if (component.hasBoundProperties()) { createPropertyChangeMethods(jClass); } sgState.bindReference(jClass, classInfo); classes[1] = jClass; //-- create main group class String fname = component.getJavaClassName() + ITEM_NAME; fname = getJavaNaming().toJavaMemberName(fname, false); FieldInfo fInfo = null; if (createForSingleGroup) { //By default a nested group Item can occur only once fInfo = getInfoFactory().createFieldInfo(new XSClass(jClass), fname); } else { fInfo = getInfoFactory().createCollection( new XSClass(jClass), "_items", fname, getJavaNaming(), getConfig().useJava50()); } fInfo.setContainer(true); String newClassName = className.substring(0, className.length() - 4); state = new FactoryState(newClassName, sgState, packageName, component); classInfo = state.getClassInfo(); jClass = state.getJClass(); initialize(jClass); if (type != null && type.isComplexType()) { ComplexType complexType = (ComplexType) type; if (complexType.isTopLevel() ^ creatingForAnElement) { //process attributes and content type since it has not be performed before Annotated saved = component.getAnnotated(); processAttributes(component.getBinding(), complexType, state); component.setView(saved); if (complexType.getContentType() == ContentType.mixed) { FieldInfo fieldInfo = _memberFactory.createFieldInfoForContent( component, new XSString(), getConfig().useJava50()); handleField(fieldInfo, state, component); } else if (complexType.getContentType().getType() == ContentType.SIMPLE) { SimpleContent simpleContent = (SimpleContent) complexType.getContentType(); SimpleType temp = simpleContent.getSimpleType(); XSType xsType = _typeConversion.convertType( temp, packageName, getConfig().useJava50()); FieldInfo fieldInfo = _memberFactory.createFieldInfoForContent( component, xsType, getConfig().useJava50()); handleField(fieldInfo, state, component); temp = null; } else { // handle multi-valued choice group xmlNature.setSchemaType(new XSClass(jClass)); } } } classInfo.addFieldInfo(fInfo); fInfo.getMemberAndAccessorFactory().createJavaField(fInfo, jClass); fInfo.getMemberAndAccessorFactory().createAccessMethods( fInfo, jClass, getConfig().useJava50(), getConfig().getAnnotationBuilders()); fInfo.getMemberAndAccessorFactory().generateInitializerCode( fInfo, jClass.getConstructor(0).getSourceCode()); //-- name information XMLInfoNature xmlInfoNature = new XMLInfoNature(classInfo); xmlInfoNature.setNodeName(component.getXMLName()); //-- mark as a container xmlInfoNature.setContainer(true); // -- if we have a superclass, make sure that the actual type extends it, not the // xxxItem holder class. String actSuperClass = classes[1].getSuperClassQualifiedName(); jClass.setSuperClass(actSuperClass); classes[1].setSuperClass(null); } } classes[0] = jClass; //7--set the class information given the component information //--base class String baseClass = component.getExtends(); if (baseClass != null && baseClass.length() > 0) { //-- at this point if a base class has been set //-- it means that it is a class generated for an element //-- that extends a class generated for a complexType. Thus //-- no change is possible if (jClass.getSuperClassQualifiedName() == null) { jClass.setSuperClass(baseClass); } } //--interface implemented String[] implemented = component.getImplements(); if (implemented != null) { for (int i = 0; i < implemented.length; i++) { String interfaceName = implemented[i]; if ((interfaceName != null) && (interfaceName.length() > 0)) { jClass.addInterface(interfaceName); } } } //--final jClass.getModifiers().setFinal(component.isFinal()); //--abstract if (component.isAbstract()) { jClass.getModifiers().setAbstract(true); classInfo.setAbstract(true); } processAppInfo(component.getAnnotated(), classInfo); extractAnnotations(component.getAnnotated(), jClass); createContructorForDefaultValueForSimpleContent(component.getAnnotated(), classInfo, sgState); makeMethods(component, sgState, state, jClass, baseClass); if (classInfo.hasNature(JDOClassInfoNature.class.getName())) { JDOClassInfoNature jdoNature = new JDOClassInfoNature(classInfo); if (jdoNature.getDetachable()) { createJdoTimestampImplementations(jClass); } } sgState.bindReference(jClass, classInfo); sgState.bindReference(component.getAnnotated(), classInfo); //-- Save source code bindings to prevent duplicate code generation sgState.bindSourceCode(component.getAnnotated(), classes); // custom annotations AnnotationBuilder[] annotationBuilders = getConfig().getAnnotationBuilders(); for (int i = 0; i < annotationBuilders.length; i++) { AnnotationBuilder annotationBuilder = annotationBuilders[i]; annotationBuilder.addClassAnnotations(classInfo, jClass); } return classes; } /** * This method adds a contructor with a string parameter to set the default * value of a simple content. If the provided class is not a Simple Content * class, nothing is done. * * @param annotated type information for the class to potentially create a * constructor for * @param classInfo the ClassInfo for the class to potentially create a * constructor for */ private void createContructorForDefaultValueForSimpleContent(final Annotated annotated, final ClassInfo classInfo, final SGStateInfo sgStateInfo) { FieldInfo textFieldInfo = classInfo.getTextField(); boolean generate = false; boolean inherited = false; // check if there is some field inherited from a type if (annotated instanceof ElementDecl) { XMLType type = ((ElementDecl) annotated).getType(); ClassInfo typeInfo = sgStateInfo.resolve(type); if (typeInfo != null && typeInfo.getTextField() != null) { textFieldInfo = typeInfo.getTextField(); inherited = true; } generate = (type.isComplexType() && ((ComplexType) type).isSimpleContent()); } // check if we are a complexType ourself else if (annotated instanceof ComplexType && ((ComplexType) annotated).isSimpleContent()) { generate = true; } // discard primitiv types and collections if (textFieldInfo != null) { XSType textFieldType = new XMLInfoNature(textFieldInfo).getSchemaType(); if (textFieldType != null && textFieldType.getJType().isArray()) { generate = false; } } if (!generate) { return; } XMLInfoNature xmlNature = new XMLInfoNature(textFieldInfo); // create constructor JClass jClass = classInfo.getJClass(); JParameter parameter = new JParameter(new JClass("java.lang.String"), "defaultValue"); JConstructor constructor = jClass.createConstructor(new JParameter[] {parameter}); JSourceCode sourceCode = new JSourceCode(); if (inherited) { sourceCode.add("super(defaultValue);"); } else { sourceCode.add("try {"); String defaultValue = xmlNature.getSchemaType().createDefaultValueWithString("defaultValue"); sourceCode.addIndented("setContent(" + defaultValue + ");"); sourceCode.add(" } catch(Exception e) {"); sourceCode.addIndented("throw new RuntimeException(\"Unable to cast default value for simple content!\");"); sourceCode.add(" } "); } constructor.setSourceCode(sourceCode); jClass.addConstructor(constructor); } /** * Extract 'documentation' annotations from the {@link Annotated} instance given. * @param annotated {@link Annotated} instance to extract annotations from. * @param jClass {@link JClass} instance to inject annotations into. */ private void extractAnnotations(final Annotated annotated, final JClass jClass) { //-- process annotation String comment = extractCommentsFromAnnotations(annotated); if (comment != null) { jClass.getJDocComment().setComment(comment); if (getConfig().generateExtraDocumentationMethods()) { generateExtraDocumentationMethods(annotated, jClass); } } } /** * Creates the #getXmlSchemaDocumentation methods for the given JClass. * * @param annotated * The {@link Annotation} instance to extract the XML schema * documentation instances from. * @param parent * the JClass to create the #getXmlSchemaDocumentation() methods * for */ private void generateExtraDocumentationMethods(final Annotated annotated, final JClass jClass) { JField documentationsField = new JField(new JClass("java.util.Map"), "_xmlSchemaDocumentations"); documentationsField.setComment("The content of the <xsd:documentation> elements"); documentationsField.setInitString("new java.util.HashMap()"); jClass.addMember(documentationsField); Enumeration<Annotation> annotations = annotated.getAnnotations(); while (annotations.hasMoreElements()) { Annotation annotation = annotations.nextElement(); Enumeration<Documentation> documentations = annotation.getDocumentation(); while (documentations.hasMoreElements()) { Documentation documentation = documentations.nextElement(); JConstructor defaultConstructor = jClass.getConstructor(0); String documentationContent = normalize(documentation.getContent()); documentationContent = StringUtil.replaceAll(documentationContent, "\n", "\"\n+ \" "); defaultConstructor.getSourceCode().add("_xmlSchemaDocumentations.put(\"" + documentation.getSource() + "\", \"" + documentationContent + "\");"); } } JMethod aMethod = new JMethod("getXmlSchemaDocumentations", new JClass("java.util.Map"), " A collection of documentation elements."); JSourceCode sourceCode = aMethod.getSourceCode(); sourceCode.add("return _xmlSchemaDocumentations;"); jClass.addMethod(aMethod); JMethod anotherMethod = new JMethod("getXmlSchemaDocumentation", new JClass("java.lang.String"), " A specific XML schema documentation element."); JParameter parameter = new JParameter(new JClass("java.lang.String"), "source"); anotherMethod.addParameter(parameter); sourceCode = anotherMethod.getSourceCode(); sourceCode.add("return (java.lang.String) _xmlSchemaDocumentations.get(source);"); jClass.addMethod(anotherMethod); } /** * Generate methods for our class. * * @param component * @param sgState * @param state * @param jClass * @param baseClass */ private void makeMethods( final XMLBindingComponent component, final SGStateInfo sgState, final FactoryState state, final JClass jClass, final String baseClass) { //NOTE: be careful with the derivation stuff when generating bounds properties if (_createMarshalMethods) { //-- #validate() createValidateMethods(jClass); //--don't generate marshal/unmarshal methods //--for abstract classes if (!component.isAbstract()) { //-- #marshal() createMarshalMethods(jClass); //-- #unmarshal() createUnmarshalMethods(jClass, sgState); } } //create equals() method? if (component.hasEquals()) { createEqualsMethod(jClass); createHashCodeMethod(jClass); } //implements CastorTestable? if (_testable) { createTestableMethods(jClass, state); } //-- This boolean is set to create bound properties //-- even if the user has set the SUPER CLASS property String superclassQualifiedName = jClass.getSuperClassQualifiedName(); if (superclassQualifiedName == null || superclassQualifiedName.equals(baseClass)) { //-- create Bound Properties code if (component.hasBoundProperties()) { createPropertyChangeMethods(jClass); } } } private boolean processSchemaGroup(final XMLBindingComponent component, final FactoryState state, final ClassInfo classInfo) { try { Group group = (Group) component.getAnnotated(); processContentModel(group, state); component.setView(group); //-- Check Group Type Order order = group.getOrder(); GroupInfo groupInfo = new XMLInfoNature(classInfo).getGroupInfo(); if (order == Order.choice) { groupInfo.setAsChoice(); } else if (order == Order.sequence) { groupInfo.setAsSequence(); } else { groupInfo.setAsAll(); } return group.getMaxOccurs() == 1; } catch (ClassCastException ce) { //--Should not happen throw new IllegalArgumentException("Illegal binding component: " + ce.getMessage()); } } private void processSimpleTypeEnumeration(final XMLBindingComponent component, final SGStateInfo sgState, final ClassInfo classInfo, final SimpleType simpleType) { //-- Don't create source code for simple types that //-- don't belong in the elements target namespace String tns = simpleType.getSchema().getTargetNamespace(); boolean create = false; if (tns == null) { create = (component.getTargetNamespace() == null); } else { create = tns.equals(component.getTargetNamespace()); } if (create) { ClassInfo tmpInfo = sgState.resolve(simpleType); JClass tmpClass = null; if (tmpInfo != null) { tmpClass = tmpInfo.getJClass(); } else { tmpClass = createSourceCode(component.getBinding(), simpleType, sgState); } XMLInfoNature xmlNature = new XMLInfoNature(classInfo); xmlNature.setSchemaType(new XSClass(tmpClass)); } } private void processComplexType(final XMLBindingComponent component, final SGStateInfo sgState, final FactoryState state) { XMLType type = component.getXMLType(); ClassInfo classInfo = state.getClassInfo(); JClass jClass = state.getJClass(); boolean creatingForAnElement = (component.getAnnotated().getStructureType() == Structure.ELEMENT); ComplexType complexType = (ComplexType) type; if (complexType.isTopLevel() && creatingForAnElement) { //--move the view and keep the structure Annotated saved = component.getAnnotated(); String previousPackage = component.getJavaPackage(); XMLBindingComponent baseComponent = new XMLBindingComponent( getConfig(), getGroupNaming()); baseComponent.setBinding(component.getBinding()); baseComponent.setView(complexType); //-- call createSourceCode to pre-process the complexType createSourceCode(baseComponent, sgState); String baseClassName = null; String basePackage = baseComponent.getJavaPackage(); //--if the base class is not in the same package //--of the current class then we have to qualify the base //--class if (basePackage != null && !basePackage.equals(previousPackage)) { baseClassName = baseComponent.getQualifiedName(); if (baseClassName.indexOf('.') == -1) { //--be sure it is a valid className baseClassName = getJavaNaming().toJavaClassName(baseClassName); } } else { baseClassName = baseComponent.getJavaClassName(); } jClass.setSuperClass(baseClassName); basePackage = null; baseClassName = null; component.setView(saved); saved = null; } else if (complexType.isTopLevel() || creatingForAnElement) { //generate class if the complexType is anonymous and we create classes //for an element OR if the complexType is top-level and we create //classes for it. //-- check Group type if (complexType.getParticleCount() == 1) { Particle particle = complexType.getParticle(0); if (particle.getStructureType() == Structure.GROUP) { Group group = (Group) particle; if (group.getOrder() == Order.choice) { new XMLInfoNature(classInfo).getGroupInfo().setAsChoice(); } } } Annotated saved = component.getAnnotated(); processComplexType(complexType, state); component.setView(saved); saved = null; } } /** * Creates the Java source code to support the given Simpletype. * * @param binding Current XML binding * @param simpleType the Simpletype to create the Java source for * @param sgState the current SGStateInfo (cannot be null). * @return the JClass representation of the given Simpletype */ public JClass createSourceCode(final ExtendedBinding binding, final SimpleType simpleType, final SGStateInfo sgState) { if (SimpleTypesFactory.isBuiltInType(simpleType.getTypeCode())) { String err = "You cannot construct a ClassInfo for a built-in SimpleType."; throw new IllegalArgumentException(err); } if (sgState == null) { throw new IllegalArgumentException("SGStateInfo cannot be null."); } //-- Unions are currently processed as the built-in //-- basetype for the member types of the Union, so //-- do nothing for now...however we can warn //-- user that no validation will be peformed on the //-- union if (simpleType.getStructureType() == Structure.UNION) { if (!sgState.getSuppressNonFatalWarnings()) { String message = "warning: support for unions is incomplete."; sgState.getDialog().notify(message); } return null; } ClassInfo cInfo = sgState.resolve(simpleType); if (cInfo != null) { return cInfo.getJClass(); } boolean enumeration = false; //-- class name information String typeName = simpleType.getName(); if (typeName == null) { Structure struct = simpleType.getParent(); FactoryState fstate = null; switch (struct.getStructureType()) { case Structure.ATTRIBUTE: typeName = ((AttributeDecl) struct).getName(); fstate = sgState.getCurrentFactoryState(); break; case Structure.ELEMENT: typeName = ((ElementDecl) struct).getName(); break; default: // Nothing to do break; } //-- In case of naming collision we append current class name if (fstate != null) { typeName = getJavaNaming().toJavaClassName(typeName); Structure attrDeclParent = ((AttributeDecl) struct).getParent(); if (attrDeclParent != null && attrDeclParent.getStructureType() == Structure.ATTRIBUTE_GROUP) { typeName = getJavaNaming().toJavaClassName( ((AttributeGroupDecl) attrDeclParent).getName() + typeName); } else { typeName = fstate.getJClass().getLocalName() + typeName; } } //-- otherwise (???) just append "Type" typeName += "Type"; } String className = getJavaNaming().toJavaClassName(typeName); //--XMLBindingComponent is only used to retrieve the java package //-- we need to optimize it by enabling the binding of simpleTypes. XMLBindingComponent comp = new XMLBindingComponent(getConfig(), getGroupNaming()); if (binding != null) { comp.setBinding(binding); } // set component view for anonymous simple types to parent if (simpleType.getName() == null) { Annotated annotated = (Annotated) simpleType.getParent(); comp.setView(annotated); } else { comp.setView(simpleType); } String packageName = comp.getJavaPackage(); if ((packageName == null) || (packageName.length() == 0)) { packageName = sgState.getPackageName(); } // reset component view for anonymous simple types if (simpleType.getName() == null) { comp.setView(simpleType); } if (simpleType.hasFacet(Facet.ENUMERATION)) { enumeration = true; // Fix packageName // TODO this is a hack I know, we should change this if ((packageName != null) && (packageName.length() > 0)) { packageName = packageName + "." + SourceGeneratorConstants.TYPES_PACKAGE; } else { packageName = SourceGeneratorConstants.TYPES_PACKAGE; } } String boundClassName = comp.getJavaClassName(); if ((boundClassName != null) && (boundClassName.length() > 0)) { className = boundClassName; typeName = boundClassName; } className = resolveClassName(className, packageName); FactoryState state = new FactoryState(className, sgState, packageName, comp, (enumeration && getConfig().useJava5Enums())); state.setParent(sgState.getCurrentFactoryState()); ClassInfo classInfo = state.getClassInfo(); JClass jClass = state.getJClass(); initialize(jClass); //-- XML information Schema schema = simpleType.getSchema(); XMLInfoNature xmlNature = new XMLInfoNature(classInfo); xmlNature.setNamespaceURI(schema.getTargetNamespace()); xmlNature.setNodeName(typeName); extractAnnotations(simpleType, jClass); XSClass xsClass = new XSClass(jClass, typeName); xmlNature.setSchemaType(xsClass); //-- handle enumerated types if (enumeration) { xsClass.setAsEnumerated(true); processEnumeration(binding, simpleType, state); } //-- create Bound Properties code if (state.hasBoundProperties() && !enumeration) { createPropertyChangeMethods(jClass); } if (classInfo.hasNature(JDOClassInfoNature.class.getName())) { JDOClassInfoNature jdoNature = new JDOClassInfoNature(classInfo); if (jdoNature.getDetachable()) { createJdoTimestampImplementations(jClass); } } sgState.bindReference(jClass, classInfo); sgState.bindReference(simpleType, classInfo); return jClass; } private void createJdoTimestampImplementations(final JClass jClass) { jClass.addInterface("org.exolab.castor.jdo.TimeStampable"); JField jdoTimestamp = new JField(JType.LONG, "_jdoTimeStamp"); jClass.addField(jdoTimestamp); JMethod getTSMethod = new JMethod("jdoGetTimeStamp", JType.LONG, "returns the current time stamp"); JSourceCode getSourceCode = getTSMethod.getSourceCode(); getSourceCode.addIndented("return _jdoTimeStamp;"); jClass.addMethod(getTSMethod); JMethod setTSMethod = new JMethod("jdoSetTimeStamp"); JParameter parameter = new JParameter(JType.LONG, "jdoTimeStamp"); setTSMethod.addParameter(parameter); JSourceCode setSourceCode = setTSMethod.getSourceCode(); setSourceCode.addIndented("this._jdoTimeStamp = jdoTimeStamp;"); jClass.addMethod(setTSMethod); } //-------------------/ //- Private Methods -/ //-------------------/ /** * Initializes the given JClass. * @param jClass the JClass to initialize */ private void initialize(final JClass jClass) { jClass.addInterface("java.io.Serializable"); if (getConfig().useJava50()) { JAnnotation serial = new JAnnotation(new JAnnotationType("SuppressWarnings")); serial.setValue(new String[] {"\"serial\""}); jClass.addAnnotation(serial); } //-- add default constructor JConstructor con = jClass.createConstructor(); jClass.addConstructor(con); con.getSourceCode().add("super();"); } //-- initialize /** * Creates the #marshal methods for the given JClass. * @param parent the JClass to create the #marshal methods for */ private void createPropertyChangeMethods(final JClass parent) { //-- add vector to hold listeners String vName = "propertyChangeSupport"; JField field = new JField(SGTypes.PROPERTY_CHANGE_SUPPORT, vName); field.getModifiers().makePrivate(); parent.addField(field); //---------------------------------/ //- notifyPropertyChangeListeners -/ //---------------------------------/ JMethod jMethod = new JMethod("notifyPropertyChangeListeners"); jMethod.getModifiers().makeProtected(); JDocComment jdc = jMethod.getJDocComment(); JDocDescriptor jdDesc = null; String desc = null; desc = "Notifies all registered PropertyChangeListeners " + "when a bound property's value changes."; jdc.appendComment(desc); jMethod.addParameter(new JParameter(SGTypes.STRING, "fieldName")); jdDesc = jdc.getParamDescriptor("fieldName"); jdDesc.setDescription("the name of the property that has changed."); jMethod.addParameter(new JParameter(SGTypes.OBJECT, "oldValue")); jdDesc = jdc.getParamDescriptor("oldValue"); jdDesc.setDescription("the old value of the property."); jMethod.addParameter(new JParameter(SGTypes.OBJECT, "newValue")); jdDesc = jdc.getParamDescriptor("newValue"); jdDesc.setDescription("the new value of the property."); parent.addMethod(jMethod); JSourceCode jsc = jMethod.getSourceCode(); //--fix for bug 1026 jsc.add("if ("); jsc.append(vName); jsc.append(" == null) return;"); jsc.add(vName); jsc.append(".firePropertyChange(fieldName,oldValue,newValue);"); //-----------------------------/ //- addPropertyChangeListener -/ //-----------------------------/ JType jType = new JClass("java.beans.PropertyChangeListener"); jMethod = new JMethod("addPropertyChangeListener"); desc = "Registers a PropertyChangeListener with this class."; jdc = jMethod.getJDocComment(); jdc.appendComment(desc); jMethod.addParameter(new JParameter(jType, "pcl")); desc = "The PropertyChangeListener to register."; jdDesc = jdc.getParamDescriptor("pcl"); jdDesc.setDescription(desc); parent.addMethod(jMethod); jsc = jMethod.getSourceCode(); jsc.add("if ("); jsc.append(vName); jsc.append(" == null) {"); jsc.addIndented(vName + " = new java.beans.PropertyChangeSupport(this);"); jsc.add("}"); jsc.add(vName); jsc.append(".addPropertyChangeListener(pcl);"); //--------------------------------/ //- removePropertyChangeListener -/ //--------------------------------/ jMethod = new JMethod("removePropertyChangeListener", JType.BOOLEAN, "always returns true if pcl != null"); desc = "Removes the given PropertyChangeListener " + "from this classes list of ProperyChangeListeners."; jdc = jMethod.getJDocComment(); jdc.appendComment(desc); jMethod.addParameter(new JParameter(jType, "pcl")); desc = "The PropertyChangeListener to remove."; jdDesc = jdc.getParamDescriptor("pcl"); jdDesc.setDescription(desc); parent.addMethod(jMethod); jsc = jMethod.getSourceCode(); jsc.add("if ("); jsc.append(vName); jsc.append(" == null) return false;"); jsc.add(vName); jsc.append(".removePropertyChangeListener(pcl);"); jsc.add("return true;"); } //-- createPropertyChangeMethods /** * Creates the #marshal methods for the given JClass. * @param parent the JClass to create the #marshal methods for */ private void createMarshalMethods(final JClass parent) { createMarshalMethods(parent, false); } //-- createMarshalMethods /** * Creates the #marshal methods for the given JClass. * @param parent the JClass to create the #marshal methods for * @param isAbstract true if the generated Class should be marked abstract */ private void createMarshalMethods(final JClass parent, final boolean isAbstract) { //-- create main marshal method JMethod jMethod = new JMethod("marshal"); jMethod.addException(SGTypes.MARSHAL_EXCEPTION, "if object is null or if any SAXException is thrown during marshaling"); jMethod.addException(SGTypes.VALIDATION_EXCEPTION, "if this object is an invalid instance according to the schema"); jMethod.addParameter(new JParameter(SGTypes.WRITER, "out")); //if (_config.useJava50()) { // jMethod.addAnnotation(new JAnnotation(new JAnnotationType("Override"))); //} parent.addMethod(jMethod); if (isAbstract) { jMethod.getModifiers().setAbstract(true); } else { JSourceCode jsc = jMethod.getSourceCode(); jsc.add("org.exolab.castor.xml.Marshaller.marshal(this, out);"); } //-- create helper marshal method //-- start helper marshal method, this method will //-- be built up as we process the given ElementDecl jMethod = new JMethod("marshal"); JClass jc = null; if (_sax1) { jc = new JClass("org.xml.sax.DocumentHandler"); } else { jc = new JClass("org.xml.sax.ContentHandler"); jMethod.addException(SGTypes.IO_EXCEPTION, "if an IOException occurs during marshaling"); } jMethod.addException(SGTypes.MARSHAL_EXCEPTION, "if object is null or if any SAXException is thrown during marshaling"); jMethod.addException(SGTypes.VALIDATION_EXCEPTION, "if this object is an invalid instance according to the schema"); jMethod.addParameter(new JParameter(jc, "handler")); parent.addMethod(jMethod); if (isAbstract) { jMethod.getModifiers().setAbstract(true); } else { JSourceCode jsc = jMethod.getSourceCode(); jsc.add("org.exolab.castor.xml.Marshaller.marshal(this, handler);"); } } //-- createMarshalMethods private void createUnmarshalMethods(final JClass parent, final SGStateInfo sgState) { //-- mangle method name to avoid compiler errors when this class is extended String methodName = "unmarshal"; if (sgState.getSourceGenerator().mappingSchemaType2Java()) { methodName += parent.getLocalName(); } //-- create main unmarshal method //-- search for proper base class // TODO[WG]: java 5.0 allows different types for unmarshal method in extension hierarchy JClass returnType; if (!getConfig().useJava50()) { returnType = findBaseClass(parent, sgState); } else { returnType = parent; } JMethod jMethod = new JMethod(methodName, returnType, "the unmarshaled " + returnType); jMethod.getModifiers().setStatic(true); jMethod.addException(SGTypes.MARSHAL_EXCEPTION, "if object is null or if any SAXException is thrown during marshaling"); jMethod.addException(SGTypes.VALIDATION_EXCEPTION, "if this object is an invalid instance according to the schema"); jMethod.addParameter(new JParameter(SGTypes.READER, "reader")); parent.addMethod(jMethod); JSourceCode jsc = jMethod.getSourceCode(); jsc.add("return ("); jsc.append(returnType.getName()); jsc.append(") org.exolab.castor.xml.Unmarshaller.unmarshal("); jsc.append(parent.getName()); jsc.append(".class, reader);"); } //-- createUnmarshalMethods /** * Returns the base class (as found in the schema) of the provided class. * Climbs the inheritence tree of the provided class to find and return the * base class of the provided class. * * @param jClass class to find the base class of * @param sgState current state of source generation * @return the base class of the provided class. */ private JClass findBaseClass(final JClass jClass, final SGStateInfo sgState) { JClass returnType = jClass; List<JClass> classes = new LinkedList<JClass>(); classes.add(returnType); while (returnType.getSuperClassQualifiedName() != null) { String superClassName = returnType.getSuperClassQualifiedName(); JClass superClass = sgState.getSourceCode(superClassName); if (superClass == null) { superClass = sgState.getImportedSourceCode(superClassName); } // A binding can cause us to have to look for the superclass class in // the package of the current class if (superClass == null && superClassName.indexOf('.') < 0) { String pkgName = returnType.getPackageName(); if (pkgName != null && pkgName.length() > 0) { superClassName = pkgName + "." + superClassName; superClass = sgState.getSourceCode(superClassName); } } // If returnClass has no superclass then it is the base class if (superClass == null) { break; } // Prevent inheritance loops from causing infinite loops if (classes.contains(superClass)) { StringBuilder buffer = new StringBuilder(64); buffer.append("Loop found in class hierarchy: "); for (Iterator<JClass> i = classes.iterator(); i.hasNext(); ) { JClass element = i.next(); // If JClass told us the source of the class (ComplexType, Element, ... // then we could report that to and make name conflicts more obvious. buffer.append(element.getName()); buffer.append(" -> "); } buffer.append(superClass.getName()); sgState.getDialog().notify(buffer.toString()); // FIXME We should probably throw an exception here break; } classes.add(superClass); returnType = superClass; } classes.clear(); return returnType; } /** * Create an "hashCode" method on the given JClass. * * @param jclass the JClass in wich we create the hashCode method. */ public void createHashCodeMethod(final JClass jclass) { if (jclass == null) { throw new IllegalArgumentException("JClass must not be null"); } // The argument is not null JField[] fields = jclass.getFields(); // Creates the method signature JMethod jMethod = new JMethod("hashCode", JType.INT, "a hash code value for the object."); jMethod.setComment("Overrides the java.lang.Object.hashCode method.\n" + "<p>\n" + "The following steps came from " + "<b>Effective Java Programming Language Guide</b> " + "by Joshua Bloch, Chapter 3"); // The hashCode method has no arguments jclass.addMethod(jMethod); JSourceCode jsc = jMethod.getSourceCode(); if (jclass.getSuperClassQualifiedName() == null) { jsc.add("int result = 17;"); } else { jsc.add("int result = super.hashCode();"); } jsc.add(""); jsc.add("long tmp;"); for (int i = 0; i < fields.length; i++) { JField temp = fields[i]; // If the field is an object the hashCode method is called recursively JType type = temp.getType(); String name = temp.getName(); if (type.isPrimitive()) { if (type == JType.BOOLEAN) { // Skip the _has_* variables only if they represent // a primitive that may or may not be present if (!name.startsWith("_has_") || jclass.getField(name.substring(5)) != null) { jsc.add("result = 37 * result + (" + name + "?0:1);"); } } else if (type == JType.BYTE || type == JType.INT || type == JType.SHORT) { jsc.add("result = 37 * result + " + name + ";"); } else if (type == JType.LONG) { jsc.add("result = 37 * result + (int)(" + name + "^(" + name + ">>>32));"); } else if (type == JType.FLOAT) { jsc.add("result = 37 * result + java.lang.Float.floatToIntBits(" + name + ");"); } else if (type == JType.DOUBLE) { jsc.add("tmp = java.lang.Double.doubleToLongBits(" + name + ");"); jsc.add("result = 37 * result + (int)(tmp^(tmp>>>32));"); } } else { if (getConfig().useCycleBreaker()) { // Calculates hashCode in an acyclic recursive manner jsc.add("if (" + name + " != null"); jsc.add(" && !org.castor.core.util.CycleBreaker.startingToCycle(" + name + ")) {"); } else { // Calculates hashCode in a recursive manner jsc.add("if (" + name + " != null) {"); } jsc.add(" result = 37 * result + " + name + ".hashCode();"); if (getConfig().useCycleBreaker()) { // Calculates hashCode in an acyclic recursive manner jsc.add(" org.castor.core.util.CycleBreaker.releaseCycleHandle(" + name + ");"); } jsc.add("}"); } } jsc.add(""); jsc.add("return result;"); } //createHashCodeMethod /** * Create an 'equals' method on the given JClass. * * @param jclass the Jclass in which we create the equals method */ public void createEqualsMethod(final JClass jclass) { if (jclass == null) { throw new IllegalArgumentException("JClass must not be null"); } JField[] fields = jclass.getFields(); JMethod jMethod = new JMethod("equals", JType.BOOLEAN, "true if the objects are equal."); jMethod.setComment("Overrides the java.lang.Object.equals method."); jMethod.addParameter(new JParameter(SGTypes.OBJECT, "obj")); if (getConfig().useJava50()) { jMethod.addAnnotation(new JAnnotation(new JAnnotationType("Override"))); } jclass.addMethod(jMethod); JSourceCode jsc = jMethod.getSourceCode(); jsc.add("if ( this == obj )"); jsc.indent(); jsc.add("return true;"); jsc.unindent(); if (jclass.getSuperClassQualifiedName() != null) { jsc.add(""); jsc.add("if (super.equals(obj)==false)"); jsc.indent(); jsc.add("return false;"); jsc.unindent(); } jsc.add(""); jsc.add("if (obj instanceof "); jsc.append(jclass.getLocalName()); jsc.append(") {"); jsc.add(""); if (fields.length > 0) { jsc.indent(); jsc.add(jclass.getLocalName()); jsc.append(" temp = ("); jsc.append(jclass.getLocalName()); jsc.append(")obj;"); if (getConfig().useCycleBreaker()) { jsc.add("boolean thcycle;"); jsc.add("boolean tmcycle;"); } } for (int i = 0; i < fields.length; i++) { JField temp = fields[i]; //Be careful to arrayList.... String name = temp.getName(); if (temp.getType().isPrimitive()) { jsc.add("if (this."); jsc.append(name); jsc.append(" != temp."); jsc.append(name); jsc.append(")"); } else { //-- Check first if the field is not null. This can occur while comparing //-- two objects that contains non-mandatory fields. We only have to check //-- one field since x.equals(null) should return false when equals() is //-- correctly implemented. jsc.add("if (this."); jsc.append(name); jsc.append(" != null) {"); jsc.indent(); jsc.add("if (temp."); jsc.append(name); jsc.append(" == null) "); jsc.indent(); jsc.append("return false;"); jsc.unindent(); if (getConfig().useCycleBreaker()) { jsc.add("if (this."); jsc.append(name); jsc.append(" != temp."); jsc.append(name); jsc.append(") {"); // This prevents string constants and improper DOM subtree self comparisons // (where Q(A(B)) and Q'(C(B)) are compared) screwing up cycle detection jsc.indent(); jsc.add("thcycle=org.castor.core.util.CycleBreaker.startingToCycle(this." + name + ");"); jsc.add("tmcycle=org.castor.core.util.CycleBreaker.startingToCycle(temp." + name + ");"); // equivalent objects *will* cycle at the same time jsc.add("if (thcycle!=tmcycle) {"); jsc.indent(); jsc.add("if (!thcycle) { org.castor.core.util.CycleBreaker.releaseCycleHandle(this." + name + "); };"); jsc.add("if (!tmcycle) { org.castor.core.util.CycleBreaker.releaseCycleHandle(temp." + name + "); };"); jsc.add("return false;"); jsc.unindent(); jsc.add("}"); // end of unequal cycle point test jsc.add("if (!thcycle) {"); jsc.indent(); } jsc.add("if (!"); // Special handling for comparing arrays if (temp.getType().isArray()) { jsc.append("java.util.Arrays.equals(this."); jsc.append(name); jsc.append(", temp."); jsc.append(name); jsc.append(")"); } else { jsc.append("this."); jsc.append(name); jsc.append(".equals(temp."); jsc.append(name); jsc.append(")"); } if (getConfig().useCycleBreaker()) { jsc.append(") {"); } else { jsc.append(") "); } jsc.indent(); if (getConfig().useCycleBreaker()) { jsc.add("org.castor.core.util.CycleBreaker.releaseCycleHandle(this." + name + ");"); jsc.add("org.castor.core.util.CycleBreaker.releaseCycleHandle(temp." + name + ");"); } jsc.add("return false;"); jsc.unindent(); if (getConfig().useCycleBreaker()) { jsc.add("}"); jsc.add("org.castor.core.util.CycleBreaker.releaseCycleHandle(this." + name + ");"); jsc.add("org.castor.core.util.CycleBreaker.releaseCycleHandle(temp." + name + ");"); } jsc.unindent(); if (getConfig().useCycleBreaker()) { jsc.add("}"); // end of !thcycle jsc.unindent(); jsc.add("}"); // end of this.name != that.name object constant check jsc.unindent(); } jsc.add("} else if (temp."); // end of != null jsc.append(name); jsc.append(" != null)"); } jsc.indent(); jsc.add("return false;"); jsc.unindent(); } jsc.add("return true;"); jsc.unindent(); jsc.add("}"); jsc.add("return false;"); } //CreateEqualsMethod /** * Implement org.castor.xmlctf.CastorTestable im the given JClass. * * @param jclass The JClass which will implement the CastorTestable Interface. * @param state our state, e.g., state of this Factory instance. */ public void createTestableMethods(final JClass jclass, final FactoryState state) { if (jclass == null) { throw new IllegalArgumentException("JClass must not be null"); } jclass.addInterface("org.castor.xmlctf.CastorTestable"); jclass.addImport("org.castor.xmlctf.CastorTestable"); jclass.addImport("org.castor.xmlctf.RandomHelper"); createRandomizeFields(jclass, state); // implementation of randomizeFields createDumpFields(jclass); // implementation of dumpFields } //CreateTestableMethods /** * Creates the randomizeFields method for a class that implements the * interface org.castor.xmlctf.CastorTestable. * * @param jclass The JClass which will implement the CastorTestable Interface. * @param state */ private void createRandomizeFields(final JClass jclass, final FactoryState state) { JMethod jMethod = new JMethod("randomizeFields"); jMethod.addException(new JClass("InstantiationException"), "if we try to instantiate an abstract class or interface"); jMethod.addException(new JClass("IllegalAccessException"), "if we do not have access to the field, for example if it is private"); jMethod.setComment("implementation of org.castor.xmlctf.CastorTestable"); jclass.addMethod(jMethod); JSourceCode jsc = jMethod.getSourceCode(); JField[] fields = jclass.getFields(); for (int i = 0; i < fields.length; i++) { JField temp = fields[i]; JType type = temp.getType(); String name = temp.getName(); if (state.getFieldInfoForChoice() != null && name.equals(state.getFieldInfoForChoice().getName())) { continue; } if (name.startsWith("enumConstants")) { continue; } if (name.startsWith("_")) { name = getJavaNaming().toJavaClassName(name.substring(1)); } else { name = getJavaNaming().toJavaClassName(name); } String setName = "set" + name; if (name.indexOf("Has") == -1) { if (type instanceof JCollectionType) { //Collection needs a specific handling int listLocat = name.lastIndexOf("List"); String tempName = name; if (listLocat != -1) { tempName = tempName.substring(0, listLocat); } String methodName = getJavaNaming().toJavaClassName(tempName); methodName = "get" + methodName; JMethod method = jclass.getMethod(methodName, 0); // TODO handle the Item introduced in with the group handling if (method == null) { continue; } String componentName = method.getReturnType().getName(); jsc.add(temp.getName()); jsc.append(" = RandomHelper.getRandom("); jsc.append(temp.getName()); jsc.append(", "); jsc.append(componentName); jsc.append(".class);"); } else if (type.isPrimitive()) { // Primitive jsc.add(setName); jsc.append("(RandomHelper.getRandom("); jsc.append(temp.getName()); jsc.append("));"); } else if (type.isArray()) { // Array jsc.add(setName); jsc.append("(("); jsc.append(type.toString()); jsc.append(")RandomHelper.getRandom("); jsc.append(temp.getName()); // Any Class will do, but Array.class seems appropriate jsc.append(", java.lang.reflect.Array.class));"); } else { // Object jsc.add(setName); jsc.append("(("); jsc.append(type.getName()); jsc.append(")RandomHelper.getRandom("); jsc.append(temp.getName()); jsc.append(", "); jsc.append(type.getName()); jsc.append(".class));"); } jsc.add(""); } } } /** * Creates the dumpFields method for a class that implements the interface * org.castor.xmlctf.CastorTestable. * * @param jclass The JClass which will implement the CastorTestable Interface. */ private void createDumpFields(final JClass jclass) { JMethod jMethod = new JMethod("dumpFields", SGTypes.STRING, "a String representation of all of the fields for " + jclass.getName()); jMethod.setComment("implementation of org.castor.xmlctf.CastorTestable"); jclass.addMethod(jMethod); JSourceCode jsc = jMethod.getSourceCode(); jsc.add("StringBuffer result = new StringBuffer(\"DumpFields() for element: "); jsc.append(jclass.getName()); jsc.append("\\n\");"); JField[] fields = jclass.getFields(); for (int i = 0; i < fields.length; i++) { JField temp = fields[i]; String name = temp.getName(); if ((temp.getType().isPrimitive()) || temp.getType().getName().startsWith("java.lang.")) { //hack when using the option 'primitivetowrapper' //this should not interfere with other cases jsc.add("result.append(\"Field "); jsc.append(name); jsc.append(":\" +"); jsc.append(name); jsc.append("+\"\\n\");"); } else if (temp.getType().isArray()) { jsc.add("if ("); jsc.append(name); jsc.append(" != null) {"); jsc.indent(); jsc.add("result.append(\"[\");"); jsc.add("for (int i = 0; i < "); jsc.append(name); jsc.append(".length; i++) {"); jsc.indent(); jsc.add("result.append("); jsc.append(name); jsc.append("[i] + \" \");"); jsc.unindent(); jsc.add("}"); jsc.add("result.append(\"]\");"); jsc.unindent(); jsc.add("}"); } else { jsc.add("if ( ("); jsc.append(name); jsc.append(" != null) && ("); jsc.append(name); jsc.append(".getClass().isAssignableFrom(CastorTestable.class)))"); jsc.indent(); jsc.add("result.append(((CastorTestable)"); jsc.append(name); jsc.append(").dumpFields());"); jsc.unindent(); jsc.add("else result.append(\"Field "); jsc.append(name); jsc.append(":\" +"); jsc.append(name); jsc.append("+\"\\n\");"); } jsc.add(""); } jsc.add(""); jsc.add("return result.toString();"); } /** * Creates the Validate methods for the given JClass. * @param jClass the JClass to create the Validate methods for */ private void createValidateMethods(final JClass jClass) { JMethod jMethod = null; JSourceCode jsc = null; //-- #validate jMethod = new JMethod("validate"); jMethod.addException(SGTypes.VALIDATION_EXCEPTION, "if this object is an invalid instance according to the schema"); jClass.addMethod(jMethod); jsc = jMethod.getSourceCode(); jsc.add("org.exolab.castor.xml.Validator validator = new "); jsc.append("org.exolab.castor.xml.Validator();"); jsc.add("validator.validate(this);"); //-- #isValid jMethod = new JMethod("isValid", JType.BOOLEAN, "true if this object is valid according to the schema"); jsc = jMethod.getSourceCode(); jsc.add("try {"); jsc.indent(); jsc.add("validate();"); jsc.unindent(); jsc.add("} catch (org.exolab.castor.xml.ValidationException vex) {"); jsc.indent(); jsc.add("return false;"); jsc.unindent(); jsc.add("}"); jsc.add("return true;"); jClass.addMethod(jMethod); } //-- createValidateMethods //-------------------/ //- Private Methods -/ //-------------------/ /** * Resolves the className out of the given name and the packageName. * * @param name the class name * @param packageName the package name * @return the full qualified class name. */ private String resolveClassName(final String name, final String packageName) { if ((packageName != null) && (packageName.length() > 0)) { return packageName + "." + name; } return name; } //-- resolveClassName ////////////////////////////////////////////// //Process XML Schema structures //Note: This code is XML specific, it has to be moved somehow in XMLBindingComponent. //The aim of the SourceFactory is to generate code from a BindingComponent. /////////////////////////////////////////////// /** * This method handles the processing of AppInfo elements from the input * schema and adds the information found therein to the specified * {@link ClassInfo}. * * @param annotated * the {@link org.exolab.castor.xml.schema.Structure} holding the * {@link AppInfo} annotations. * @param cInfo * the {@link ClassInfo} to store the information found in the * AppInfos in. * @see ClassInfo * @see AppInfo * @see Annotated */ private void processAppInfo(final Annotated annotated, final ClassInfo cInfo) { Enumeration<Annotation> annotations = annotated.getAnnotations(); while (annotations.hasMoreElements()) { Annotation ann = annotations.nextElement(); Enumeration<AppInfo> appInfos = ann.getAppInfo(); while (appInfos.hasMoreElements()) { AppInfo appInfo = appInfos.nextElement(); List<?> content = appInfo.getJdoContent(); Iterator<?> it = content.iterator(); if (it.hasNext()) { cInfo.addNature(JDOClassInfoNature.class.getName()); JDOClassInfoNature cNature = new JDOClassInfoNature(cInfo); while (it.hasNext()) { Object tmpObject = it.next(); if (tmpObject instanceof Table) { Table table = (Table) tmpObject; cNature.setTableName(table.getName()); cNature.setAccessMode(AccessMode.valueOf("shared")); cNature.setDetachable(table.isDetachable()); // TODO: Uncomment next line as soon as Annotation Classes have been updated! // cNature.setAccessMode(AccessMode.valueOf(table.getAccessMode().toString())); PrimaryKey pk = table.getPrimaryKey(); Iterator<String> pIt = pk.iterateKey(); while (pIt.hasNext()) { cNature.addPrimaryKey(pIt.next()); } } } } } } } /** * This method handles the processing of AppInfo elements from the input * schema and adds the information found therein to the specified * {@link FieldInfo}. * * @param annotated * the {@link org.exolab.castor.xml.schema.Structure} holding the * {@link AppInfo} annotations. * @param fInfo * the {@link ClassInfo} to store the information found in the * AppInfos in. * @see FieldInfo * @see AppInfo * @see Annotated */ private void processAppInfo(final Annotated annotated, final FieldInfo fInfo) { Enumeration<Annotation> annotations = annotated.getAnnotations(); while (annotations.hasMoreElements()) { Annotation ann = annotations.nextElement(); Enumeration<AppInfo> appInfos = ann.getAppInfo(); while (appInfos.hasMoreElements()) { AppInfo appInfo = appInfos.nextElement(); List<?> content = appInfo.getJdoContent(); Iterator<?> it = content.iterator(); if (it.hasNext()) { while (it.hasNext()) { Object tmpObject = it.next(); if (tmpObject instanceof Column) { fInfo.addNature(JDOFieldInfoNature.class.getName()); JDOFieldInfoNature fNature = new JDOFieldInfoNature(fInfo); Column column = (Column) tmpObject; fNature.setColumnName(column.getName()); fNature.setColumnType(column.getType()); fNature.setReadOnly(column.isReadOnly()); fNature.setDirty(false); fNature.setDirty(column.getDirty()); } else if (tmpObject instanceof OneToOne) { OneToOne relation = (OneToOne) tmpObject; fInfo.addNature(JDOOneToOneNature.class.getName()); JDOOneToOneNature oneNature = new JDOOneToOneNature(fInfo); oneNature.addForeignKey(relation.getName()); oneNature.setDirty(relation.isDirty()); oneNature.setReadOnly(relation.isReadOnly()); } else if (tmpObject instanceof OneToMany) { OneToMany relation = (OneToMany) tmpObject; fInfo.addNature(JDOOneToManyNature.class.getName()); JDOOneToManyNature manyNature = new JDOOneToManyNature(fInfo); manyNature.addForeignKey(relation.getName()); manyNature.setDirty(relation.isDirty()); manyNature.setReadOnly(relation.isReadOnly()); } } } } } } /** * Process the attributes contained in this complexType. * @param binding * @param complexType the given complex type. * @param state the given FactoryState */ private void processAttributes(final ExtendedBinding binding, final ComplexType complexType, final FactoryState state) { if (complexType == null) { return; } Enumeration<AttributeDecl> enumeration = complexType.getAttributeDecls(); XMLBindingComponent component = new XMLBindingComponent(getConfig(), getGroupNaming()); if (_binding != null) { component.setBinding(_binding); } while (enumeration.hasMoreElements()) { AttributeDecl attr = enumeration.nextElement(); component.setView(attr); //-- if we have a new SimpleType...generate ClassInfo SimpleType sType = attr.getSimpleType(); // look for simpleType def in base type(s) XMLType baseXMLType = complexType.getBaseType(); while (sType == null) { // If no simple type found: Get the same attribute of the base type. // If base type is not complex, forget it; break out of loop now. if (baseXMLType == null || !(baseXMLType instanceof ComplexType)) { break; } // There's a base complexType; get the attribute with the same name // as this attribute (=attr) from it final ComplexType baseComplexType = (ComplexType) baseXMLType; AttributeDecl baseAttribute = baseComplexType.getAttributeDecl(attr.getName()); if (baseAttribute != null) { // See if this one has a simple-type... sType = baseAttribute.getSimpleType(); if (sType != null) { attr.setSimpleType(sType); break; } } // ... if not, go another step higher in the class hierarchy baseXMLType = baseXMLType.getBaseType(); } // Look for referenced type (if any) for setting type, and use // it, if found. if (sType == null && attr.getReference() != null) { SimpleType referencedSimpleType = attr.getReference().getSimpleType(); attr.setSimpleType(referencedSimpleType); sType = referencedSimpleType; } if (sType != null && !(SimpleTypesFactory.isBuiltInType(sType.getTypeCode()))) { if (sType.getSchema() == component.getSchema() && state.resolve(sType) == null) { if (sType.hasFacet(Facet.ENUMERATION)) { createSourceCode(component.getBinding(), sType, state.getSGStateInfo()); } } } FieldInfo fieldInfo = _memberFactory.createFieldInfo( component, state, getConfig().useJava50()); handleField(fieldInfo, state, component); } } /** * @param complexType the ComplexType to process * @param state the FactoryState. */ private void processComplexType(final ComplexType complexType, final FactoryState state) { XMLBindingComponent component = new XMLBindingComponent(getConfig(), getGroupNaming()); if (_binding != null) { component.setBinding(_binding); } component.setView(complexType); String typeName = component.getXMLName(); ClassInfo classInfo = state.getClassInfo(); XMLInfoNature xmlNature = new XMLInfoNature(classInfo); xmlNature.setSchemaType(new XSClass(state.getJClass(), typeName)); /// I don't believe this should be here: kv 20030423 ///classInfo.setNamespaceURI(component.getTargetNamespace()); //- Handle derived types XMLType base = complexType.getBaseType(); //-- if the base is a complexType, we need to process it if (base != null) { if (base.isComplexType()) { String baseClassName = null; component.setView(base); //-- Is this base type from the schema we are currently generating source for? ////////////////////////////////////////////////////////// //NOTE: generate sources if the flag for generating sources //from imported schemas in on ////////////////////////////////////////////////////////// if (base.getSchema() == complexType.getSchema()) { ClassInfo cInfo = state.resolve(base); //--no classInfo yet so no source code available //--for the base type: we need to generate it if (cInfo == null) { JClass[] classes = createSourceCode(component, state.getSGStateInfo()); cInfo = state.resolve(base); baseClassName = classes[0].getName(); } else { baseClassName = cInfo.getJClass().getName(); } //set the base class classInfo.setBaseClass(cInfo); } else { //-- Create qualified class name for a base type class //-- from another package baseClassName = component.getQualifiedName(); } //-- Set super class //-- and reset the view on the current ComplexType component.setView(complexType); // only set a super class name if the current complexType is not a // restriction of a simpleContent (--> no object hierarchy, only content hierarchy) /* Note: There are times when a simpleContent restriction needs to extend the hierarchy, such as a restriction of a restriction, so I'm commenting out the following line for now. see bug 1875 for more details. If this causes any regressions we'll need to find a more appropriate solution. if (! ( complexType.isRestricted() && ((ComplexType)base).isSimpleContent() ) ) */ state.getJClass().setSuperClass(baseClassName); } //--complexType //--if the content type is a simpleType create a field info for it. if (complexType.getContentType().getType() == ContentType.SIMPLE) { SimpleContent simpleContent = (SimpleContent) complexType.getContentType(); SimpleType temp = simpleContent.getSimpleType(); SimpleType baseType = (SimpleType) temp.getBaseType(); XSType xsType = _typeConversion.convertType( temp, state.getPackageName(), getConfig().useJava50()); FieldInfo fieldInfo = null; if ((baseType != null) && extendsSimpleType(state.getJClass(), baseType, state)) { if (xsType.isEnumerated()) { fieldInfo = _memberFactory.createFieldInfoForContent( component, xsType, getConfig().useJava50()); fieldInfo.setBound(false); handleField(fieldInfo, state, component); //-- remove getter since we don't need to override the original getter String mname = fieldInfo.getReadMethodName(); JClass jClass = state.getJClass(); JMethod method = jClass.getMethod(mname, 0); jClass.removeMethod(method); //-- update setter method mname = fieldInfo.getWriteMethodName(); method = jClass.getMethod(mname, 0); JSourceCode jsc = method.getSourceCode(); jsc.add("super."); jsc.append(mname); jsc.append("(this."); jsc.append(fieldInfo.getName()); jsc.append(".toString());"); } //-- else just use superclass setters/getters //-- do nothing } else { // while (temp.getBaseType() != null && !temp.isBuiltInType()) { temp = (SimpleType) temp.getBaseType(); } xsType = _typeConversion.convertType( temp, state.getPackageName(), getConfig().useJava50()); fieldInfo = _memberFactory.createFieldInfoForContent( component, xsType, getConfig().useJava50()); handleField(fieldInfo, state, component); } } } //--base not null //---------------------/ //- handle attributes -/ //- and mixed content -/ //---------------------/ if (!state.isCreateGroupItem()) { processAttributes(component.getBinding(), complexType, state); //--reset the view on the current ComplexType component.setView(complexType); if (complexType.getContentType() == ContentType.mixed) { FieldInfo fieldInfo = _memberFactory.createFieldInfoForContent( component, new XSString(), getConfig().useJava50()); handleField(fieldInfo, state, component); } } //--process the contentModelGroup processContentModel(complexType, state); } //-- processComplextype /** * Processes the given ContentModelGroup. This method is responsible for * creating FieldInfos (or sometimes ClassInfos) for elements and * model group contained in the given ContentModelGroup. * * @param model the ContentModelGroup to process * @param state the current FactoryState. */ private void processContentModel(final ContentModelGroup model, final FactoryState state) { //------------------------------/ //- handle elements and groups -/ //------------------------------/ ContentModelGroup contentModel = model; Enumeration<Annotated> enumeration = contentModel.enumerate(); //-- handle choice item if (new XMLInfoNature(state.getClassInfo()).isChoice() && state.getFieldInfoForChoice() == null) { state.setFieldInfoForChoice(_memberFactory.createFieldInfoForChoiceValue()); state.getFieldInfoForChoice().getMemberAndAccessorFactory().createJavaField( state.getFieldInfoForChoice(), state.getJClass()); state.getFieldInfoForChoice().getMemberAndAccessorFactory().createAccessMethods( state.getFieldInfoForChoice(), state.getJClass(), getConfig().useJava50(), getConfig().getAnnotationBuilders()); } FieldInfo fieldInfo = null; XMLBindingComponent component = new XMLBindingComponent(getConfig(), getGroupNaming()); if (_binding != null) { component.setBinding(_binding); } while (enumeration.hasMoreElements()) { Annotated annotated = enumeration.nextElement(); component.setView(annotated); switch (annotated.getStructureType()) { case Structure.ELEMENT: //-- handle element declarations fieldInfo = _memberFactory.createFieldInfo( component, state, getConfig().useJava50()); //-- Fix for element declarations being used in //-- a group with minOccurs = 0; //-- (kvisco - 20021007) if (contentModel.getMinOccurs() == 0) { XMLInfoNature xmlNature = new XMLInfoNature(fieldInfo); xmlNature.setRequired(false); } handleField(fieldInfo, state, component); break; case Structure.GROUP: //-- handle groups Group group = (Group) annotated; //set the compositor if ((contentModel instanceof ComplexType) || (contentModel instanceof ModelGroup)) { if (group.getOrder() == Order.choice) { new XMLInfoNature(state.getClassInfo()).getGroupInfo().setAsChoice(); } else if (group.getOrder() == Order.all) { new XMLInfoNature(state.getClassInfo()).getGroupInfo().setAsAll(); } else if (group.getOrder() == Order.sequence) { new XMLInfoNature(state.getClassInfo()).getGroupInfo().setAsSequence(); } } //-- create class member,if necessary if (!((contentModel instanceof ComplexType) || (contentModel instanceof ModelGroup))) { if (contentModel instanceof ModelGroup) { ModelGroup mg = (ModelGroup) contentModel; if (mg.isReference()) { contentModel = mg.getReference(); } } if (contentModel.getParticleCount() > 0) { fieldInfo = _memberFactory.createFieldInfo( component, state.getSGStateInfo(), getConfig().useJava50()); handleField(fieldInfo, state, component); } } else { //--else we just flatten the group processContentModel(group, state); } break; case Structure.MODELGROUP: ModelGroup modelgroup = (ModelGroup) annotated; //--a Model Group definition can only referenced //--another group at this point. //get the contentModel and proccess it if (modelgroup.getName() != null) { //create the field info for the element //that is referring to a model group in order //not to loose the Particle information if (modelgroup.isReference()) { modelgroup = modelgroup.getReference(); } if (modelgroup.getParticleCount() > 0) { fieldInfo = _memberFactory.createFieldInfo( component, state.getSGStateInfo(), getConfig().useJava50()); handleField(fieldInfo, state, component); } break; } //--else we just flatten the group processContentModel(modelgroup.getContentModelGroup(), state); break; case Structure.WILDCARD: Wildcard wildcard = (Wildcard) annotated; FieldInfo fieldForAny = _memberFactory.createFieldInfoForAny( wildcard, getConfig().useJava50()); handleField(fieldForAny, state, component); break; default: break; } } } //-- process(ContentModelGroup) /** * Creates all the necessary enumeration code from the given Creates all the * necessary enumeration code from the given SimpleType. Enumerations are * handled a couple ways. * * @param binding CUrrent XML binding * @param simpleType the SimpleType we are processing an enumeration for * @param state our current state * @see #processEnumerationAsBaseType */ private void processEnumeration(final ExtendedBinding binding, final SimpleType simpleType, final FactoryState state) { // Added by robertlaferla at comcast dot net 01/21/2004 if (getConfig().useEnumeratedTypeInterface()) { state.getJClass().addInterface(ENUM_ACCESS_INTERFACE); } // end enumTypeInterface switch (_enumerationType) { case BASE_TYPE_ENUMERATION: processEnumerationAsBaseType(binding, simpleType, state); break; default: processEnumerationAsNewObject(binding, simpleType, state); break; } } //-- processEnumeration /** * Creates all the necessary enumeration code from the given SimpleType. Delegates * to EnumerationFactory. * * @param binding Current XML binding * @param simpleType the SimpleType we are processing an enumeration for * @param state our current state * @see #processEnumerationAsBaseType */ private void processEnumerationAsNewObject(final ExtendedBinding binding, final SimpleType simpleType, final FactoryState state) { _enumerationFactory.processEnumerationAsNewObject(binding, simpleType, state); if (_testable && state.getJClass() instanceof JEnum) { createTestableMethods(state.getJClass(), state); } } //-- processEnumerationAsNewObject /** * Delegates creation of enumeration code to EnumerationFactory. * * @param binding Current XML binding * @param simpleType the SimpleType we are processing an enumeration for * @param state our current state */ private void processEnumerationAsBaseType(final ExtendedBinding binding, final SimpleType simpleType, final FactoryState state) { _enumerationFactory.processEnumerationAsBaseType(binding, simpleType, state); } //-- processEnumerationAsBaseType /** * Adds a given FieldInfo to the JClass and ClassInfo stored in the given * FactoryState. * * @param fieldInfo The fieldInfo to add * @param state the current FactoryState * @param component the current XMLBindingComponent to read AppInfos from. */ private void handleField(final FieldInfo fieldInfo, final FactoryState state, final XMLBindingComponent component) { if (fieldInfo == null) { return; } XMLInfoNature xmlNature = new XMLInfoNature(fieldInfo); if (CLASS_METHOD_SUFFIX.equals(fieldInfo.getMethodSuffix())) { SGStateInfo sInfo = state.getSGStateInfo(); if (!sInfo.getSuppressNonFatalWarnings()) { String warn = "warning a field name conflicts with \"" + CLASS_KEYWORD + "\", please use a binding file to specify " + "a different name for the " + xmlNature.getNodeTypeName() + " '" + xmlNature.getNodeName() + "'."; sInfo.getDialog().notify(warn); } } else if (CLASS_KEYWORD.equals(xmlNature.getNodeName())) { SGStateInfo sInfo = state.getSGStateInfo(); if (!sInfo.getSuppressNonFatalWarnings()) { String warn = "warning a field name conflicts with \"" + CLASS_KEYWORD + "\" and is being replaced by \"clazz\". " + "You may use a binding file to specify a different " + "name for the " + xmlNature.getNodeTypeName() + " '" + xmlNature.getNodeName() + "'."; sInfo.getDialog().notify(warn); } } processAppInfo(component.getAnnotated(), fieldInfo); JSourceCode scInitializer = state.getJClass().getConstructor(0).getSourceCode(); ClassInfo base = state.getClassInfo().getBaseClass(); boolean present = false; if (base != null) { switch (new XMLInfoNature(fieldInfo).getNodeType()) { case ATTRIBUTE: present = (base.getAttributeField(xmlNature.getNodeName()) != null); break; case ELEMENT: String baseNodeName = xmlNature.getNodeName(); // TODO[WG]: replace this error check with something more meaningful if (baseNodeName != null && !(baseNodeName.equals(XMLInfo.CHOICE_NODE_NAME_ERROR_INDICATION))) { // check whether there is an equally named field in the base // class, and don't forget to consider the namspaces as well to // determine whether the fields are really equal. FieldInfo inheritedFieldInfo = base.getElementField(baseNodeName); if (inheritedFieldInfo != null) { if (xmlNature.getNamespaceURI() .equals(new XMLInfoNature(inheritedFieldInfo).getNamespaceURI())) { present = true; } } } break; default: break; } } state.getClassInfo().addFieldInfo(fieldInfo); present = present && !xmlNature.isMultivalued(); //create the relevant Java fields only if the field //info is not yet in the base classInfo or if it is not a collection if (!present) { if (state.getFieldInfoForChoice() != null) { if (fieldInfo != state.getFieldInfoForChoice()) { fieldInfo.setFieldInfoReference(state.getFieldInfoForChoice()); } } fieldInfo.getMemberAndAccessorFactory().createJavaField( fieldInfo, state.getJClass()); //-- do not create access methods for transient fields if (!fieldInfo.isTransient()) { fieldInfo.getMemberAndAccessorFactory().createAccessMethods( fieldInfo, state.getJClass(), getConfig().useJava50(), getConfig().getAnnotationBuilders()); if (fieldInfo.isBound()) { state.setBoundProperties(true); } } } //-- Add initialization code fieldInfo.getMemberAndAccessorFactory().generateInitializerCode(fieldInfo, scInitializer); } //-- handleField /** * Returns true if the given JClass extends the class represented by the * given SimpleType. * * @param jClass the JClass to check * @param type the SimpleType to check against * @param state the FactoryState * * @return true if the given JClass extends the class associated with the * given SimpleType, otherwise false. */ private boolean extendsSimpleType(final JClass jClass, final SimpleType type, final FactoryState state) { String superClassName = jClass.getSuperClassQualifiedName(); if (superClassName != null) { ClassInfo cInfo = state.resolve(type); if (cInfo != null) { return superClassName.equals(cInfo.getJClass().getName()); } } return false; } //-- extendsSimpleType } //-- SourceFactory