Index: src/main/java/org/codehaus/modello/plugin/xsd/XsdGenerator.java =================================================================== --- src/main/java/org/codehaus/modello/plugin/xsd/XsdGenerator.java (r‚vision 607) +++ src/main/java/org/codehaus/modello/plugin/xsd/XsdGenerator.java (copie de travail) @@ -1,7 +1,7 @@ package org.codehaus.modello.plugin.xsd; /* - * Copyright (c) 2005, Codehaus.org + * Copyright (c) 2004, Codehaus.org * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -29,6 +29,9 @@ import org.codehaus.modello.model.ModelField; import org.codehaus.modello.plugin.AbstractModelloGenerator; import org.codehaus.modello.plugin.model.ModelClassMetadata; +import org.codehaus.modello.plugin.model.ModelFieldMetadata; +import org.codehaus.modello.plugins.xml.XmlFieldMetadata; +import org.codehaus.modello.plugins.xml.XmlModelMetadata; import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; import org.codehaus.plexus.util.xml.XMLWriter; @@ -93,9 +96,14 @@ w.startElement( "xs:schema" ); w.addAttribute( "xmlns:xs", "http://www.w3.org/2001/XMLSchema" ); w.addAttribute( "elementFormDefault", "qualified" ); + + XmlModelMetadata modelMetadata = (XmlModelMetadata) objectModel.getMetadata( XmlModelMetadata.ID ); + + String nameSpace = modelMetadata.getNameSpace() + "/" + getGeneratedVersion(); + // TODO: make configurable - w.addAttribute( "targetNamespace", "http://maven.apache.org/POM/4.0.0" ); - w.addAttribute( "xmlns", "http://maven.apache.org/POM/4.0.0" ); + w.addAttribute( "targetNamespace", nameSpace ); + w.addAttribute( "xmlns", nameSpace ); ModelClass root = objectModel.getClass( objectModel.getRoot( getGeneratedVersion() ), getGeneratedVersion() ); @@ -165,91 +173,165 @@ writeClassDocumentation( w, modelClass ); - w.startElement( "xs:all" ); + List fields = getFieldsForClass( objectModel, modelClass ); - List fields = new ArrayList(); - while ( modelClass != null ) + List attributeFields = getAttributeFieldsForClass( objectModel, modelClass ); + + fields.removeAll( attributeFields ); + + Set toWrite = new HashSet(); + + if ( fields.size() > 0 ) { - fields.addAll( modelClass.getFields( getGeneratedVersion() ) ); - String superClass = modelClass.getSuperClass(); - if ( superClass != null ) + /* + * If we have any flat association under this element, we have to go + * to an xs:sequence instead of an xs:all, since xs:all doesn't allow + * for many multiplicity elements. + */ + if ( hasFlatAssociation( objectModel, fields ) ) { - modelClass = objectModel.getClass( superClass, getGeneratedVersion() ); + w.startElement( "xs:sequence" ); } else { - modelClass = null; + w.startElement( "xs:all" ); } - } - Set toWrite = new HashSet(); - for ( Iterator j = fields.iterator(); j.hasNext(); ) - { - ModelField field = (ModelField) j.next(); + for ( Iterator j = fields.iterator(); j.hasNext(); ) + { + ModelField field = (ModelField) j.next(); - w.startElement( "xs:element" ); - w.addAttribute( "name", field.getName() ); + w.startElement( "xs:element" ); - // Usually, would only do this if the field is not "required", but due to inheritence, it may be present, - // even if not here, so we need to let it slide - w.addAttribute( "minOccurs", "0" ); + // Usually, would only do this if the field is not "required", but + // due to inheritence, it may be present, + // even if not here, so we need to let it slide + w.addAttribute( "minOccurs", "0" ); - String xsdType = getXsdType( field.getType() ); - if ( xsdType != null ) - { - w.addAttribute( "type", xsdType ); + String xsdType = getXsdType( field.getType() ); - if ( field.getDefaultValue() != null ) + String tagName = resolveTagName( field ); + + if ( field instanceof ModelAssociation ) { - w.addAttribute( "default", field.getDefaultValue() ); - } - writeFieldDocumentation( w, field ); - } - else - { - if ( field instanceof ModelAssociation && - isClassInModel( ( (ModelAssociation) field ).getTo(), objectModel ) ) - { - ModelAssociation association = (ModelAssociation) field; - ModelClass fieldModelClass = objectModel.getClass( association.getTo(), getGeneratedVersion() ); + ModelAssociation assoc = (ModelAssociation) field; - toWrite.add( fieldModelClass ); - if ( "*".equals( association.getMultiplicity() ) ) + boolean wrappedAssociation = isWrappedAssociation( objectModel, field ); + + if ( wrappedAssociation ) { + w.addAttribute( "name", uncapitalise( assoc.getName() ) ); + writeFieldDocumentation( w, field ); - writeListElement( w, field, fieldModelClass.getName() ); + + ModelClass fieldModelClass = objectModel.getClass( assoc.getTo(), getGeneratedVersion() ); + + writeListElement( w, tagName, fieldModelClass.getName() ); + + toWrite.add( fieldModelClass ); } else { - w.addAttribute( "type", fieldModelClass.getName() ); + w.addAttribute( "name", tagName ); + + if ( isManyAssociation( objectModel, assoc ) ) + { + w.addAttribute( "maxOccurs", "unbounded" ); + } + + if ( xsdType != null ) + { + w.addAttribute( "type", xsdType ); + } + else if ( isInnerAssociation( objectModel, assoc ) ) + { + ModelClass fieldModelClass = objectModel.getClass( assoc.getTo(), getGeneratedVersion() ); + + w.addAttribute( "type", objectModel.getClass( assoc.getTo(), getGeneratedVersion() ) + .getName() ); + + toWrite.add( fieldModelClass ); + } + writeFieldDocumentation( w, field ); } } + else { - if ( List.class.getName().equals( field.getType() ) ) + w.addAttribute( "name", field.getName() ); + + if ( xsdType != null ) { - writeFieldDocumentation( w, field ); - writeListElement( w, field, getXsdType( "String" ) ); + w.addAttribute( "type", xsdType ); + + if ( field.getDefaultValue() != null ) + { + w.addAttribute( "default", field.getDefaultValue() ); + } } + + writeFieldDocumentation( w, field ); + + if ( xsdType != null ) + { + } + // TODO Is list type really supported???? + else if ( List.class.getName().equals( field.getType() ) ) + { + writeListElement( w, tagName, getXsdType( "String" ) ); + } else if ( Properties.class.getName().equals( field.getType() ) || "DOM".equals( field.getType() ) ) { - writeFieldDocumentation( w, field ); writePropertiesElement( w ); } + // TODO Where is support for the date format? else { - throw new IllegalStateException( - "Non-association field of a non-primitive type '" + field.getType() + "' for '" + field.getName() + "'" ); + throw new IllegalStateException( "Non-association field of a non-primitive type '" + + field.getType() + "' for '" + field.getName() + "'" ); } } + + w.endElement(); } w.endElement(); } - w.endElement(); + for ( Iterator j = attributeFields.iterator(); j.hasNext(); ) + { + ModelField field = (ModelField) j.next(); + + w.startElement( "xs:attribute" ); + + String xsdType = getXsdType( field.getType() ); + + String tagName = resolveTagName( field ); + + w.addAttribute( "name", tagName ); + + if ( xsdType != null ) + { + w.addAttribute( "type", xsdType ); + + if ( field.getDefaultValue() != null ) + { + w.addAttribute( "default", field.getDefaultValue() ); + } + } + else + { + throw new IllegalStateException( "Attribute field of a non-primitive type '" + field.getType() + + "' for '" + field.getName() + "'" ); + } + + writeFieldDocumentation( w, field ); + + w.endElement(); + } + w.endElement(); for ( Iterator iter = toWrite.iterator(); iter.hasNext(); ) @@ -296,14 +378,14 @@ w.endElement(); } - private void writeListElement( XMLWriter w, ModelField field, String type ) + private void writeListElement( XMLWriter w, String tagName, String type ) { w.startElement( "xs:complexType" ); w.startElement( "xs:sequence" ); w.startElement( "xs:element" ); - w.addAttribute( "name", singular( field.getName() ) ); + w.addAttribute( "name", tagName ); w.addAttribute( "minOccurs", "0" ); w.addAttribute( "maxOccurs", "unbounded" ); w.addAttribute( "type", type ); @@ -335,4 +417,199 @@ } } + private boolean hasFlatAssociation( Model objectModel, List fields ) + { + boolean flatAssociation = false; + + for ( Iterator i = fields.iterator(); i.hasNext(); ) + { + flatAssociation |= isFlatAssociation( objectModel, (ModelField) i.next() ); + } + + return flatAssociation; + } + + // ------------------------------------------------------------------------ + // TODO Copied/pasted from XdocGenerator + // ------------------------------------------------------------------------ + + private List getFieldsForClass( Model objectModel, ModelClass modelClass ) + { + List fields = new ArrayList(); + while ( modelClass != null ) + { + fields.addAll( modelClass.getFields( getGeneratedVersion() ) ); + String superClass = modelClass.getSuperClass(); + if ( superClass != null ) + { + modelClass = objectModel.getClass( superClass, getGeneratedVersion() ); + } + else + { + modelClass = null; + } + } + return fields; + } + + /** + * Return the child attribute fields of this class. + * @param objectModel global object model + * @param modelClass current class + * @return the list of attribute fields of this class + */ + private List getAttributeFieldsForClass( Model objectModel, ModelClass modelClass ) + { + List attributeFields = new ArrayList(); + while ( modelClass != null ) + { + List allFields = modelClass.getFields( getGeneratedVersion() ); + + Iterator allFieldsIt = allFields.iterator(); + + while ( allFieldsIt.hasNext() ) + { + ModelField field = (ModelField) allFieldsIt.next(); + XmlFieldMetadata fieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID ); + if ( fieldMetadata.isAttribute() ) + { + attributeFields.add( field ); + } + } + + String superClass = modelClass.getSuperClass(); + if ( superClass != null ) + { + modelClass = objectModel.getClass( superClass, getGeneratedVersion() ); + } + else + { + modelClass = null; + } + } + return attributeFields; + } + + /** + * Compute the tagName of a given class, living inside an association. + * @param modelClass the class we are looking for the tag name + * @param association the association where this class is used + * @return the tag name to use + */ + private String resolveTagName( ModelField field ) + { + XmlFieldMetadata metadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID ); + + String tagName; + if ( metadata == null || metadata.getTagName() == null ) + { + if ( !( field instanceof ModelAssociation ) ) + { + tagName = uncapitalise( field.getName() ); + } + else + { + ModelAssociation association = (ModelAssociation) field; + + XmlFieldMetadata fieldMetadata = (XmlFieldMetadata) association.getMetadata( XmlFieldMetadata.ID ); + + if ( fieldMetadata != null && fieldMetadata.getAssociationTagName() != null ) + { + tagName = fieldMetadata.getAssociationTagName(); + } + else + { + tagName = association.getName(); + + if ( ModelAssociation.MANY_MULTIPLICITY.equals( association.getMultiplicity() ) ) + { + tagName = singular( tagName ); + } + } + } + } + else + { + tagName = metadata.getTagName(); + } + return tagName; + } + + private boolean isInnerAssociation( Model objectModel, ModelField field ) + { + boolean innerAssociation = field instanceof ModelAssociation + && isClassInModel( ( (ModelAssociation) field ).getTo(), objectModel ); + + return innerAssociation; + } + + private boolean isManyAssociation( Model objectModel, ModelField field ) + { + boolean manyAssociation = false; + + if ( isInnerAssociation( objectModel, field ) ) + { + ModelAssociation assoc = (ModelAssociation) field; + + manyAssociation = ModelAssociation.MANY_MULTIPLICITY.equals( assoc.getMultiplicity() ); + } + + return manyAssociation; + } + + private boolean isWrappedAssociation( Model objectModel, ModelField field ) + { + + boolean wrappedAssociation = false; + + if ( isManyAssociation( objectModel, field ) ) + { + + XmlFieldMetadata fieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID ); + + wrappedAssociation = XmlFieldMetadata.LIST_STYLE_WRAPPED.equals( fieldMetadata.getListStyle() ); + + } + + return wrappedAssociation; + + } + + /** + * Wether the given field is an instance of flat association or not.
+ * Also checks that to class is included in model. + * @param objectModel the current model + * @param field the field to check + * @return true if the field is an flat association, false otherwise. + */ + private boolean isFlatAssociation( Model objectModel, ModelField field ) + { + + boolean flatAssociation = false; + + if ( isManyAssociation( objectModel, field ) ) + { + + XmlFieldMetadata fieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID ); + + flatAssociation = XmlFieldMetadata.LIST_STYLE_FLAT.equals( fieldMetadata.getListStyle() ); + + } + + return flatAssociation; + + } + + private boolean isNonRecursiveAssociation( Model objectModel, ModelClass modelClass, ModelAssociation association ) + { + + ModelClass fieldModelClass = objectModel.getClass( association.getTo(), getGeneratedVersion() ); + + boolean recursiveAssociation = ( modelClass.getName().equals( fieldModelClass.getName() ) ) + && ( modelClass.getPackageName().equals( fieldModelClass.getPackageName() ) ); + + return !( recursiveAssociation ); + + } + }