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