Index: src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java
===================================================================
--- src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java (r‚vision 607)
+++ src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java (copie de travail)
@@ -29,8 +29,9 @@
import org.codehaus.modello.model.ModelClass;
import org.codehaus.modello.model.ModelField;
import org.codehaus.modello.plugin.AbstractModelloGenerator;
-import org.codehaus.modello.plugin.model.ModelClassMetadata;
+import org.codehaus.modello.plugins.xml.XmlClassMetadata;
import org.codehaus.modello.plugins.xml.XmlFieldMetadata;
+import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
import org.codehaus.plexus.util.xml.XMLWriter;
@@ -116,7 +117,14 @@
w.startElement( "p" );
- w.writeMarkup( objectModel.getDescription() );
+ if ( objectModel.getDescription() != null )
+ {
+ w.writeMarkup( objectModel.getDescription() );
+ }
+ else
+ {
+ w.writeText( "No description." );
+ }
w.endElement();
@@ -146,50 +154,39 @@
writer.close();
}
- private void writeElementDescriptor( XMLWriter w, Model objectModel, ModelClass modelClass, ModelField field,
- Set written )
+ /**
+ * Builds the table describing the given class.
+ * @param w the writer to output Xdoc to
+ * @param objectModel the current objectModel
+ * @param modelClass the class we are describing
+ * @param association the association the class is orginating from
+ * @param written the set of written classes
+ */
+ private void writeElementDescriptor( XMLWriter w, Model objectModel, ModelClass modelClass,
+ ModelAssociation association, Set written )
{
written.add( modelClass );
- ModelClassMetadata metadata = (ModelClassMetadata) modelClass.getMetadata( ModelClassMetadata.ID );
+ // resolve tagName
String tagName;
- if ( metadata == null || metadata.getTagName() == null )
+
+ if ( association == null )
{
- if ( field == null )
- {
- tagName = uncapitalise( modelClass.getName() );
- }
- else
- {
- tagName = field.getName();
- if ( field instanceof ModelAssociation )
- {
- ModelAssociation a = (ModelAssociation) field;
- if ( ModelAssociation.MANY_MULTIPLICITY.equals( a.getMultiplicity() ) )
- {
- tagName = singular( tagName );
- }
- }
- }
+ tagName = resolveClassTagName( modelClass );
}
- else
+ else if ( isWrappedAssociation( association ) )
{
- tagName = metadata.getTagName();
+ tagName = resolveInnerAssociationTagName( association );
}
-
- if ( field != null )
+ else
{
- XmlFieldMetadata fieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID );
- if ( fieldMetadata != null && fieldMetadata.getTagName() != null )
- {
- tagName = fieldMetadata.getTagName();
- }
+ tagName = resolveOuterFieldTagName( association );
}
w.startElement( "a" );
- w.addAttribute( "name", "class_" + tagName );
+ w.addAttribute( "name", "class_" + modelClass.getName() );
w.endElement();
@@ -197,15 +194,19 @@
w.addAttribute( "name", tagName );
+ w.startElement( "p" );
+
if ( modelClass.getDescription() != null )
{
- w.startElement( "p" );
-
w.writeMarkup( modelClass.getDescription() );
-
- w.endElement();
}
+ else
+ {
+ w.writeMarkup( "No description." );
+ }
+ w.endElement();
+
w.startElement( "table" );
w.startElement( "tr" );
@@ -224,50 +225,40 @@
w.endElement();
- List fields = getFieldsForClass( objectModel, modelClass );
+ List fields = getFieldsForClass( modelClass );
+ List associationsToWrite = new ArrayList();
+
for ( Iterator j = fields.iterator(); j.hasNext(); )
{
ModelField f = (ModelField) j.next();
- XmlFieldMetadata fieldMetadata = (XmlFieldMetadata) f.getMetadata( XmlFieldMetadata.ID );
-
w.startElement( "tr" );
w.startElement( "td" );
w.startElement( "code" );
- boolean flatAssociation = f instanceof ModelAssociation
- && isClassInModel( ( (ModelAssociation) f ).getTo(), objectModel )
- && XmlFieldMetadata.LIST_STYLE_FLAT.equals( fieldMetadata.getListStyle() );
+ w.writeText( resolveOuterFieldTagName( f ) );
- if ( flatAssociation )
+ if ( isInnerAssociation( f ) )
{
-
- ModelAssociation association = (ModelAssociation) f;
-
- ModelClass associationModelClass = objectModel.getClass( association.getTo(), getGeneratedVersion() );
-
- w.writeText( uncapitalise( associationModelClass.getName() ) );
-
+ associationsToWrite.add( (ModelAssociation) f );
}
- else
- {
- w.writeText( f.getName() );
-
- }
-
w.endElement();
w.endElement();
w.startElement( "td" );
- if ( flatAssociation )
+ if ( isFlatAssociation( f ) )
{
- w.writeMarkup( "List " );
+ w.startElement( "b" );
+
+ w.writeText( "[List of] " );
+
+ w.endElement();
}
if ( f.getDescription() != null )
@@ -288,24 +279,24 @@
w.endElement();
- for ( Iterator iter = fields.iterator(); iter.hasNext(); )
+ for ( Iterator iter = associationsToWrite.iterator(); iter.hasNext(); )
{
- ModelField f = (ModelField) iter.next();
+ ModelAssociation assoc = (ModelAssociation) iter.next();
- if ( f instanceof ModelAssociation && isClassInModel( ( (ModelAssociation) f ).getTo(), objectModel ) )
+ // TODO We are currently not handling the case where a given class
+ // would participate in 2 associations with different tagName.
+
+ // This case would result in class information being generated
+ // with only the name of the association found
+
+ if ( !written.contains( assoc.getToClass() ) )
{
- ModelAssociation association = (ModelAssociation) f;
- ModelClass fieldModelClass = objectModel.getClass( association.getTo(), getGeneratedVersion() );
-
- if ( !written.contains( f.getName() ) )
- {
- writeElementDescriptor( w, objectModel, fieldModelClass, f, written );
- }
+ writeElementDescriptor( w, objectModel, assoc.getToClass(), assoc, written );
}
}
}
- private List getFieldsForClass( Model objectModel, ModelClass modelClass )
+ private List getFieldsForClass( ModelClass modelClass )
{
List fields = new ArrayList();
while ( modelClass != null )
@@ -314,7 +305,7 @@
String superClass = modelClass.getSuperClass();
if ( superClass != null )
{
- modelClass = objectModel.getClass( superClass, getGeneratedVersion() );
+ modelClass = getModel().getClass( superClass, getGeneratedVersion() );
}
else
{
@@ -326,22 +317,19 @@
/**
* 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 )
+ private List getAttributeFieldsForClass( ModelClass modelClass )
{
List attributeFields = new ArrayList();
while ( modelClass != null )
{
List allFields = modelClass.getFields( getGeneratedVersion() );
- Iterator allFieldsIt = allFields.iterator();
-
- while ( allFieldsIt.hasNext() )
+ for ( Iterator iter = allFields.iterator(); iter.hasNext(); )
{
- ModelField field = (ModelField) allFieldsIt.next();
+ ModelField field = (ModelField) iter.next();
XmlFieldMetadata fieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID );
if ( fieldMetadata.isAttribute() )
{
@@ -352,7 +340,7 @@
String superClass = modelClass.getSuperClass();
if ( superClass != null )
{
- modelClass = objectModel.getClass( superClass, getGeneratedVersion() );
+ modelClass = getModel().getClass( superClass, getGeneratedVersion() );
}
else
{
@@ -362,152 +350,143 @@
return attributeFields;
}
- private String getModelClassDescriptor( Model objectModel, ModelClass modelClass, ModelField field, int depth )
+ private String getModelClassDescriptor( Model objectModel, ModelClass modelClass, ModelAssociation association,
+ int depth )
+ {
+ return getModelClassDescriptor( objectModel, modelClass, association, depth, true );
+ }
+
+ /**
+ * Build the pretty tree describing the model.
+ * This method is recursive.
+ * @param objectModel the complete model
+ * @param modelClass the class we are printing the model
+ * @param association the association we are coming from (can be null)
+ * @param depth how deep we currently are (for spacers purpose)
+ * @param recursive are we still in recursive mode or not
+ * @return the String representing the tree model
+ * @throws ModelloRuntimeException
+ */
+ private String getModelClassDescriptor( Model objectModel, ModelClass modelClass, ModelAssociation association,
+ int depth, boolean recursive )
throws ModelloRuntimeException
{
StringBuffer sb = new StringBuffer();
- for ( int i = 0; i < depth; i++ )
- {
- sb.append( " " );
- }
+ generateSpacer( sb, depth );
- ModelClassMetadata metadata = (ModelClassMetadata) modelClass.getMetadata( ModelClassMetadata.ID );
+ // resolve tagName
String tagName;
- if ( metadata == null || metadata.getTagName() == null )
+
+ if ( association == null )
{
- if ( field == null )
- {
- tagName = uncapitalise( modelClass.getName() );
- }
- else
- {
- tagName = field.getName();
- if ( field instanceof ModelAssociation )
- {
- ModelAssociation a = (ModelAssociation) field;
- if ( ModelAssociation.MANY_MULTIPLICITY.equals( a.getMultiplicity() ) )
- {
- tagName = singular( tagName );
- }
- }
- }
+ tagName = resolveClassTagName( modelClass );
}
- else
+ else if ( isWrappedAssociation( association ) )
{
- tagName = metadata.getTagName();
+ tagName = resolveInnerAssociationTagName( association );
}
-
- if ( field != null )
+ else
{
- XmlFieldMetadata fieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID );
- if ( fieldMetadata != null && fieldMetadata.getTagName() != null )
- {
- tagName = fieldMetadata.getTagName();
- }
+ tagName = resolveOuterFieldTagName( association );
}
- sb.append( "<" ).append( tagName );
+ // Write down tagName
+ sb.append( "<" ).append( tagName );
+
sb.append( "" );
- List fields = getFieldsForClass( objectModel, modelClass );
+ List fields = getFieldsForClass( modelClass );
- List attributeFields = getAttributeFieldsForClass( objectModel, modelClass );
+ // handle the attributes
+ List attributeFields = getAttributeFieldsForClass( modelClass );
+
if ( attributeFields.size() > 0 )
{
-
for ( Iterator iter = attributeFields.iterator(); iter.hasNext(); )
{
ModelField f = (ModelField) iter.next();
- sb.append( " " );
+ sb.append( " " );
- sb.append( uncapitalise( f.getName() ) ).append( "=.." );
+ sb.append( resolveFieldTagName( f ) ).append( "=.." );
}
sb.append( " " );
fields.removeAll( attributeFields );
-
}
+ // handle the other fields of the current class
+
if ( fields.size() > 0 )
{
sb.append( ">\n" );
- for ( Iterator iter = fields.iterator(); iter.hasNext(); )
+ if ( recursive )
{
- ModelField f = (ModelField) iter.next();
- XmlFieldMetadata fieldMetadata = (XmlFieldMetadata) f.getMetadata( XmlFieldMetadata.ID );
-
- ModelClass fieldModelClass;
-
- if ( f instanceof ModelAssociation && isClassInModel( ( (ModelAssociation) f ).getTo(), objectModel ) )
+ for ( Iterator iter = fields.iterator(); iter.hasNext(); )
{
- ModelAssociation association = (ModelAssociation) f;
+ ModelField f = (ModelField) iter.next();
- if ( XmlFieldMetadata.LIST_STYLE_FLAT.equals( fieldMetadata.getListStyle() ) )
+ if ( isInnerAssociation( f ) )
{
- fieldModelClass = objectModel.getClass( association.getTo(), getGeneratedVersion() );
- sb.append( getModelClassDescriptor( objectModel, fieldModelClass, f, depth + 1 ) );
+ ModelAssociation assoc = (ModelAssociation) f;
- }
+ boolean wrappedListStyle = isWrappedAssociation( f );
- else
- {
-
- if ( ModelAssociation.MANY_MULTIPLICITY.equals( association.getMultiplicity() ) )
+ if ( wrappedListStyle )
{
depth++;
- for ( int i = 0; i < depth; i++ )
- {
- sb.append( " " );
- }
+ generateSpacer( sb, depth );
- sb.append( "<" ).append( uncapitalise( association.getName() ) ).append( ">\n" );
+ sb.append( "<" ).append( resolveOuterFieldTagName( f ) ).append( ">\n" );
}
- fieldModelClass = objectModel.getClass( association.getTo(), getGeneratedVersion() );
+ if ( isNonRecursiveAssociation( modelClass, assoc ) )
+ {
+ sb.append( getModelClassDescriptor( objectModel, assoc.getToClass(), assoc, depth + 1 ) );
+ }
+ else
+ {
+ sb.append( getModelClassDescriptor( objectModel, assoc.getToClass(), assoc, depth + 1,
+ false ) );
+ }
- sb.append( getModelClassDescriptor( objectModel, fieldModelClass, f, depth + 1 ) );
-
- if ( ModelAssociation.MANY_MULTIPLICITY.equals( association.getMultiplicity() ) )
+ if ( wrappedListStyle )
{
- for ( int i = 0; i < depth; i++ )
- {
- sb.append( " " );
- }
+ generateSpacer( sb, depth );
- sb.append( "</" ).append( uncapitalise( association.getName() ) ).append( ">\n" );
+ sb.append( "</" ).append( resolveOuterFieldTagName( f ) ).append( ">\n" );
depth--;
}
+
}
-
- }
- else
- {
- for ( int i = 0; i < depth + 1; i++ )
+ else
{
- sb.append( " " );
- }
+ generateSpacer( sb, depth + 1 );
- sb.append( "<" ).append( uncapitalise( f.getName() ) ).append( "/>\n" );
+ sb.append( "<" ).append( resolveFieldTagName( f ) ).append( "/>\n" );
+ }
}
}
+ else
+ {
+ generateSpacer( sb, depth + 1 );
- for ( int i = 0; i < depth; i++ )
- {
- sb.append( " " );
+ sb.append( ".. infinite loop ..\n" );
}
+ generateSpacer( sb, depth );
+
sb.append( "</" ).append( tagName ).append( ">\n" );
}
else
@@ -517,4 +496,182 @@
return sb.toString();
}
+
+ /**
+ * Compute the tagName of a given field.
+ * This method return the first child tag name created by this field.
+ * This means that for a association with multiplicity * and listStyle to
+ * wrapped (which is the default), this method will return the plural tagName,
+ * while for a listStyle of flat, it will return the singular tagName.
+ * @param field the field we are looking for the tag name.
+ * @return the tag name to use
+ */
+ private String resolveFieldTagName( ModelField field )
+ {
+ XmlFieldMetadata metadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID );
+
+ String tagName = uncapitalise( field.getName() );
+
+ if ( metadata != null && StringUtils.isNotEmpty( metadata.getTagName() ) )
+ {
+ tagName = metadata.getTagName();
+ }
+
+ return tagName;
+ }
+
+ /**
+ * Compute the outer tagName of a given field.
+ * If this method is called with a normal field, it will just return the normal name (see {@link #resolveFieldTagName(ModelField)}).
+ * If the argument is a flat association it will return the tagName of the collection.
+ * @param field the field we are looking for the tag name.
+ * @return the tag name to use
+ */
+ private String resolveOuterFieldTagName( ModelField field )
+ {
+ String tagName = resolveFieldTagName( field );
+
+ if ( isFlatAssociation( field ) )
+ {
+ tagName = resolveInnerAssociationTagName( (ModelAssociation) field );
+ }
+ return tagName;
+ }
+
+ /**
+ * Compute the inner tagName of a wrapped Style association.
+ * The tagName returned is the name of the nested tag in an association.
+ * If the given association is not a * multiplicity association, this method
+ * will throw an {@link IllegalArgumentException}, because inner tagName
+ * has no signification outside of * mutliplicity association.
+ * @param association the association we want the inner tagName of
+ * @return the inner tag name to use
+ */
+ private String resolveInnerAssociationTagName( ModelAssociation association )
+ {
+ XmlFieldMetadata metadata = (XmlFieldMetadata) association.getMetadata( XmlFieldMetadata.ID );
+
+ if ( !isManyAssociation( association ) )
+ {
+ throw new IllegalArgumentException(
+ "Thid method should be called only with an association having multiplicity *." );
+ }
+
+ String innerTagName = singular( resolveFieldTagName( association ) );
+
+ if ( metadata != null && StringUtils.isNotEmpty( metadata.getAssociationTagName() ) )
+ {
+ innerTagName = metadata.getAssociationTagName();
+ }
+ return innerTagName;
+ }
+
+ /**
+ * Compute the tagName of a modelClass.
+ * This method should only be called with the root class, since the
+ * tagName of a given class is given by the association it is involved in.
+ * @param modelClass the class we are looking for the name
+ * @return the tag name to use
+ */
+ private String resolveClassTagName( ModelClass modelClass )
+ {
+ XmlClassMetadata metadata = (XmlClassMetadata) modelClass.getMetadata( XmlClassMetadata.ID );
+
+ String tagName = uncapitalise( modelClass.getName() );
+
+ if ( metadata != null && StringUtils.isNotEmpty( metadata.getTagName() ) )
+ {
+ tagName = metadata.getTagName();
+ }
+ return tagName;
+ }
+
+ private boolean isInnerAssociation( ModelField field )
+ {
+ boolean innerAssociation = field instanceof ModelAssociation
+ && isClassInModel( ( (ModelAssociation) field ).getTo(), getModel() );
+
+ return innerAssociation;
+ }
+
+ private boolean isManyAssociation( ModelField field )
+ {
+ boolean manyAssociation = false;
+
+ if ( isInnerAssociation( field ) )
+ {
+ ModelAssociation assoc = (ModelAssociation) field;
+
+ manyAssociation = ModelAssociation.MANY_MULTIPLICITY.equals( assoc.getMultiplicity() );
+ }
+
+ return manyAssociation;
+ }
+
+ private boolean isWrappedAssociation( ModelField field )
+ {
+
+ boolean wrappedAssociation = false;
+
+ if ( isManyAssociation( 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 field the field to check
+ * @return true if the field is an flat association, false otherwise.
+ */
+ private boolean isFlatAssociation( ModelField field )
+ {
+
+ boolean flatAssociation = false;
+
+ if ( isManyAssociation( field ) )
+ {
+
+ XmlFieldMetadata fieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID );
+
+ flatAssociation = XmlFieldMetadata.LIST_STYLE_FLAT.equals( fieldMetadata.getListStyle() );
+
+ }
+
+ return flatAssociation;
+
+ }
+
+ private boolean isNonRecursiveAssociation( ModelClass modelClass, ModelAssociation association )
+ {
+
+ ModelClass fieldModelClass = getModel().getClass( association.getTo(), getGeneratedVersion() );
+
+ boolean recursiveAssociation = ( modelClass.getName().equals( fieldModelClass.getName() ) )
+ && ( modelClass.getPackageName().equals( fieldModelClass.getPackageName() ) );
+
+ return !( recursiveAssociation );
+
+ }
+
+ /**
+ * Generates the required spacers in the given Stringbuffer.
+ * @param stringBuffer where to generate the spacers
+ * @param depth the depth of spacers to generate
+ */
+ private void generateSpacer( StringBuffer stringBuffer, int depth )
+ {
+ for ( int i = 0; i < depth; i++ )
+ {
+ stringBuffer.append( " " );
+ }
+ }
}