View Javadoc
1   package org.codehaus.modello.plugin.dom4j;
2   
3   /*
4    * Copyright (c) 2006, Codehaus.
5    *
6    * Permission is hereby granted, free of charge, to any person obtaining a copy of
7    * this software and associated documentation files (the "Software"), to deal in
8    * the Software without restriction, including without limitation the rights to
9    * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10   * of the Software, and to permit persons to whom the Software is furnished to do
11   * so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in all
14   * copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22   * SOFTWARE.
23   */
24  
25  import javax.inject.Named;
26  
27  import java.io.IOException;
28  import java.util.List;
29  import java.util.Properties;
30  
31  import org.codehaus.modello.ModelloException;
32  import org.codehaus.modello.model.Model;
33  import org.codehaus.modello.model.ModelAssociation;
34  import org.codehaus.modello.model.ModelClass;
35  import org.codehaus.modello.model.ModelDefault;
36  import org.codehaus.modello.model.ModelField;
37  import org.codehaus.modello.plugin.java.javasource.JClass;
38  import org.codehaus.modello.plugin.java.javasource.JMethod;
39  import org.codehaus.modello.plugin.java.javasource.JParameter;
40  import org.codehaus.modello.plugin.java.javasource.JSourceCode;
41  import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
42  import org.codehaus.modello.plugin.java.javasource.JType;
43  import org.codehaus.modello.plugins.xml.AbstractXmlJavaGenerator;
44  import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata;
45  import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata;
46  import org.codehaus.plexus.util.StringUtils;
47  
48  /**
49   * Generator that reads a model using dom4j.
50   * TODO: chunks are lifted from xpp3, including the tests. Can we abstract it in some way?
51   *
52   * @author <a href="mailto:brett@codehaus.org">Brett Porter</a>
53   */
54  @Named("dom4j-reader")
55  public class Dom4jReaderGenerator extends AbstractXmlJavaGenerator {
56  
57      private boolean requiresDomSupport;
58  
59      public void generate(Model model, Properties parameters) throws ModelloException {
60          initialize(model, parameters);
61  
62          requiresDomSupport = false;
63  
64          try {
65              generateDom4jReader();
66          } catch (IOException ex) {
67              throw new ModelloException("Exception while generating Dom4j Reader.", ex);
68          }
69      }
70  
71      private void generateDom4jReader() throws ModelloException, IOException {
72          Model objectModel = getModel();
73  
74          String packageName =
75                  objectModel.getDefaultPackageName(isPackageWithVersion(), getGeneratedVersion()) + ".io.dom4j";
76  
77          String unmarshallerName = getFileName("Dom4jReader");
78  
79          JSourceWriter sourceWriter = newJSourceWriter(packageName, unmarshallerName);
80  
81          JClass jClass = new JClass(packageName + '.' + unmarshallerName);
82          initHeader(jClass);
83          suppressAllWarnings(objectModel, jClass);
84  
85          jClass.addImport("java.io.InputStream");
86          jClass.addImport("java.io.IOException");
87          jClass.addImport("java.io.Reader");
88          jClass.addImport("java.net.URL");
89          jClass.addImport("java.util.Date");
90          jClass.addImport("java.util.Locale");
91          jClass.addImport("java.text.DateFormat");
92          jClass.addImport("java.text.ParsePosition");
93          jClass.addImport("java.util.Iterator");
94          jClass.addImport("org.dom4j.Attribute");
95          jClass.addImport("org.dom4j.Document");
96          jClass.addImport("org.dom4j.DocumentException");
97          jClass.addImport("org.dom4j.Element");
98          jClass.addImport("org.dom4j.Node");
99          jClass.addImport("org.dom4j.io.SAXReader");
100 
101         addModelImports(jClass, null);
102 
103         ModelClass root = objectModel.getClass(objectModel.getRoot(getGeneratedVersion()), getGeneratedVersion());
104         JClass rootType = new JClass(root.getName());
105 
106         // ----------------------------------------------------------------------
107         // Write the read(XMLStreamReader,boolean) method which will do the unmarshalling.
108         // ----------------------------------------------------------------------
109 
110         JMethod unmarshall = new JMethod("read", rootType, null);
111         unmarshall.getModifiers().makePrivate();
112 
113         unmarshall.addParameter(new JParameter(new JClass("Document"), "document"));
114         unmarshall.addParameter(new JParameter(JType.BOOLEAN, "strict"));
115 
116         unmarshall.addException(new JClass("IOException"));
117         unmarshall.addException(new JClass("DocumentException"));
118 
119         JSourceCode sc = unmarshall.getSourceCode();
120 
121         String className = root.getName();
122         String variableName = uncapitalise(className);
123 
124         sc.add("String encoding = document.getXMLEncoding();");
125 
126         sc.add(className + ' ' + variableName + " = parse" + root.getName() + "( \"" + resolveTagName(root)
127                 + "\", document.getRootElement(), strict );");
128 
129         sc.add(variableName + ".setModelEncoding( encoding );");
130 
131         sc.add("return " + variableName + ";");
132 
133         jClass.addMethod(unmarshall);
134 
135         // ----------------------------------------------------------------------
136         // Write the read(Reader[,boolean]) methods which will do the unmarshalling.
137         // ----------------------------------------------------------------------
138 
139         unmarshall = new JMethod("read", rootType, null);
140 
141         unmarshall.addParameter(new JParameter(new JClass("Reader"), "reader"));
142         unmarshall.addParameter(new JParameter(JType.BOOLEAN, "strict"));
143 
144         unmarshall.addException(new JClass("IOException"));
145         unmarshall.addException(new JClass("DocumentException"));
146 
147         sc = unmarshall.getSourceCode();
148 
149         sc.add("SAXReader parser = new SAXReader();");
150 
151         sc.add("Document document = parser.read( reader );");
152 
153         sc.add("return read( document, strict );");
154 
155         jClass.addMethod(unmarshall);
156 
157         // ----------------------------------------------------------------------
158 
159         unmarshall = new JMethod("read", rootType, null);
160 
161         unmarshall.addParameter(new JParameter(new JClass("Reader"), "reader"));
162 
163         unmarshall.addException(new JClass("IOException"));
164         unmarshall.addException(new JClass("DocumentException"));
165 
166         sc = unmarshall.getSourceCode();
167 
168         sc.add("return read( reader, true );");
169 
170         jClass.addMethod(unmarshall);
171 
172         // ----------------------------------------------------------------------
173         // Write the read(InputStream[,boolean]) methods which will do the unmarshalling.
174         // ----------------------------------------------------------------------
175 
176         unmarshall = new JMethod("read", rootType, null);
177 
178         unmarshall.addParameter(new JParameter(new JClass("InputStream"), "stream"));
179         unmarshall.addParameter(new JParameter(JType.BOOLEAN, "strict"));
180 
181         unmarshall.addException(new JClass("IOException"));
182         unmarshall.addException(new JClass("DocumentException"));
183 
184         sc = unmarshall.getSourceCode();
185 
186         sc.add("SAXReader parser = new SAXReader();");
187 
188         sc.add("Document document = parser.read( stream );");
189 
190         sc.add("return read( document, strict );");
191 
192         jClass.addMethod(unmarshall);
193 
194         // ----------------------------------------------------------------------
195 
196         unmarshall = new JMethod("read", rootType, null);
197 
198         unmarshall.addParameter(new JParameter(new JClass("InputStream"), "stream"));
199 
200         unmarshall.addException(new JClass("IOException"));
201         unmarshall.addException(new JClass("DocumentException"));
202 
203         sc = unmarshall.getSourceCode();
204 
205         sc.add("return read( stream, true );");
206 
207         jClass.addMethod(unmarshall);
208 
209         // ----------------------------------------------------------------------
210         // Write the read(URL[,boolean]) methods which will do the unmarshalling.
211         // ----------------------------------------------------------------------
212 
213         unmarshall = new JMethod("read", rootType, null);
214 
215         unmarshall.addParameter(new JParameter(new JClass("URL"), "url"));
216         unmarshall.addParameter(new JParameter(JType.BOOLEAN, "strict"));
217 
218         unmarshall.addException(new JClass("IOException"));
219         unmarshall.addException(new JClass("DocumentException"));
220 
221         sc = unmarshall.getSourceCode();
222 
223         sc.add("SAXReader parser = new SAXReader();");
224 
225         sc.add("Document document = parser.read( url );");
226 
227         sc.add("return read( document, strict );");
228 
229         jClass.addMethod(unmarshall);
230 
231         // ----------------------------------------------------------------------
232 
233         unmarshall = new JMethod("read", rootType, null);
234 
235         unmarshall.addParameter(new JParameter(new JClass("URL"), "url"));
236 
237         unmarshall.addException(new JClass("IOException"));
238         unmarshall.addException(new JClass("DocumentException"));
239 
240         sc = unmarshall.getSourceCode();
241 
242         sc.add("return read( url, true );");
243 
244         jClass.addMethod(unmarshall);
245 
246         // ----------------------------------------------------------------------
247         // Write the class parsers
248         // ----------------------------------------------------------------------
249 
250         writeAllClassesParser(objectModel, jClass);
251 
252         // ----------------------------------------------------------------------
253         // Write helpers
254         // ----------------------------------------------------------------------
255 
256         writeHelpers(jClass);
257 
258         if (requiresDomSupport) {
259             jClass.addImport("org.codehaus.plexus.util.xml.Xpp3Dom");
260             writeDomHelpers(jClass);
261         }
262 
263         // ----------------------------------------------------------------------
264         //
265         // ----------------------------------------------------------------------
266 
267         jClass.print(sourceWriter);
268 
269         sourceWriter.close();
270     }
271 
272     private void writeAllClassesParser(Model objectModel, JClass jClass) {
273         ModelClass root = objectModel.getClass(objectModel.getRoot(getGeneratedVersion()), getGeneratedVersion());
274 
275         for (ModelClass clazz : getClasses(objectModel)) {
276             writeClassParser(clazz, jClass, root.getName().equals(clazz.getName()));
277         }
278     }
279 
280     private void writeClassParser(ModelClass modelClass, JClass jClass, boolean rootElement) {
281         String className = modelClass.getName();
282 
283         String capClassName = capitalise(className);
284 
285         String uncapClassName = uncapitalise(className);
286 
287         JMethod unmarshall = new JMethod("parse" + capClassName, new JClass(className), null);
288         unmarshall.getModifiers().makePrivate();
289 
290         unmarshall.addParameter(new JParameter(new JClass("String"), "tagName"));
291         unmarshall.addParameter(new JParameter(new JClass("Element"), "element"));
292         unmarshall.addParameter(new JParameter(JType.BOOLEAN, "strict"));
293 
294         unmarshall.addException(new JClass("IOException"));
295         unmarshall.addException(new JClass("DocumentException"));
296 
297         JSourceCode sc = unmarshall.getSourceCode();
298 
299         sc.add(className + " " + uncapClassName + " = new " + className + "();");
300 
301         ModelField contentField = null;
302 
303         List<ModelField> modelFields = getFieldsForXml(modelClass, getGeneratedVersion());
304 
305         // read all XML attributes first
306         for (ModelField field : modelFields) {
307             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
308 
309             if (xmlFieldMetadata.isAttribute()) {
310                 String tagName = xmlFieldMetadata.getTagName();
311                 if (tagName == null) {
312                     tagName = field.getName();
313                 }
314 
315                 sc.add("if ( element.attributeValue( \"" + tagName + "\" ) != null  )");
316                 sc.add("{");
317                 sc.indent();
318 
319                 writePrimitiveField(
320                         field,
321                         field.getType(),
322                         uncapClassName,
323                         "set" + capitalise(field.getName()),
324                         sc,
325                         jClass,
326                         "element",
327                         "childElement");
328 
329                 sc.unindent();
330                 sc.add("}");
331             }
332             // TODO check if we have already one with this type and throws Exception
333             if (xmlFieldMetadata.isContent()) {
334                 contentField = field;
335             }
336         }
337 
338         if (rootElement) {
339             sc.add("if ( strict )");
340             sc.add("{");
341             sc.indent();
342 
343             sc.add("if ( !element.getName().equals( tagName ) )");
344             sc.add("{");
345             sc.addIndented(
346                     "throw new DocumentException( \"Error parsing model: root element tag is '\" + element.getName() + \"' instead of '\" + tagName + \"'\" );");
347             sc.add("}");
348 
349             sc.unindent();
350             sc.add("}");
351         }
352 
353         if (contentField != null) {
354             writePrimitiveField(
355                     contentField,
356                     contentField.getType(),
357                     uncapClassName,
358                     "set" + capitalise(contentField.getName()),
359                     sc,
360                     jClass,
361                     null,
362                     "element");
363         } else {
364             sc.add("java.util.Set parsed = new java.util.HashSet();");
365 
366             sc.add("for ( Iterator i = element.nodeIterator(); i.hasNext(); )");
367             sc.add("{");
368             sc.indent();
369 
370             sc.add("Node node = (Node) i.next();");
371 
372             sc.add("if ( node.getNodeType() == Node.ELEMENT_NODE )");
373             // TODO: attach other NodeTypes to model in some way
374             sc.add("{");
375             sc.indent();
376 
377             sc.add("Element childElement = (Element) node;");
378 
379             boolean addElse = false;
380 
381             for (ModelField field : modelFields) {
382                 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
383 
384                 if (!xmlFieldMetadata.isAttribute()) {
385                     processField(field, xmlFieldMetadata, addElse, sc, uncapClassName, jClass);
386 
387                     addElse = true;
388                 }
389             }
390 
391             if (addElse) {
392                 sc.add("else");
393 
394                 sc.add("{");
395                 sc.indent();
396             }
397 
398             sc.add("checkUnknownElement( childElement, strict );");
399 
400             if (addElse) {
401                 sc.unindent();
402                 sc.add("}");
403             }
404 
405             sc.unindent();
406             sc.add("}");
407 
408             sc.unindent();
409             sc.add("}");
410         }
411 
412         sc.add("return " + uncapClassName + ";");
413 
414         jClass.addMethod(unmarshall);
415     }
416 
417     /**
418      * Generate code to process a field represented as an XML element.
419      *
420      * @param field the field to process
421      * @param xmlFieldMetadata its XML metadata
422      * @param addElse add an <code>else</code> statement before generating a new <code>if</code>
423      * @param sc the method source code to add to
424      * @param objectName the object name in the source
425      * @param jClass the generated class source file
426      */
427     private void processField(
428             ModelField field,
429             XmlFieldMetadata xmlFieldMetadata,
430             boolean addElse,
431             JSourceCode sc,
432             String objectName,
433             JClass jClass) {
434         String fieldTagName = resolveTagName(field, xmlFieldMetadata);
435 
436         String capFieldName = capitalise(field.getName());
437 
438         String singularName = singular(field.getName());
439 
440         String alias;
441         if (StringUtils.isEmpty(field.getAlias())) {
442             alias = "null";
443         } else {
444             alias = "\"" + field.getAlias() + "\"";
445         }
446 
447         String tagComparison = (addElse ? "else " : "") + "if ( checkFieldWithDuplicate( childElement, \""
448                 + fieldTagName + "\", " + alias + ", parsed ) )";
449 
450         if (field instanceof ModelAssociation) {
451             ModelAssociation association = (ModelAssociation) field;
452 
453             String associationName = association.getName();
454 
455             if (association.isOneMultiplicity()) {
456                 sc.add(tagComparison);
457 
458                 sc.add("{");
459                 sc.addIndented(objectName + ".set" + capFieldName + "( parse" + association.getTo() + "( \""
460                         + fieldTagName + "\", childElement, strict ) );");
461                 sc.add("}");
462             } else {
463                 // MANY_MULTIPLICITY
464 
465                 XmlAssociationMetadata xmlAssociationMetadata =
466                         (XmlAssociationMetadata) association.getAssociationMetadata(XmlAssociationMetadata.ID);
467 
468                 String valuesTagName = resolveTagName(fieldTagName, xmlAssociationMetadata);
469 
470                 String type = association.getType();
471 
472                 if (ModelDefault.LIST.equals(type) || ModelDefault.SET.equals(type)) {
473                     boolean wrappedItems = xmlAssociationMetadata.isWrappedItems();
474 
475                     if (wrappedItems) {
476                         sc.add(tagComparison);
477 
478                         sc.add("{");
479                         sc.indent();
480 
481                         sc.add(type + " " + associationName + " = " + association.getDefaultValue() + ";");
482 
483                         sc.add(objectName + ".set" + capFieldName + "( " + associationName + " );");
484 
485                         sc.add("for ( Iterator j = childElement.nodeIterator(); j.hasNext(); )");
486 
487                         sc.add("{");
488                         sc.indent();
489 
490                         sc.add("Node n = (Node) j.next();");
491 
492                         sc.add("if ( n.getNodeType() == Node.ELEMENT_NODE )");
493                         // TODO: track the whitespace in the model (other NodeTypes)
494 
495                         sc.add("{");
496                         sc.indent();
497 
498                         sc.add("Element listElement = (Element) n;");
499 
500                         sc.add("if ( \"" + valuesTagName + "\".equals( listElement.getName() ) )");
501 
502                         sc.add("{");
503                         sc.indent();
504                     } else {
505                         sc.add((addElse ? "else " : "") + "if ( \"" + valuesTagName
506                                 + "\".equals( childElement.getName() ) )");
507 
508                         sc.add("{");
509                         sc.indent();
510 
511                         sc.add("Element listElement = childElement;");
512 
513                         sc.add(type + " " + associationName + " = " + objectName + ".get" + capFieldName + "();");
514 
515                         sc.add("if ( " + associationName + " == null )");
516 
517                         sc.add("{");
518                         sc.indent();
519 
520                         sc.add(associationName + " = " + association.getDefaultValue() + ";");
521 
522                         sc.add(objectName + ".set" + capFieldName + "( " + associationName + " );");
523 
524                         sc.unindent();
525                         sc.add("}");
526                     }
527 
528                     if (isClassInModel(
529                             association.getTo(), field.getModelClass().getModel())) {
530                         sc.add(associationName + ".add( parse" + association.getTo() + "( \"" + valuesTagName
531                                 + "\", listElement, strict ) );");
532                     } else {
533                         writePrimitiveField(
534                                 association,
535                                 association.getTo(),
536                                 associationName,
537                                 "add",
538                                 sc,
539                                 jClass,
540                                 "childElement",
541                                 "listElement");
542                     }
543 
544                     if (wrappedItems) {
545                         sc.unindent();
546                         sc.add("}");
547 
548                         sc.add("else");
549 
550                         sc.add("{");
551                         sc.add("}");
552 
553                         sc.unindent();
554                         sc.add("}");
555 
556                         sc.unindent();
557                         sc.add("}");
558 
559                         sc.unindent();
560                         sc.add("}");
561                     } else {
562                         sc.unindent();
563                         sc.add("}");
564                     }
565                 } else {
566                     // Map or Properties
567 
568                     sc.add(tagComparison);
569 
570                     sc.add("{");
571                     sc.indent();
572 
573                     if (xmlAssociationMetadata.isMapExplode()) {
574                         sc.add("for ( Iterator j = childElement.nodeIterator(); j.hasNext(); )");
575 
576                         sc.add("{");
577                         sc.indent();
578 
579                         sc.add("Node n = (Node) j.next();");
580 
581                         sc.add("if ( n.getNodeType() == Node.ELEMENT_NODE )");
582                         // TODO: track the whitespace in the model (other NodeTypes)
583 
584                         sc.add("{");
585                         sc.indent();
586 
587                         sc.add("Element listElement = (Element) n;");
588 
589                         sc.add("if ( \"" + valuesTagName + "\".equals( listElement.getName() ) )");
590 
591                         sc.add("{");
592                         sc.indent();
593 
594                         sc.add("String key = null;");
595 
596                         sc.add("String value = null;");
597 
598                         sc.add("//" + xmlAssociationMetadata.getMapStyle() + " mode.");
599 
600                         sc.add("for ( Iterator k = listElement.nodeIterator(); k.hasNext(); )");
601 
602                         sc.add("{");
603                         sc.indent();
604 
605                         sc.add("Node nd = (Node) k.next();");
606 
607                         sc.add("if ( nd.getNodeType() == Node.ELEMENT_NODE )");
608                         // TODO: track the whitespace in the model (other NodeTypes)
609 
610                         sc.add("{");
611                         sc.indent();
612 
613                         sc.add("Element propertyElement = (Element) nd;");
614 
615                         sc.add("if ( \"key\".equals( propertyElement.getName() ) )");
616 
617                         sc.add("{");
618                         sc.addIndented("key = propertyElement.getText();");
619                         sc.add("}");
620 
621                         sc.add("else if ( \"value\".equals( propertyElement.getName() ) )");
622 
623                         sc.add("{");
624                         sc.addIndented("value = propertyElement.getText()"
625                                 + (xmlFieldMetadata.isTrim() ? ".trim()" : "") + ";");
626                         sc.add("}");
627 
628                         sc.add("else");
629 
630                         sc.add("{");
631                         sc.add("}");
632 
633                         sc.unindent();
634                         sc.add("}");
635 
636                         sc.unindent();
637                         sc.add("}");
638 
639                         sc.add(objectName + ".add" + capitalise(singularName) + "( key, value );");
640 
641                         sc.unindent();
642                         sc.add("}");
643 
644                         sc.unindent();
645                         sc.add("}");
646 
647                         sc.unindent();
648                         sc.add("}");
649                     } else {
650                         // INLINE Mode
651 
652                         sc.add("for ( Iterator j = childElement.nodeIterator(); j.hasNext(); )");
653 
654                         sc.add("{");
655                         sc.indent();
656 
657                         sc.add("Node n = (Node) j.next();");
658 
659                         sc.add("if ( n.getNodeType() == Node.ELEMENT_NODE )");
660                         // TODO: track the whitespace in the model (other NodeTypes)
661 
662                         sc.add("{");
663                         sc.indent();
664 
665                         sc.add("Element listElement = (Element) n;");
666 
667                         sc.add("String key = listElement.getName();");
668 
669                         sc.add("String value = listElement.getText()" + (xmlFieldMetadata.isTrim() ? ".trim()" : "")
670                                 + ";");
671 
672                         sc.add(objectName + ".add" + capitalise(singularName) + "( key, value );");
673 
674                         sc.unindent();
675                         sc.add("}");
676 
677                         sc.unindent();
678                         sc.add("}");
679                     }
680 
681                     sc.unindent();
682                     sc.add("}");
683                 }
684             }
685         } else {
686             sc.add(tagComparison);
687 
688             sc.add("{");
689             sc.indent();
690 
691             // ModelField
692             writePrimitiveField(
693                     field,
694                     field.getType(),
695                     objectName,
696                     "set" + capitalise(field.getName()),
697                     sc,
698                     jClass,
699                     "element",
700                     "childElement");
701 
702             sc.unindent();
703             sc.add("}");
704         }
705     }
706 
707     private void writePrimitiveField(
708             ModelField field,
709             String type,
710             String objectName,
711             String setterName,
712             JSourceCode sc,
713             JClass jClass,
714             String parentElementName,
715             String childElementName) {
716         XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
717 
718         String tagName = resolveTagName(field, xmlFieldMetadata);
719 
720         String parserGetter;
721         if (xmlFieldMetadata.isAttribute()) {
722             parserGetter = parentElementName + ".attributeValue( \"" + tagName + "\" )";
723         } else {
724             parserGetter = childElementName + ".getText()";
725         }
726 
727         // TODO: this and a default
728         //        if ( fieldMetaData.isRequired() )
729         //        {
730         //            parserGetter = "getRequiredAttributeValue( " + parserGetter + ", \"" + tagName + "\", parser,
731         // strict, encoding )";
732         //        }
733         //
734 
735         if (xmlFieldMetadata.isTrim()) {
736             parserGetter = "getTrimmedValue( " + parserGetter + " )";
737         }
738 
739         if ("boolean".equals(type)) {
740             sc.add(objectName + "." + setterName + "( getBooleanValue( " + parserGetter + ", \"" + tagName + "\" ) );");
741         } else if ("char".equals(type)) {
742             sc.add(objectName + "." + setterName + "( getCharacterValue( " + parserGetter + ", \"" + tagName
743                     + "\" ) );");
744         } else if ("double".equals(type)) {
745             sc.add(objectName + "." + setterName + "( getDoubleValue( " + parserGetter + ", \"" + tagName
746                     + "\", strict ) );");
747         } else if ("float".equals(type)) {
748             sc.add(objectName + "." + setterName + "( getFloatValue( " + parserGetter + ", \"" + tagName
749                     + "\", strict ) );");
750         } else if ("int".equals(type)) {
751             sc.add(objectName + "." + setterName + "( getIntegerValue( " + parserGetter + ", \"" + tagName
752                     + "\", strict ) );");
753         } else if ("long".equals(type)) {
754             sc.add(objectName + "." + setterName + "( getLongValue( " + parserGetter + ", \"" + tagName
755                     + "\", strict ) );");
756         } else if ("short".equals(type)) {
757             sc.add(objectName + "." + setterName + "( getShortValue( " + parserGetter + ", \"" + tagName
758                     + "\", strict ) );");
759         } else if ("byte".equals(type)) {
760             sc.add(objectName + "." + setterName + "( getByteValue( " + parserGetter + ", \"" + tagName
761                     + "\", strict ) );");
762         } else if ("String".equals(type) || "Boolean".equals(type)) {
763             // TODO: other Primitive types
764             sc.add(objectName + "." + setterName + "( " + parserGetter + " );");
765         } else if ("Date".equals(type)) {
766             sc.add("String dateFormat = "
767                     + (xmlFieldMetadata.getFormat() != null ? "\"" + xmlFieldMetadata.getFormat() + "\"" : "null")
768                     + ";");
769             sc.add(objectName + "." + setterName + "( getDateValue( " + parserGetter + ", \"" + tagName
770                     + "\", dateFormat ) );");
771         } else if ("DOM".equals(type)) {
772             sc.add(objectName + "." + setterName + "( writeElementToXpp3Dom( " + childElementName + " ) );");
773 
774             requiresDomSupport = true;
775         } else {
776             throw new IllegalArgumentException("Unknown type: " + type);
777         }
778     }
779 
780     private void writeHelpers(JClass jClass) {
781         JMethod method = new JMethod("getTrimmedValue", new JClass("String"), null);
782         method.getModifiers().makePrivate();
783 
784         method.addParameter(new JParameter(new JClass("String"), "s"));
785 
786         JSourceCode sc = method.getSourceCode();
787 
788         sc.add("if ( s != null )");
789 
790         sc.add("{");
791         sc.addIndented("s = s.trim();");
792         sc.add("}");
793 
794         sc.add("return s;");
795 
796         jClass.addMethod(method);
797 
798         // --------------------------------------------------------------------
799 
800         /* TODO
801                 method = new JMethod( new JClass( "String" ), "getRequiredAttributeValue" );
802                 method.addException( new JClass( "XmlPullParserException" ) );
803                 method.getModifiers().makePrivate();
804 
805                 method.addParameter( new JParameter( new JClass( "String" ), "s" ) );
806                 method.addParameter( new JParameter( new JClass( "String" ), "attribute" ) );
807                 method.addParameter( new JParameter( new JClass( "XmlPullParser" ), "parser" ) );
808                 method.addParameter( new JParameter( JClass.Boolean, "strict" ) );
809 
810                 sc = method.getSourceCode();
811 
812                 sc.add( "if ( s == null )" );
813 
814                 sc.add( "{" );
815                 sc.indent();
816 
817                 sc.add( "if ( strict )" );
818 
819                 sc.add( "{" );
820                 sc.addIndented(
821                     "throw new XmlPullParserException( \"Missing required value for attribute '\" + attribute + \"'\", parser, null );" );
822                 sc.add( "}" );
823 
824                 sc.unindent();
825                 sc.add( "}" );
826 
827                 sc.add( "return s;" );
828 
829                 jClass.addMethod( method );
830         */
831         // --------------------------------------------------------------------
832 
833         method = new JMethod("getBooleanValue", JType.BOOLEAN, null);
834         method.getModifiers().makePrivate();
835 
836         method.addParameter(new JParameter(new JClass("String"), "s"));
837         method.addParameter(new JParameter(new JClass("String"), "attribute"));
838 
839         sc = method.getSourceCode();
840 
841         sc.add("if ( s != null )");
842 
843         sc.add("{");
844         sc.addIndented("return Boolean.valueOf( s ).booleanValue();");
845         sc.add("}");
846 
847         sc.add("return false;");
848 
849         jClass.addMethod(method);
850 
851         // --------------------------------------------------------------------
852 
853         method = new JMethod("getCharacterValue", JType.CHAR, null);
854         method.getModifiers().makePrivate();
855 
856         method.addParameter(new JParameter(new JClass("String"), "s"));
857         method.addParameter(new JParameter(new JClass("String"), "attribute"));
858 
859         sc = method.getSourceCode();
860 
861         sc.add("if ( s != null )");
862 
863         sc.add("{");
864         sc.addIndented("return s.charAt( 0 );");
865         sc.add("}");
866 
867         sc.add("return 0;");
868 
869         jClass.addMethod(method);
870 
871         // --------------------------------------------------------------------
872 
873         method = convertNumericalType("getIntegerValue", JType.INT, "Integer.valueOf( s ).intValue()", "an integer");
874 
875         jClass.addMethod(method);
876 
877         // --------------------------------------------------------------------
878 
879         method = convertNumericalType(
880                 "getShortValue", JType.SHORT, "Short.valueOf( s ).shortValue()", "a short integer");
881 
882         jClass.addMethod(method);
883 
884         // --------------------------------------------------------------------
885 
886         method = convertNumericalType("getByteValue", JType.BYTE, "Byte.valueOf( s ).byteValue()", "a byte");
887 
888         jClass.addMethod(method);
889 
890         // --------------------------------------------------------------------
891 
892         method = convertNumericalType("getLongValue", JType.LONG, "Long.valueOf( s ).longValue()", "a long integer");
893 
894         jClass.addMethod(method);
895 
896         // --------------------------------------------------------------------
897 
898         method = convertNumericalType(
899                 "getFloatValue", JType.FLOAT, "Float.valueOf( s ).floatValue()", "a floating point number");
900 
901         jClass.addMethod(method);
902 
903         // --------------------------------------------------------------------
904 
905         method = convertNumericalType(
906                 "getDoubleValue", JType.DOUBLE, "Double.valueOf( s ).doubleValue()", "a floating point number");
907 
908         jClass.addMethod(method);
909 
910         // --------------------------------------------------------------------
911 
912         method = new JMethod("getDateValue", new JClass("java.util.Date"), null);
913         method.getModifiers().makePrivate();
914 
915         method.addParameter(new JParameter(new JClass("String"), "s"));
916         method.addParameter(new JParameter(new JClass("String"), "attribute"));
917         method.addParameter(new JParameter(new JClass("String"), "dateFormat"));
918         method.addException(new JClass("DocumentException"));
919 
920         writeDateParsingHelper(method.getSourceCode(), "new DocumentException( e.getMessage(), e )");
921 
922         jClass.addMethod(method);
923 
924         // --------------------------------------------------------------------
925 
926         method = new JMethod("checkFieldWithDuplicate", JType.BOOLEAN, null);
927         method.getModifiers().makePrivate();
928 
929         method.addParameter(new JParameter(new JClass("Element"), "element"));
930         method.addParameter(new JParameter(new JClass("String"), "tagName"));
931         method.addParameter(new JParameter(new JClass("String"), "alias"));
932         method.addParameter(new JParameter(new JClass("java.util.Set"), "parsed"));
933         method.addException(new JClass("DocumentException"));
934 
935         sc = method.getSourceCode();
936 
937         sc.add("if ( !( element.getName().equals( tagName ) || element.getName().equals( alias ) ) )");
938 
939         sc.add("{");
940         sc.addIndented("return false;");
941         sc.add("}");
942 
943         sc.add("if ( !parsed.add( tagName ) )");
944 
945         sc.add("{");
946         sc.addIndented("throw new DocumentException( \"Duplicated tag: '\" + tagName + \"'\" );");
947         sc.add("}");
948 
949         sc.add("return true;");
950 
951         jClass.addMethod(method);
952 
953         // --------------------------------------------------------------------
954 
955         method = new JMethod("checkUnknownElement", null, null);
956         method.getModifiers().makePrivate();
957 
958         method.addParameter(new JParameter(new JClass("Element"), "element"));
959         method.addParameter(new JParameter(JType.BOOLEAN, "strict"));
960         method.addException(new JClass("DocumentException"));
961 
962         sc = method.getSourceCode();
963 
964         sc.add("if ( strict )");
965 
966         sc.add("{");
967         sc.addIndented("throw new DocumentException( \"Unrecognised tag: '\" + element.getName() + \"'\" );");
968         sc.add("}");
969 
970         jClass.addMethod(method);
971     }
972 
973     private void writeDomHelpers(JClass jClass) {
974         JMethod method = new JMethod("writeElementToXpp3Dom", new JClass("Xpp3Dom"), null);
975         method.getModifiers().makePrivate();
976 
977         method.addParameter(new JParameter(new JClass("Element"), "element"));
978 
979         JSourceCode sc = method.getSourceCode();
980 
981         sc.add("Xpp3Dom xpp3Dom = new Xpp3Dom( element.getName() );");
982 
983         sc.add("if ( element.elements().isEmpty() && element.getText() != null )");
984         sc.add("{");
985         sc.addIndented("xpp3Dom.setValue( element.getText() );");
986         sc.add("}");
987 
988         sc.add("for ( Iterator i = element.attributeIterator(); i.hasNext(); )");
989         sc.add("{");
990         sc.indent();
991 
992         sc.add("Attribute attribute = (Attribute) i.next();");
993         sc.add("xpp3Dom.setAttribute( attribute.getName(), attribute.getValue() );");
994 
995         sc.unindent();
996         sc.add("}");
997 
998         // TODO: would be nice to track whitespace in here
999 
1000         sc.add("for ( Iterator i = element.elementIterator(); i.hasNext(); )");
1001         sc.add("{");
1002         sc.indent();
1003 
1004         sc.add("Element child = (Element) i.next();");
1005         sc.add("xpp3Dom.addChild( writeElementToXpp3Dom( child ) );");
1006 
1007         sc.unindent();
1008         sc.add("}");
1009 
1010         sc.add("return xpp3Dom;");
1011 
1012         jClass.addMethod(method);
1013     }
1014 
1015     private JMethod convertNumericalType(String methodName, JType returnType, String expression, String typeDesc) {
1016         JMethod method = new JMethod(methodName, returnType, null);
1017         method.addException(new JClass("DocumentException"));
1018         method.getModifiers().makePrivate();
1019 
1020         method.addParameter(new JParameter(new JClass("String"), "s"));
1021         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1022         method.addParameter(new JParameter(JClass.BOOLEAN, "strict"));
1023 
1024         JSourceCode sc = method.getSourceCode();
1025 
1026         sc.add("if ( s != null )");
1027 
1028         sc.add("{");
1029         sc.indent();
1030 
1031         sc.add("try");
1032 
1033         sc.add("{");
1034         sc.addIndented("return " + expression + ";");
1035         sc.add("}");
1036 
1037         sc.add("catch ( NumberFormatException nfe )");
1038 
1039         sc.add("{");
1040         sc.indent();
1041 
1042         sc.add("if ( strict )");
1043 
1044         sc.add("{");
1045         sc.addIndented("throw new DocumentException( \"Unable to parse element '\" + attribute + \"', must be "
1046                 + typeDesc + "\", nfe );");
1047         sc.add("}");
1048 
1049         sc.unindent();
1050 
1051         sc.add("}");
1052 
1053         sc.unindent();
1054 
1055         sc.add("}");
1056 
1057         sc.add("return 0;");
1058 
1059         return method;
1060     }
1061 }