View Javadoc
1   /* ==========================================================================
2    * Copyright 2005 Mevenide Team
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   * =========================================================================
16   */
17  package org.codehaus.modello.plugin.jdom;
18  
19  import javax.inject.Named;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.codehaus.modello.ModelloException;
27  import org.codehaus.modello.model.Model;
28  import org.codehaus.modello.model.ModelAssociation;
29  import org.codehaus.modello.model.ModelClass;
30  import org.codehaus.modello.model.ModelDefault;
31  import org.codehaus.modello.model.ModelField;
32  import org.codehaus.modello.plugin.java.javasource.JClass;
33  import org.codehaus.modello.plugin.java.javasource.JConstructor;
34  import org.codehaus.modello.plugin.java.javasource.JField;
35  import org.codehaus.modello.plugin.java.javasource.JMethod;
36  import org.codehaus.modello.plugin.java.javasource.JParameter;
37  import org.codehaus.modello.plugin.java.javasource.JSourceCode;
38  import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
39  import org.codehaus.modello.plugin.java.javasource.JType;
40  import org.codehaus.modello.plugin.java.metadata.JavaFieldMetadata;
41  import org.codehaus.modello.plugin.model.ModelClassMetadata;
42  import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata;
43  import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata;
44  import org.codehaus.modello.plugins.xml.metadata.XmlModelMetadata;
45  
46  /**
47   * @author mkleint@codehaus.org
48   */
49  @Named("jdom-writer")
50  public class JDOMWriterGenerator extends AbstractJDOMGenerator {
51  
52      private boolean requiresDomSupport;
53  
54      @Override
55      public void generate(Model model, Map<String, Object> parameters) throws ModelloException {
56          initialize(model, parameters);
57  
58          requiresDomSupport = false;
59  
60          try {
61              generateJDOMWriter();
62          } catch (IOException ex) {
63              throw new ModelloException("Exception while generating JDOM Writer.", ex);
64          }
65      }
66  
67      private void generateJDOMWriter() throws ModelloException, IOException {
68          Model objectModel = getModel();
69  
70          String packageName =
71                  objectModel.getDefaultPackageName(isPackageWithVersion(), getGeneratedVersion()) + ".io.jdom";
72  
73          String marshallerName = getFileName("JDOMWriter");
74  
75          JClass jClass = new JClass(packageName + '.' + marshallerName);
76          initHeader(jClass);
77          suppressAllWarnings(objectModel, jClass);
78  
79          // -------------------------------------------------------------
80          // imports now
81          // -------------------------------------------------------------
82          jClass.addImport("java.io.OutputStream");
83          jClass.addImport("java.io.OutputStreamWriter");
84          jClass.addImport("java.io.Writer");
85          jClass.addImport("java.text.DateFormat");
86          jClass.addImport("java.util.ArrayList");
87          jClass.addImport("java.util.Arrays");
88          jClass.addImport("java.util.Collection");
89          jClass.addImport("java.util.Iterator");
90          jClass.addImport("java.util.List");
91          jClass.addImport("java.util.ListIterator");
92          jClass.addImport("java.util.Locale");
93          jClass.addImport("java.util.Map");
94          jClass.addImport("java.util.Properties");
95          jClass.addImport("org.jdom2.Content");
96          jClass.addImport("org.jdom2.DefaultJDOMFactory");
97          jClass.addImport("org.jdom2.Document");
98          jClass.addImport("org.jdom2.Element");
99          jClass.addImport("org.jdom2.JDOMFactory");
100         jClass.addImport("org.jdom2.Namespace");
101         jClass.addImport("org.jdom2.Parent");
102         jClass.addImport("org.jdom2.Text");
103         jClass.addImport("org.jdom2.output.Format");
104         jClass.addImport("org.jdom2.output.XMLOutputter");
105 
106         addModelImports(jClass, null);
107 
108         JField factoryField = new JField(new JClass("JDOMFactory"), "factory");
109         factoryField.getModifiers().setFinal(true);
110         jClass.addField(factoryField);
111 
112         JField lineSeparatorField = new JField(new JClass("String"), "lineSeparator");
113         lineSeparatorField.getModifiers().setFinal(true);
114         jClass.addField(lineSeparatorField);
115 
116         createCounter(jClass);
117 
118         // constructor --
119         JConstructor constructor = jClass.createConstructor();
120         JSourceCode constCode = constructor.getSourceCode();
121         constCode.add("this( new DefaultJDOMFactory() );");
122 
123         constructor = jClass.createConstructor();
124         constructor.addParameter(new JParameter(new JClass("JDOMFactory"), "factory"));
125         constCode = constructor.getSourceCode();
126         constCode.add("this.factory = factory;");
127         constCode.add("this.lineSeparator = \"\\n\";");
128 
129         String root = objectModel.getRoot(getGeneratedVersion());
130 
131         ModelClass rootClass = objectModel.getClass(root, getGeneratedVersion());
132 
133         String rootElement = resolveTagName(rootClass);
134 
135         // the public global write method..
136         jClass.addMethod(generateWriteModel2(root, rootElement));
137         jClass.addMethod(generateWriteModel3(root, rootElement));
138         // the private utility classes;
139         jClass.addMethods(generateUtilityMethods());
140 
141         writeAllClasses(objectModel, jClass, rootClass);
142 
143         if (requiresDomSupport) {
144             jClass.addImport("org.codehaus.plexus.util.xml.Xpp3Dom");
145             jClass.addMethods(generateDomMethods());
146         }
147 
148         try (JSourceWriter sourceWriter = newJSourceWriter(packageName, marshallerName)) {
149             jClass.print(sourceWriter);
150         }
151     }
152 
153     private void createCounter(final JClass jClass) throws IllegalArgumentException {
154         // inner counter class
155         JClass counter = jClass.createInnerClass("Counter");
156         counter.getModifiers().setStatic(true);
157 
158         JField fld = new JField(new JType("int"), "currentIndex");
159         fld.setInitString("0");
160         counter.addField(fld);
161 
162         fld = new JField(new JType("int"), "level");
163         counter.addField(fld);
164 
165         JConstructor constr =
166                 counter.createConstructor(new JParameter[] {new JParameter(new JType("int"), "depthLevel")});
167         constr.getSourceCode().append("level = depthLevel;");
168 
169         JMethod inc = new JMethod("increaseCount");
170         inc.getSourceCode().add("currentIndex = currentIndex + 1;");
171         counter.addMethod(inc);
172 
173         JMethod getter = new JMethod("getCurrentIndex", new JType("int"), null);
174         getter.getSourceCode().add("return currentIndex;");
175         counter.addMethod(getter);
176 
177         getter = new JMethod("getDepth", new JType("int"), null);
178         getter.getSourceCode().add("return level;");
179         counter.addMethod(getter);
180     }
181 
182     private JMethod generateWriteModel2(String root, String rootElement) {
183         String variableName = uncapitalise(root);
184 
185         JMethod marshall = new JMethod("write");
186 
187         marshall.addParameter(new JParameter(new JClass(root), variableName));
188         marshall.addParameter(new JParameter(new JClass("OutputStreamWriter"), "writer"));
189         marshall.addException(new JClass("java.io.IOException"));
190 
191         JSourceCode sc = marshall.getSourceCode();
192         sc.add("Format format = Format.getRawFormat()");
193         sc.add("    .setEncoding( writer.getEncoding() )");
194         sc.add("    .setLineSeparator( System.getProperty( \"line.separator\" ) );");
195         sc.add("write( " + variableName + ", writer, format );");
196         return marshall;
197     }
198 
199     private JMethod generateWriteModel3(String root, String rootElement) {
200         String variableName = uncapitalise(root);
201 
202         JMethod marshall = new JMethod("write");
203 
204         marshall.addParameter(new JParameter(new JClass(root), variableName));
205         marshall.addParameter(new JParameter(new JClass("Writer"), "writer"));
206         marshall.addParameter(new JParameter(new JClass("Format"), "jdomFormat"));
207         marshall.addException(new JClass("java.io.IOException"));
208 
209         JSourceCode sc = marshall.getSourceCode();
210         sc.add("Document document = factory.document( null );");
211         sc.add("update" + root + "( " + variableName + ", \"" + rootElement + "\", new Counter( 0 ), document );");
212         sc.add("XMLOutputter outputter = new XMLOutputter();");
213         sc.add("outputter.setFormat( jdomFormat );");
214         sc.add("outputter.output( document, writer );");
215 
216         return marshall;
217     }
218 
219     private JMethod[] generateUtilityMethods() {
220         JMethod findRSElement = new JMethod("findAndReplaceSimpleElement", new JClass("Element"), null);
221         findRSElement.addParameter(new JParameter(new JClass("Counter"), "counter"));
222         findRSElement.addParameter(new JParameter(new JClass("Element"), "parent"));
223         findRSElement.addParameter(new JParameter(new JClass("String"), "name"));
224         findRSElement.addParameter(new JParameter(new JClass("String"), "text"));
225         findRSElement.addParameter(new JParameter(new JClass("String"), "defaultValue"));
226 
227         findRSElement.getModifiers().makeProtected();
228         JSourceCode sc = findRSElement.getSourceCode();
229         sc.add("if ( ( defaultValue != null ) && ( text != null ) && defaultValue.equals( text ) )");
230         sc.add("{");
231         sc.indent();
232         sc.add("Element element =  parent.getChild( name, parent.getNamespace() );");
233         sc.add("// if exist and is default value or if doesn't exist.. just keep the way it is..");
234         sc.add("if ( ( element != null && defaultValue.equals( element.getText() ) ) || element == null )");
235         sc.add("{");
236         sc.addIndented("return element;");
237         sc.add("}");
238         sc.unindent();
239         sc.add("}");
240 
241         sc.add("boolean shouldExist = ( text != null ) && ( text.trim().length() > 0 );");
242         sc.add("Element element = updateElement( counter, parent, name );");
243         sc.add("if ( shouldExist )");
244         sc.add("{");
245         sc.addIndented("element.setText( text );");
246         sc.add("}");
247         sc.add("return element;");
248 
249         JMethod updateElement = new JMethod("updateElement", new JClass("Element"), null);
250         updateElement.addParameter(new JParameter(new JClass("Counter"), "counter"));
251         updateElement.addParameter(new JParameter(new JClass("Element"), "parent"));
252         updateElement.addParameter(new JParameter(new JClass("String"), "name"));
253         updateElement.getModifiers().makeProtected();
254         sc = updateElement.getSourceCode();
255         sc.add("Element element = factory.element( name, parent.getNamespace() );");
256         sc.add("insertAtPreferredLocation( parent, element, counter );");
257         sc.add("counter.increaseCount();");
258         sc.add("return element;");
259 
260         JMethod insAtPref = new JMethod("insertAtPreferredLocation");
261         insAtPref.addParameter(new JParameter(new JClass("Element"), "parent"));
262         insAtPref.addParameter(new JParameter(new JClass("Element"), "child"));
263         insAtPref.addParameter(new JParameter(new JClass("Counter"), "counter"));
264         insAtPref.getModifiers().makeProtected();
265         sc = insAtPref.getSourceCode();
266         sc.add("int contentIndex = 0;");
267         sc.add("int elementCounter = 0;");
268         sc.add("Iterator it = parent.getContent().iterator();");
269         sc.add("Text lastText = null;");
270         sc.add("int offset = 0;");
271         sc.add("while ( it.hasNext() && elementCounter <= counter.getCurrentIndex() )");
272         sc.add("{");
273         sc.indent();
274         sc.add("Object next = it.next();");
275         sc.add("offset = offset + 1;");
276         sc.add("if ( next instanceof Element )");
277         sc.add("{");
278         sc.indent();
279         sc.add("elementCounter = elementCounter + 1;");
280         sc.add("contentIndex = contentIndex + offset;");
281         sc.add("offset = 0;");
282         sc.unindent();
283         sc.add("}");
284         sc.add("if ( next instanceof Text && it.hasNext() )");
285         sc.add("{");
286         sc.addIndented("lastText = (Text) next;");
287         sc.add("}");
288         sc.unindent();
289         sc.add("}");
290         sc.add("if ( lastText != null && lastText.getTextTrim().length() == 0 )");
291         sc.add("{");
292         sc.addIndented("lastText = (Text) lastText.clone();");
293         sc.add("}");
294         sc.add("else");
295         sc.add("{");
296         sc.indent();
297         sc.add("String starter = lineSeparator;");
298         sc.add("for ( int i = 0; i < counter.getDepth(); i++ )");
299         sc.add("{");
300         sc.addIndented("starter = starter + \"    \"; //TODO make settable?");
301         sc.add("}");
302         sc.add("lastText = factory.text( starter );");
303         sc.unindent();
304         sc.add("}");
305         sc.add("if ( parent.getContentSize() == 0 )");
306         sc.add("{");
307         sc.indent();
308         sc.add("Text finalText = (Text) lastText.clone();");
309         sc.add(
310                 "finalText.setText( finalText.getText().substring( 0, finalText.getText().length() - \"    \".length() ) );");
311         sc.add("parent.addContent( contentIndex, finalText );");
312         sc.unindent();
313         sc.add("}");
314         sc.add("parent.addContent( contentIndex, child );");
315         sc.add("parent.addContent( contentIndex, lastText );");
316 
317         JMethod findRSLists = new JMethod("findAndReplaceSimpleLists", new JClass("Element"), null);
318         findRSLists.addParameter(new JParameter(new JClass("Counter"), "counter"));
319         findRSLists.addParameter(new JParameter(new JClass("Element"), "parent"));
320         findRSLists.addParameter(new JParameter(new JClass("java.util.Collection"), "list"));
321         findRSLists.addParameter(new JParameter(new JClass("String"), "parentName"));
322         findRSLists.addParameter(new JParameter(new JClass("String"), "childName"));
323         findRSLists.getModifiers().makeProtected();
324         sc = findRSLists.getSourceCode();
325         sc.add("boolean shouldExist = ( list != null ) && ( list.size() > 0 );");
326         sc.add("Element element = updateElement( counter, parent, parentName );");
327         sc.add("if ( shouldExist )");
328         sc.add("{");
329         sc.indent();
330         sc.add("Iterator it = list.iterator();");
331         sc.add("Counter innerCount = new Counter( counter.getDepth() + 1 );");
332         sc.add("while ( it.hasNext() )");
333         sc.add("{");
334         sc.indent();
335         sc.add("String value = (String) it.next();");
336         sc.add("Element el = factory.element( childName, element.getNamespace() );");
337         sc.add("insertAtPreferredLocation( element, el, innerCount );");
338         sc.add("el.setText( value );");
339         sc.add("innerCount.increaseCount();");
340         sc.unindent();
341         sc.add("}");
342         sc.add("}");
343         sc.add("return element;");
344 
345         return new JMethod[] {findRSElement, updateElement, insAtPref, findRSLists};
346     }
347 
348     private JMethod[] generateDomMethods() {
349         JMethod findRSDom = new JMethod("findAndReplaceXpp3DOM", new JClass("Element"), null);
350         findRSDom.addParameter(new JParameter(new JClass("Counter"), "counter"));
351         findRSDom.addParameter(new JParameter(new JClass("Element"), "parent"));
352         findRSDom.addParameter(new JParameter(new JClass("String"), "name"));
353         findRSDom.addParameter(new JParameter(new JClass("Xpp3Dom"), "dom"));
354         findRSDom.getModifiers().makeProtected();
355         JSourceCode sc = findRSDom.getSourceCode();
356         sc.add("boolean shouldExist = ( dom != null ) && ( dom.getChildCount() > 0 || dom.getValue() != null );");
357         sc.add("Element element = updateElement( counter, parent, name );");
358         sc.add("if ( shouldExist )");
359         sc.add("{");
360         sc.addIndented("replaceXpp3DOM( element, dom, new Counter( counter.getDepth() + 1 ) );");
361         sc.add("}");
362         sc.add("return element;");
363 
364         JMethod findRSDom2 = new JMethod("replaceXpp3DOM");
365         findRSDom2.addParameter(new JParameter(new JClass("Element"), "parent"));
366         findRSDom2.addParameter(new JParameter(new JClass("Xpp3Dom"), "parentDom"));
367         findRSDom2.addParameter(new JParameter(new JClass("Counter"), "counter"));
368         findRSDom2.getModifiers().makeProtected();
369         sc = findRSDom2.getSourceCode();
370 
371         sc.add("for( String attributeName : parentDom.getAttributeNames() )");
372         sc.add("{");
373         sc.indent();
374         sc.add("String[] attrDetails = attributeName.split( \":\", 2 );");
375         sc.add("if ( attrDetails.length == 2 )");
376         sc.add("{");
377         sc.addIndented(
378                 "parent.setAttribute( attrDetails[1], parentDom.getAttribute( attributeName ), parent.getNamespace( attrDetails[0] ) );");
379         sc.add("}");
380         sc.add("else ");
381         sc.add("{");
382         sc.addIndented("parent.setAttribute( attributeName, parentDom.getAttribute( attributeName ) );");
383         sc.add("}");
384         sc.unindent();
385         sc.add("}");
386         sc.add("if ( parentDom.getChildCount() > 0 )");
387         sc.add("{");
388         sc.indent();
389         sc.add("Xpp3Dom[] childs = parentDom.getChildren();");
390         sc.add("Collection domChilds = new ArrayList();");
391         sc.add("for ( int i = 0; i < childs.length; i++ )");
392         sc.add("{");
393         sc.addIndented("domChilds.add( childs[i] );");
394         sc.add("}");
395         sc.add("ListIterator it = parent.getChildren().listIterator();");
396         sc.add("while ( it.hasNext() )");
397         sc.add("{");
398         sc.indent();
399         sc.add("Element elem = (Element) it.next();");
400         sc.add("Iterator it2 = domChilds.iterator();");
401         sc.add("Xpp3Dom corrDom = null;");
402         sc.add("while ( it2.hasNext() )");
403         sc.add("{");
404         sc.indent();
405         sc.add("Xpp3Dom dm = (Xpp3Dom) it2.next();");
406         sc.add("if ( dm.getName().equals( elem.getName() ) )");
407         sc.add("{");
408         sc.indent();
409         sc.add("corrDom = dm;");
410         sc.add("break;");
411         sc.unindent();
412         sc.add("}");
413         sc.unindent();
414         sc.add("}");
415         sc.add("if ( corrDom != null )");
416         sc.add("{");
417         sc.indent();
418         sc.add("domChilds.remove( corrDom );");
419         sc.add("replaceXpp3DOM( elem, corrDom, new Counter( counter.getDepth() + 1 ) );");
420         sc.add("counter.increaseCount();");
421         sc.unindent();
422         sc.add("}");
423         sc.add("else");
424         sc.add("{");
425         sc.addIndented("it.remove();");
426         sc.add("}");
427         sc.unindent();
428         sc.add("}");
429         sc.add("Iterator it2 = domChilds.iterator();");
430         sc.add("while ( it2.hasNext() )");
431         sc.add("{");
432         sc.indent();
433         sc.add("Xpp3Dom dm = (Xpp3Dom) it2.next();");
434         sc.add("Element elem = factory.element( dm.getName(), parent.getNamespace() );");
435         sc.add("insertAtPreferredLocation( parent, elem, counter );");
436         sc.add("counter.increaseCount();");
437         sc.add("replaceXpp3DOM( elem, dm, new Counter( counter.getDepth() + 1 ) );");
438         sc.unindent();
439         sc.add("}");
440         sc.unindent();
441         sc.add("}");
442         sc.add(" else if ( parentDom.getValue() != null )");
443         sc.add("{");
444         sc.addIndented("parent.setText( parentDom.getValue() );");
445         sc.add("}");
446 
447         return new JMethod[] {findRSDom, findRSDom2};
448     }
449 
450     private void writeAllClasses(Model objectModel, JClass jClass, ModelClass rootClass) throws ModelloException {
451         List<ModelClass> alwaysExistingElements = new ArrayList<ModelClass>();
452         alwaysExistingElements.add(rootClass);
453 
454         for (ModelClass clazz : getClasses(objectModel)) {
455             updateClass(clazz, jClass, alwaysExistingElements);
456         }
457     }
458 
459     private void updateClass(ModelClass clazz, JClass jClass, List<ModelClass> alwaysExisting) throws ModelloException {
460         String className = clazz.getName();
461 
462         String capClassName = capitalise(className);
463 
464         String uncapClassName = uncapitalise(className);
465 
466         ModelClassMetadata classMetadata = (ModelClassMetadata) clazz.getMetadata(ModelClassMetadata.ID);
467 
468         JMethod marshall = new JMethod("update" + capClassName);
469         marshall.addParameter(new JParameter(new JClass(className), uncapClassName));
470         marshall.addParameter(new JParameter(new JClass("String"), "xmlTag"));
471         marshall.addParameter(new JParameter(new JClass("Counter"), "counter"));
472         if (classMetadata.isRootElement()) {
473             marshall.addParameter(new JParameter(new JClass("Document"), "document"));
474         } else {
475             marshall.addParameter(new JParameter(new JClass("Element"), "element"));
476         }
477         marshall.getModifiers().makeProtected();
478 
479         JSourceCode sc = marshall.getSourceCode();
480         sc.add("if ( " + uncapClassName + " != null )");
481         sc.add("{");
482         sc.indent();
483 
484         if (classMetadata.isRootElement()) {
485             XmlModelMetadata xmlModelMetadata =
486                     (XmlModelMetadata) clazz.getModel().getMetadata(XmlModelMetadata.ID);
487             String namespace = xmlModelMetadata.getNamespace(getGeneratedVersion());
488 
489             if (namespace != null) {
490                 sc.add("Element root = factory.element( xmlTag, \"" + namespace + "\" );");
491                 if (xmlModelMetadata.getSchemaLocation() != null) {
492                     String url = xmlModelMetadata.getSchemaLocation(getGeneratedVersion());
493                     sc.add(
494                             "Namespace xsins = Namespace.getNamespace( \"xsi\", \"http://www.w3.org/2001/XMLSchema-instance\" );");
495                     sc.add("root.setAttribute( \"schemaLocation\", \"" + namespace + " " + url + "\", xsins );");
496                 }
497             } else {
498                 sc.add("Element root = factory.element( xmlTag );");
499             }
500             sc.add("document.setRootElement( root );");
501         } else {
502             sc.add("Element root = updateElement( counter, element, xmlTag );");
503         }
504 
505         sc.add("Counter innerCount = new Counter( counter.getDepth() + 1 );");
506 
507         ModelField contentField = null;
508 
509         String contentValue = null;
510 
511         List<ModelField> modelFields = getFieldsForXml(clazz, getGeneratedVersion());
512 
513         for (ModelField field : modelFields) {
514             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
515             JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata(JavaFieldMetadata.ID);
516 
517             String fieldTagName = resolveTagName(field, xmlFieldMetadata);
518 
519             String type = field.getType();
520             String value = uncapClassName + '.' + getPrefix(javaFieldMetadata) + capitalise(field.getName()) + "()";
521 
522             if (xmlFieldMetadata.isContent()) {
523                 contentField = field;
524                 contentValue = value;
525                 continue;
526             }
527 
528             if (xmlFieldMetadata.isAttribute()) {
529                 sc.add(getValueChecker(type, value, field));
530                 sc.add("{");
531                 sc.addIndented("root.setAttribute( \"" + fieldTagName + "\", "
532                         + getValue(field.getType(), value, xmlFieldMetadata) + " );");
533                 sc.add("}");
534                 continue;
535             }
536             if (field instanceof ModelAssociation) {
537                 ModelAssociation association = (ModelAssociation) field;
538 
539                 ModelClass toClass = association.getToClass();
540                 if (association.isOneMultiplicity()) {
541                     sc.add(getValueChecker(type, value, field));
542                     sc.add("{");
543                     sc.addIndented("update" + capitalise(field.getType()) + "( " + value + ", \"" + fieldTagName
544                             + "\", innerCount, root );");
545                     sc.add("}");
546                 } else {
547                     // MANY_MULTIPLICITY
548 
549                     XmlAssociationMetadata xmlAssociationMetadata =
550                             (XmlAssociationMetadata) association.getAssociationMetadata(XmlAssociationMetadata.ID);
551 
552                     String valuesTagName = resolveTagName(fieldTagName, xmlAssociationMetadata);
553                     //
554                     //                    type = association.getType();
555                     //                    String toType = association.getTo();
556                     //
557                     if (ModelDefault.LIST.equals(type) || ModelDefault.SET.equals(type)) {
558                         //                        type = association.getType();
559                         String toType = association.getTo();
560                         if (toClass != null) {
561                             if (xmlAssociationMetadata.isWrappedItems()) {
562                                 sc.add("iterate" + capitalise(toType) + "( innerCount, root, " + value + ",\""
563                                         + fieldTagName + "\",\"" + valuesTagName + "\" );");
564                                 createIterateMethod(field.getName(), toClass, singular(fieldTagName), jClass);
565                             } else {
566                                 // assume flat..
567                                 sc.add("iterate2" + capitalise(toType) + "( innerCount, root, " + value + ", \""
568                                         + valuesTagName + "\" );");
569                                 createIterateMethod2(field.getName(), toClass, singular(fieldTagName), jClass);
570                             }
571                         } else {
572                             // list of strings?
573                             sc.add("findAndReplaceSimpleLists( innerCount, root, " + value + ", \"" + fieldTagName
574                                     + "\", \"" + singular(fieldTagName) + "\" );");
575                         }
576                     } else {
577                         // Map or Properties
578                         sc.add(getValueChecker(type, value, field));
579                         sc.add("{");
580                         sc.indent();
581                         sc.add("Element listElement = updateElement( innerCount, root, \"" + fieldTagName + "\" );");
582                         sc.add("Iterator it = " + value + ".keySet().iterator();");
583                         sc.add("Counter propertiesCounter = new Counter( innerCount.getDepth() + 1 );");
584                         sc.add("while ( it.hasNext() )");
585                         sc.add("{");
586                         sc.indent();
587                         sc.add("String key = (String) it.next();");
588                         if (xmlAssociationMetadata.isMapExplode()) {
589                             sc.add("Element propTag = updateElement( propertiesCounter, listElement, \""
590                                     + singular(fieldTagName) + "\" );");
591                             sc.add("Counter propertyCounter = new Counter( propertiesCounter.getDepth() + 1 );");
592                             sc.add("findAndReplaceSimpleElement( propertyCounter, propTag, \"key\", key, null );");
593                             sc.add("findAndReplaceSimpleElement( propertyCounter, propTag, \"value\", (String) " + value
594                                     + ".get( key ), null );");
595                         } else {
596                             sc.add("findAndReplaceSimpleElement( propertiesCounter, listElement, key, (String) " + value
597                                     + ".get( key ), null );");
598                         }
599                         sc.unindent();
600                         sc.add("}");
601                         sc.unindent();
602                         sc.add("}");
603                     }
604                 }
605             } else {
606                 if ("DOM".equals(field.getType())) {
607                     sc.add("findAndReplaceXpp3DOM( innerCount, root, \"" + fieldTagName + "\", (Xpp3Dom) " + value
608                             + " );");
609 
610                     requiresDomSupport = true;
611                 } else {
612                     sc.add(getValueChecker(type, value, field));
613                     sc.add("{");
614                     sc.addIndented("updateElement( innerCount, root,  \"" + fieldTagName + "\" ).setText( "
615                             + getValue(field.getType(), value, xmlFieldMetadata) + " );");
616                     sc.add("}");
617                 }
618             }
619         }
620 
621         if (contentField != null) {
622             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) contentField.getMetadata(XmlFieldMetadata.ID);
623             sc.add("root.setText( " + getValue(contentField.getType(), contentValue, xmlFieldMetadata) + " );");
624         }
625 
626         sc.unindent();
627         sc.add("}");
628 
629         jClass.addMethod(marshall);
630     }
631 
632     private void createIterateMethod(String field, ModelClass toClass, String childFieldTagName, JClass jClass) {
633         if (jClass.getMethod("iterate" + capitalise(toClass.getName()), 0) != null) {
634             return;
635         }
636         JMethod toReturn = new JMethod("iterate" + capitalise(toClass.getName()));
637         toReturn.addParameter(new JParameter(new JClass("Counter"), "counter"));
638         toReturn.addParameter(new JParameter(new JClass("Element"), "parent"));
639         toReturn.addParameter(new JParameter(new JClass("java.util.Collection"), "list"));
640         toReturn.addParameter(new JParameter(new JClass("java.lang.String"), "parentTag"));
641         toReturn.addParameter(new JParameter(new JClass("java.lang.String"), "childTag"));
642         toReturn.getModifiers().makeProtected();
643         JSourceCode sc = toReturn.getSourceCode();
644         sc.add("boolean shouldExist = ( list != null ) && ( list.size() > 0 );");
645         sc.add("Element element = updateElement( counter, parent, parentTag );");
646         sc.add("if ( shouldExist )");
647         sc.add("{");
648         sc.indent();
649         sc.add("Iterator it = list.iterator();");
650         sc.add("Counter innerCount = new Counter( counter.getDepth() + 1 );");
651         sc.add("while ( it.hasNext() )");
652         sc.add("{");
653         sc.indent();
654         sc.add(toClass.getName() + " value = (" + toClass.getName() + ") it.next();");
655         sc.add("update" + toClass.getName() + "( value, childTag, innerCount, element );");
656         sc.add("innerCount.increaseCount();");
657         sc.unindent();
658         sc.add("}");
659         sc.unindent();
660         sc.add("}");
661 
662         jClass.addMethod(toReturn);
663     }
664 
665     private void createIterateMethod2(String field, ModelClass toClass, String childFieldTagName, JClass jClass) {
666         if (jClass.getMethod("iterate2" + capitalise(toClass.getName()), 0) != null) {
667             return;
668         }
669         JMethod toReturn = new JMethod("iterate2" + capitalise(toClass.getName()));
670         toReturn.addParameter(new JParameter(new JClass("Counter"), "counter"));
671         toReturn.addParameter(new JParameter(new JClass("Element"), "parent"));
672         toReturn.addParameter(new JParameter(new JClass("java.util.Collection"), "list"));
673         toReturn.addParameter(new JParameter(new JClass("java.lang.String"), "childTag"));
674         toReturn.getModifiers().makeProtected();
675         JSourceCode sc = toReturn.getSourceCode();
676         sc.add("Iterator it = list.iterator();");
677         sc.add("while ( it.hasNext() )");
678         sc.add("{");
679         sc.indent();
680         sc.add(toClass.getName() + " value = (" + toClass.getName() + ") it.next();");
681         sc.add("update" + toClass.getName() + "( value, childTag, counter, parent );");
682         sc.unindent();
683         sc.add("}");
684 
685         jClass.addMethod(toReturn);
686     }
687 }