2525import java .io .File ;
2626import java .io .IOException ;
2727import java .io .Writer ;
28+ import java .util .HashMap ;
2829import java .util .HashSet ;
2930import java .util .List ;
31+ import java .util .Map ;
3032import java .util .Properties ;
3133import java .util .Set ;
3234import java .util .Stack ;
4244import org .codehaus .modello .model .ModelField ;
4345import org .codehaus .modello .model .Version ;
4446import org .codehaus .modello .model .VersionRange ;
47+ import org .codehaus .modello .plugin .xdoc .metadata .XdocClassMetadata ;
4548import org .codehaus .modello .plugin .xdoc .metadata .XdocFieldMetadata ;
4649import org .codehaus .modello .plugin .xsd .XsdModelHelper ;
4750import org .codehaus .modello .plugins .xml .AbstractXmlGenerator ;
@@ -171,11 +174,16 @@ private void generateXdoc( Properties parameters )
171174 * Get the anchor name by which model classes can be accessed in the generated xdoc/html file.
172175 *
173176 * @param tagName the name of the XML tag of the model class
177+ * @param modelClass the model class, that eventually can have customized anchor name
174178 * @return the corresponding anchor name
175179 */
176- private String getAnchorName ( String tagName )
180+ private String getAnchorName ( String tagName , ModelClass modelClass )
177181 {
178- return "class_" + tagName ;
182+ XdocClassMetadata xdocClassMetadata = (XdocClassMetadata ) modelClass .getMetadata ( XdocClassMetadata .ID );
183+
184+ String anchorName = xdocClassMetadata .getAnchorName ();
185+
186+ return "class_" + ( anchorName == null ? tagName : anchorName );
179187 }
180188
181189 /**
@@ -186,7 +194,7 @@ private String getAnchorName( String tagName )
186194 */
187195 private void writeModelDescriptor ( XMLWriter w , ModelClass rootModelClass )
188196 {
189- writeElementDescriptor ( w , rootModelClass , null , new HashSet <String >() );
197+ writeElementDescriptor ( w , rootModelClass , null , new HashSet <>(), new HashMap < >() );
190198 }
191199
192200 /**
@@ -195,26 +203,37 @@ private void writeModelDescriptor( XMLWriter w, ModelClass rootModelClass )
195203 * @param w the output writer
196204 * @param modelClass the mode class to describe
197205 * @param association the association we are coming from (can be <code>null</code>)
198- * @param written set of data already written
206+ * @param writtenIds set of data already written ids
207+ * @param writtenAnchors map of already written anchors with corresponding ids
199208 */
200209 private void writeElementDescriptor ( XMLWriter w , ModelClass modelClass , ModelAssociation association ,
201- Set <String > written )
210+ Set <String > writtenIds , Map < String , String > writtenAnchors )
202211 {
203212 String tagName = resolveTagName ( modelClass , association );
204213
205214 String id = getId ( tagName , modelClass );
206- if ( written .contains ( id ) )
215+ if ( writtenIds .contains ( id ) )
207216 {
208217 // tag already written for this model class accessed as this tag name
209218 return ;
210219 }
211- written .add ( id );
220+ writtenIds .add ( id );
212221
213- written .add ( tagName );
222+ String anchorName = getAnchorName ( tagName , modelClass );
223+ if ( writtenAnchors .containsKey ( anchorName ) )
224+ {
225+ // TODO use logging API?
226+ System .out .println ( "[warn] model class " + id + " with tagName " + tagName + " gets duplicate anchorName "
227+ + anchorName + ", conflicting with model class " + writtenAnchors .get ( anchorName ) );
228+ }
229+ else
230+ {
231+ writtenAnchors .put ( anchorName , id );
232+ }
214233
215234 w .startElement ( "a" );
216235
217- w .addAttribute ( "name" , getAnchorName ( tagName ) );
236+ w .addAttribute ( "name" , anchorName );
218237
219238 w .endElement ();
220239
@@ -257,9 +276,9 @@ private void writeElementDescriptor( XMLWriter w, ModelClass modelClass, ModelAs
257276 ModelAssociation assoc = (ModelAssociation ) f ;
258277 ModelClass fieldModelClass = getModel ().getClass ( assoc .getTo (), getGeneratedVersion () );
259278
260- if ( !written .contains ( getId ( resolveTagName ( fieldModelClass , assoc ), fieldModelClass ) ) )
279+ if ( !writtenIds .contains ( getId ( resolveTagName ( fieldModelClass , assoc ), fieldModelClass ) ) )
261280 {
262- writeElementDescriptor ( w , fieldModelClass , assoc , written );
281+ writeElementDescriptor ( w , fieldModelClass , assoc , writtenIds , writtenAnchors );
263282 }
264283 }
265284 }
@@ -350,7 +369,7 @@ private void writeFieldsTable( XMLWriter w, List<ModelField> fields, boolean ele
350369 if ( isInnerAssociation ( f ) )
351370 {
352371 w .startElement ( "a" );
353- w .addAttribute ( "href" , "#" + getAnchorName ( itemTagName ) );
372+ w .addAttribute ( "href" , "#" + getAnchorName ( itemTagName , assoc . getToClass () ) );
354373 w .writeText ( itemTagName );
355374 w .endElement ();
356375 }
@@ -497,7 +516,7 @@ private String getElementXmlDescriptor( ModelClass modelClass, ModelAssociation
497516 String tagName = resolveTagName ( modelClass , association );
498517
499518 // <tagName
500- sb .append ( "<<a href=\" #" ).append ( getAnchorName ( tagName ) ).append ( "\" >" );
519+ sb .append ( "<<a href=\" #" ).append ( getAnchorName ( tagName , modelClass ) ).append ( "\" >" );
501520 sb .append ( tagName ).append ( "</a>" );
502521
503522 boolean addNewline = false ;
@@ -672,7 +691,7 @@ else if ( ModelDefault.PROPERTIES.equals( f.getType() ) )
672691 * @param modelClass the class we are looking for the tag name
673692 * @param association the association where this class is used
674693 * @return the tag name to use
675- * @todo refactor to use resolveTagName helpers instead
694+ * @todo refactor to use XmlModelHelpers. resolveTagName helpers instead
676695 */
677696 private String resolveTagName ( ModelClass modelClass , ModelAssociation association )
678697 {
0 commit comments