/*
* 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