View Javadoc
1   package org.codehaus.modello.plugin.xpp3;
2   
3   /*
4    * Copyright (c) 2004, Codehaus.org
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.JCollectionType;
39  import org.codehaus.modello.plugin.java.javasource.JConstructor;
40  import org.codehaus.modello.plugin.java.javasource.JField;
41  import org.codehaus.modello.plugin.java.javasource.JMethod;
42  import org.codehaus.modello.plugin.java.javasource.JModifiers;
43  import org.codehaus.modello.plugin.java.javasource.JParameter;
44  import org.codehaus.modello.plugin.java.javasource.JSourceCode;
45  import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
46  import org.codehaus.modello.plugin.java.javasource.JType;
47  import org.codehaus.modello.plugin.java.metadata.JavaClassMetadata;
48  import org.codehaus.modello.plugin.java.metadata.JavaFieldMetadata;
49  import org.codehaus.modello.plugin.model.ModelClassMetadata;
50  import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata;
51  import org.codehaus.modello.plugins.xml.metadata.XmlClassMetadata;
52  import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata;
53  import org.codehaus.plexus.util.StringUtils;
54  
55  /**
56   * @author <a href="mailto:jason@modello.org">Jason van Zyl</a>
57   * @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse</a>
58   */
59  @Named("xpp3-reader")
60  public class Xpp3ReaderGenerator extends AbstractXpp3Generator {
61  
62      private static final String SOURCE_PARAM = "source";
63  
64      private static final String LOCATION_VAR = "_location";
65  
66      private String locationField;
67  
68      private String trackingArgs;
69  
70      @Override
71      protected void initialize(Model model, Properties parameters) throws ModelloException {
72          super.initialize(model, parameters);
73  
74          trackingArgs = locationField = "";
75  
76          if (isLocationTracking()) {
77              locationField =
78                      ((ModelClassMetadata) locationTracker.getMetadata(ModelClassMetadata.ID)).getLocationTracker();
79  
80              if (sourceTracker != null) {
81                  trackingArgs += ", " + SOURCE_PARAM;
82              }
83          }
84      }
85  
86      public void generate(Model model, Properties parameters) throws ModelloException {
87          initialize(model, parameters);
88  
89          try {
90              generateXpp3Reader();
91          } catch (IOException ex) {
92              throw new ModelloException("Exception while generating XPP3 Reader.", ex);
93          }
94      }
95  
96      private void writeAllClassesReaders(Model objectModel, JClass jClass) {
97          ModelClass root = objectModel.getClass(objectModel.getRoot(getGeneratedVersion()), getGeneratedVersion());
98  
99          for (ModelClass clazz : getClasses(objectModel)) {
100             if (isTrackingSupport(clazz)) {
101                 continue;
102             }
103 
104             writeClassReaders(clazz, jClass, root.getName().equals(clazz.getName()));
105         }
106     }
107 
108     private void writeClassReaders(ModelClass modelClass, JClass jClass, boolean rootElement) {
109         JavaClassMetadata javaClassMetadata =
110                 (JavaClassMetadata) modelClass.getMetadata(JavaClassMetadata.class.getName());
111 
112         // Skip abstract classes, no way to parse them out into objects
113         if (javaClassMetadata.isAbstract()) {
114             return;
115         }
116 
117         XmlClassMetadata xmlClassMetadata = (XmlClassMetadata) modelClass.getMetadata(XmlClassMetadata.ID);
118         if (!rootElement && !xmlClassMetadata.isStandaloneRead()) {
119             return;
120         }
121 
122         String className = modelClass.getName();
123 
124         String capClassName = capitalise(className);
125 
126         String readerMethodName = "read";
127         if (!rootElement) {
128             readerMethodName += capClassName;
129         }
130 
131         // ----------------------------------------------------------------------
132         // Write the read(XmlPullParser,boolean) method which will do the unmarshalling.
133         // ----------------------------------------------------------------------
134 
135         JMethod unmarshall = new JMethod(readerMethodName, new JClass(className), null);
136 
137         unmarshall.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
138         unmarshall.addParameter(new JParameter(JClass.BOOLEAN, "strict"));
139         addTrackingParameters(unmarshall);
140 
141         unmarshall.addException(new JClass("IOException"));
142         unmarshall.addException(new JClass("XmlPullParserException"));
143 
144         JSourceCode sc = unmarshall.getSourceCode();
145 
146         String tagName = resolveTagName(modelClass);
147         String variableName = uncapitalise(className);
148 
149         if (requiresDomSupport && !domAsXpp3) {
150             sc.add("if ( _doc_ == null )");
151             sc.add("{");
152             sc.indent();
153             sc.add("try");
154             sc.add("{");
155             sc.addIndented("initDoc();");
156             sc.add("}");
157             sc.add("catch ( javax.xml.parsers.ParserConfigurationException pce )");
158             sc.add("{");
159             sc.addIndented(
160                     "throw new XmlPullParserException( \"Unable to create DOM document: \" + pce.getMessage(), parser, pce );");
161             sc.add("}");
162             sc.unindent();
163             sc.add("}");
164         }
165 
166         sc.add(className + ' ' + variableName + " = null;");
167 
168         sc.add("int eventType = parser.getEventType();");
169 
170         sc.add("boolean parsed = false;");
171 
172         sc.add("while ( eventType != XmlPullParser.END_DOCUMENT )");
173 
174         sc.add("{");
175         sc.indent();
176 
177         sc.add("if ( eventType == XmlPullParser.START_TAG )");
178 
179         sc.add("{");
180         sc.indent();
181 
182         sc.add("if ( strict && ! \"" + tagName + "\".equals( parser.getName() ) )");
183 
184         sc.add("{");
185         sc.addIndented("throw new XmlPullParserException( \"Expected root element '" + tagName + "' but "
186                 + "found '\" + parser.getName() + \"'\", parser, null );");
187         sc.add("}");
188 
189         sc.add("else if ( parsed )");
190 
191         sc.add("{");
192         sc.addIndented("// fallback, already expected a XmlPullParserException due to invalid XML");
193         sc.addIndented("throw new XmlPullParserException( \"Duplicated tag: '" + tagName + "'\", parser, null );");
194         sc.add("}");
195 
196         sc.add(variableName + " = parse" + capClassName + "( parser, strict" + trackingArgs + " );");
197 
198         if (rootElement) {
199             sc.add(variableName + ".setModelEncoding( parser.getInputEncoding() );");
200         }
201         sc.add("parsed = true;");
202 
203         sc.unindent();
204         sc.add("}");
205 
206         sc.add("eventType = parser.next();");
207 
208         sc.unindent();
209         sc.add("}");
210 
211         sc.add("if ( parsed )");
212         sc.add("{");
213         sc.addIndented("return " + variableName + ';');
214         sc.add("}");
215 
216         sc.add("throw new XmlPullParserException( \"Expected root element '" + tagName + "' but "
217                 + "found no element at all: invalid XML document\", parser, null );");
218 
219         jClass.addMethod(unmarshall);
220 
221         // ----------------------------------------------------------------------
222         // Write the read(Reader[,boolean]) methods which will do the unmarshalling.
223         // ----------------------------------------------------------------------
224 
225         unmarshall = new JMethod(readerMethodName, new JClass(className), null);
226         unmarshall.setComment("@see XmlStreamReader");
227 
228         unmarshall.addParameter(new JParameter(new JClass("Reader"), "reader"));
229         unmarshall.addParameter(new JParameter(JClass.BOOLEAN, "strict"));
230         addTrackingParameters(unmarshall);
231 
232         unmarshall.addException(new JClass("IOException"));
233         unmarshall.addException(new JClass("XmlPullParserException"));
234 
235         sc = unmarshall.getSourceCode();
236 
237         sc.add(
238                 "XmlPullParser parser = addDefaultEntities ? new MXParser(EntityReplacementMap.defaultEntityReplacementMap) : new MXParser( );");
239 
240         sc.add("");
241 
242         sc.add("parser.setInput( reader );");
243 
244         sc.add("");
245 
246         sc.add("");
247 
248         sc.add("return " + readerMethodName + "( parser, strict" + trackingArgs + " );");
249 
250         jClass.addMethod(unmarshall);
251 
252         // ----------------------------------------------------------------------
253 
254         if (locationTracker == null) {
255             unmarshall = new JMethod(readerMethodName, new JClass(className), null);
256             unmarshall.setComment("@see XmlStreamReader");
257 
258             unmarshall.addParameter(new JParameter(new JClass("Reader"), "reader"));
259 
260             unmarshall.addException(new JClass("IOException"));
261             unmarshall.addException(new JClass("XmlPullParserException"));
262 
263             sc = unmarshall.getSourceCode();
264             sc.add("return " + readerMethodName + "( reader, true );");
265 
266             jClass.addMethod(unmarshall);
267         }
268 
269         // ----------------------------------------------------------------------
270         // Write the read(InputStream[,boolean]) methods which will do the unmarshalling.
271         // ----------------------------------------------------------------------
272 
273         unmarshall = new JMethod(readerMethodName, new JClass(className), null);
274 
275         unmarshall.addParameter(new JParameter(new JClass("InputStream"), "in"));
276         unmarshall.addParameter(new JParameter(JClass.BOOLEAN, "strict"));
277         addTrackingParameters(unmarshall);
278 
279         unmarshall.addException(new JClass("IOException"));
280         unmarshall.addException(new JClass("XmlPullParserException"));
281 
282         sc = unmarshall.getSourceCode();
283 
284         sc.add("return " + readerMethodName + "( new XmlStreamReader( in ), strict" + trackingArgs + " );");
285 
286         jClass.addMethod(unmarshall);
287 
288         // --------------------------------------------------------------------
289 
290         if (locationTracker == null) {
291             unmarshall = new JMethod(readerMethodName, new JClass(className), null);
292 
293             unmarshall.addParameter(new JParameter(new JClass("InputStream"), "in"));
294 
295             unmarshall.addException(new JClass("IOException"));
296             unmarshall.addException(new JClass("XmlPullParserException"));
297 
298             sc = unmarshall.getSourceCode();
299 
300             sc.add("return " + readerMethodName + "( new XmlStreamReader( in ) );");
301 
302             jClass.addMethod(unmarshall);
303         }
304     }
305 
306     private void generateXpp3Reader() throws ModelloException, IOException {
307         Model objectModel = getModel();
308 
309         String packageName =
310                 objectModel.getDefaultPackageName(isPackageWithVersion(), getGeneratedVersion()) + ".io.xpp3";
311 
312         String unmarshallerName = getFileName("Xpp3Reader" + (isLocationTracking() ? "Ex" : ""));
313 
314         JSourceWriter sourceWriter = newJSourceWriter(packageName, unmarshallerName);
315 
316         JClass jClass = new JClass(packageName + '.' + unmarshallerName);
317         initHeader(jClass);
318         suppressAllWarnings(objectModel, jClass);
319 
320         jClass.addImport("org.codehaus.plexus.util.xml.XmlStreamReader");
321         jClass.addImport("org.codehaus.plexus.util.xml.pull.MXParser");
322         jClass.addImport("org.codehaus.plexus.util.xml.pull.EntityReplacementMap");
323         jClass.addImport("org.codehaus.plexus.util.xml.pull.XmlPullParser");
324         jClass.addImport("org.codehaus.plexus.util.xml.pull.XmlPullParserException");
325         jClass.addImport("java.io.InputStream");
326         jClass.addImport("java.io.IOException");
327         jClass.addImport("java.io.Reader");
328         jClass.addImport("java.text.DateFormat");
329 
330         addModelImports(jClass, null);
331 
332         // ----------------------------------------------------------------------
333         // Write option setters
334         // ----------------------------------------------------------------------
335 
336         JConstructor constructor2 = new JConstructor(jClass);
337         constructor2
338                 .getSourceCode()
339                 .add("this( new ContentTransformer()\n" + "        {\n"
340                         + "            public String transform( String source, String fieldName )\n"
341                         + "            {\n" + "                return source;\n"
342                         + "            }\n" + "        } );");
343         jClass.addConstructor(constructor2);
344 
345         JConstructor constructor = new JConstructor(jClass);
346         constructor.addParameter(new JParameter(new JType("ContentTransformer"), "contentTransformer"));
347         constructor.getSourceCode().add("this.contentTransformer = contentTransformer;");
348         jClass.addConstructor(constructor);
349 
350         jClass.addSourceCode("public static interface ContentTransformer\n" + "{\n" + "    /**\n"
351                 + "     * Interpolate the value read from the xpp3 document\n"
352                 + "     * @param source The source value\n"
353                 + "     * @param fieldName A description of the field being interpolated. The implementation may use this to\n"
354                 + "     *                           log stuff.\n"
355                 + "     * @return The interpolated value.\n" + "     */\n"
356                 + "    String transform( String source, String fieldName );\n" + "}\n");
357 
358         // The Field
359         JField addDefaultEntities = new JField(JType.BOOLEAN, "addDefaultEntities");
360 
361         addDefaultEntities.setComment(
362                 "If set the parser will be loaded with all single characters from the XHTML specification.\n"
363                         + "The entities used:\n" + "<ul>\n"
364                         + "<li>http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent</li>\n"
365                         + "<li>http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent</li>\n"
366                         + "<li>http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent</li>\n" + "</ul>\n");
367 
368         addDefaultEntities.setInitString("true");
369 
370         jClass.addField(addDefaultEntities);
371 
372         JField contentTransformer = new JField(new JType("ContentTransformer"), "contentTransformer");
373         JModifiers jModifiers = new JModifiers();
374         jModifiers.setFinal(true);
375         contentTransformer.setModifiers(jModifiers);
376 
377         jClass.addField(contentTransformer);
378 
379         // The setter
380         JMethod addDefaultEntitiesSetter = new JMethod("setAddDefaultEntities");
381 
382         addDefaultEntitiesSetter.addParameter(new JParameter(JType.BOOLEAN, "addDefaultEntities"));
383 
384         addDefaultEntitiesSetter.setSourceCode("this.addDefaultEntities = addDefaultEntities;");
385 
386         addDefaultEntitiesSetter.setComment("Sets the state of the \"add default entities\" flag.");
387 
388         jClass.addMethod(addDefaultEntitiesSetter);
389 
390         // The getter
391         JMethod addDefaultEntitiesGetter = new JMethod("getAddDefaultEntities", JType.BOOLEAN, null);
392 
393         addDefaultEntitiesGetter.setComment("Returns the state of the \"add default entities\" flag.");
394 
395         addDefaultEntitiesGetter.setSourceCode("return addDefaultEntities;");
396 
397         jClass.addMethod(addDefaultEntitiesGetter);
398 
399         // ----------------------------------------------------------------------
400         // Write the class parsers
401         // ----------------------------------------------------------------------
402 
403         writeAllClassesParser(objectModel, jClass);
404 
405         // ----------------------------------------------------------------------
406         // Write the class readers
407         // ----------------------------------------------------------------------
408 
409         writeAllClassesReaders(objectModel, jClass);
410 
411         // ----------------------------------------------------------------------
412         // Write helpers
413         // ----------------------------------------------------------------------
414 
415         writeHelpers(jClass);
416 
417         if (requiresDomSupport) {
418             writeBuildDomMethod(jClass);
419 
420             if (isLocationTracking()) {
421                 writeBuildDomLocationTrackingMethod(jClass);
422             }
423         }
424 
425         // ----------------------------------------------------------------------
426         //
427         // ----------------------------------------------------------------------
428 
429         jClass.print(sourceWriter);
430 
431         sourceWriter.close();
432     }
433 
434     private void writeAllClassesParser(Model objectModel, JClass jClass) {
435         ModelClass root = objectModel.getClass(objectModel.getRoot(getGeneratedVersion()), getGeneratedVersion());
436 
437         for (ModelClass clazz : getClasses(objectModel)) {
438             if (isTrackingSupport(clazz)) {
439                 continue;
440             }
441 
442             writeClassParser(clazz, jClass, root.getName().equals(clazz.getName()));
443         }
444     }
445 
446     private void writeClassParser(ModelClass modelClass, JClass jClass, boolean rootElement) {
447         JavaClassMetadata javaClassMetadata =
448                 (JavaClassMetadata) modelClass.getMetadata(JavaClassMetadata.class.getName());
449 
450         // Skip abstract classes, no way to parse them out into objects
451         if (javaClassMetadata.isAbstract()) {
452             return;
453         }
454 
455         String className = modelClass.getName();
456 
457         String capClassName = capitalise(className);
458 
459         String uncapClassName = uncapitalise(className);
460 
461         JMethod unmarshall = new JMethod("parse" + capClassName, new JClass(className), null);
462         unmarshall.getModifiers().makePrivate();
463 
464         unmarshall.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
465         unmarshall.addParameter(new JParameter(JClass.BOOLEAN, "strict"));
466         addTrackingParameters(unmarshall);
467 
468         unmarshall.addException(new JClass("IOException"));
469         unmarshall.addException(new JClass("XmlPullParserException"));
470 
471         JSourceCode sc = unmarshall.getSourceCode();
472 
473         sc.add("String tagName = parser.getName();");
474         sc.add(className + " " + uncapClassName + " = new " + className + "();");
475 
476         if (locationTracker != null) {
477             sc.add(locationTracker.getName() + " " + LOCATION_VAR + ";");
478             writeNewSetLocation("\"\"", uncapClassName, null, sc);
479         }
480 
481         ModelField contentField = null;
482 
483         List<ModelField> modelFields = getFieldsForXml(modelClass, getGeneratedVersion());
484 
485         // read all XML attributes first
486         contentField = writeClassAttributesParser(modelFields, uncapClassName, rootElement, sc, jClass);
487 
488         // then read content, either content field or elements
489         if (contentField != null) {
490             writePrimitiveField(
491                     contentField,
492                     contentField.getType(),
493                     uncapClassName,
494                     uncapClassName,
495                     "\"\"",
496                     "set" + capitalise(contentField.getName()),
497                     sc);
498         } else {
499             // Write other fields
500 
501             if (hasJavaSourceSupport(5)) {
502                 sc.add("java.util.Set<String> parsed = new java.util.HashSet<String>();");
503             } else {
504                 sc.add("java.util.Set parsed = new java.util.HashSet();");
505             }
506 
507             sc.add("while ( ( strict ? parser.nextTag() : nextTag( parser ) ) == XmlPullParser.START_TAG )");
508 
509             sc.add("{");
510             sc.indent();
511 
512             boolean addElse = false;
513 
514             for (ModelField field : modelFields) {
515                 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
516 
517                 if (!xmlFieldMetadata.isAttribute()) {
518                     processField(field, xmlFieldMetadata, addElse, sc, uncapClassName, jClass);
519 
520                     addElse = true;
521                 }
522             }
523 
524             if (addElse) {
525                 sc.add("else");
526 
527                 sc.add("{");
528                 sc.indent();
529             }
530 
531             sc.add("checkUnknownElement( parser, strict );");
532 
533             if (addElse) {
534                 sc.unindent();
535                 sc.add("}");
536             }
537 
538             sc.unindent();
539             sc.add("}");
540         }
541 
542         sc.add("return " + uncapClassName + ";");
543 
544         jClass.addMethod(unmarshall);
545     }
546 
547     private ModelField writeClassAttributesParser(
548             List<ModelField> modelFields, String objectName, boolean rootElement, JSourceCode sc, JClass jClass) {
549         ModelField contentField = null;
550 
551         sc.add("for ( int i = parser.getAttributeCount() - 1; i >= 0; i-- )");
552         sc.add("{");
553         sc.indent();
554         sc.add("String name = parser.getAttributeName( i );");
555         sc.add("String value = parser.getAttributeValue( i );");
556         sc.add("");
557 
558         sc.add("if ( name.indexOf( ':' ) >= 0 )");
559         sc.add("{");
560         sc.addIndented("// just ignore attributes with non-default namespace (for example: xmlns:xsi)");
561         sc.add("}");
562         if (rootElement) {
563             sc.add("else if ( \"xmlns\".equals( name ) )");
564             sc.add("{");
565             sc.addIndented("// ignore xmlns attribute in root class, which is a reserved attribute name");
566             sc.add("}");
567         }
568 
569         for (ModelField field : modelFields) {
570             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
571 
572             if (xmlFieldMetadata.isAttribute()) {
573                 String tagName = resolveTagName(field, xmlFieldMetadata);
574 
575                 sc.add("else if ( \"" + tagName + "\".equals( name ) )");
576                 sc.add("{");
577                 sc.indent();
578 
579                 writePrimitiveField(
580                         field,
581                         field.getType(),
582                         objectName,
583                         objectName,
584                         "\"" + field.getName() + "\"",
585                         "set" + capitalise(field.getName()),
586                         sc);
587 
588                 sc.unindent();
589                 sc.add("}");
590             }
591             // TODO check if we have already one with this type and throws Exception
592             if (xmlFieldMetadata.isContent()) {
593                 contentField = field;
594             }
595         }
596         sc.add("else");
597 
598         sc.add("{");
599         sc.addIndented("checkUnknownAttribute( parser, name, tagName, strict );");
600         sc.add("}");
601 
602         sc.unindent();
603         sc.add("}");
604 
605         return contentField;
606     }
607 
608     /**
609      * Generate code to process a field represented as an XML element.
610      *
611      * @param field            the field to process
612      * @param xmlFieldMetadata its XML metadata
613      * @param addElse          add an <code>else</code> statement before generating a new <code>if</code>
614      * @param sc               the method source code to add to
615      * @param objectName       the object name in the source
616      * @param jClass           the generated class source file
617      */
618     private void processField(
619             ModelField field,
620             XmlFieldMetadata xmlFieldMetadata,
621             boolean addElse,
622             JSourceCode sc,
623             String objectName,
624             JClass jClass) {
625         String fieldTagName = resolveTagName(field, xmlFieldMetadata);
626 
627         String capFieldName = capitalise(field.getName());
628 
629         String singularName = singular(field.getName());
630 
631         String alias;
632         if (StringUtils.isEmpty(field.getAlias())) {
633             alias = "null";
634         } else {
635             alias = "\"" + field.getAlias() + "\"";
636         }
637 
638         String tagComparison = (addElse ? "else " : "") + "if ( checkFieldWithDuplicate( parser, \"" + fieldTagName
639                 + "\", " + alias + ", parsed ) )";
640 
641         if (!(field instanceof ModelAssociation)) { // model field
642             sc.add(tagComparison);
643 
644             sc.add("{");
645 
646             sc.indent();
647 
648             writePrimitiveField(
649                     field,
650                     field.getType(),
651                     objectName,
652                     objectName,
653                     "\"" + field.getName() + "\"",
654                     "set" + capFieldName,
655                     sc);
656 
657             sc.unindent();
658             sc.add("}");
659         } else { // model association
660             ModelAssociation association = (ModelAssociation) field;
661 
662             String associationName = association.getName();
663 
664             if (association.isOneMultiplicity()) {
665                 sc.add(tagComparison);
666 
667                 sc.add("{");
668                 sc.addIndented(objectName + ".set" + capFieldName + "( parse" + association.getTo() + "( parser, strict"
669                         + trackingArgs + " ) );");
670                 sc.add("}");
671             } else {
672                 // MANY_MULTIPLICITY
673 
674                 XmlAssociationMetadata xmlAssociationMetadata =
675                         (XmlAssociationMetadata) association.getAssociationMetadata(XmlAssociationMetadata.ID);
676 
677                 String valuesTagName = resolveTagName(fieldTagName, xmlAssociationMetadata);
678 
679                 String type = association.getType();
680 
681                 if (ModelDefault.LIST.equals(type) || ModelDefault.SET.equals(type)) {
682                     boolean wrappedItems = xmlAssociationMetadata.isWrappedItems();
683 
684                     boolean inModel = isClassInModel(
685                             association.getTo(), field.getModelClass().getModel());
686 
687                     JavaFieldMetadata javaFieldMetadata =
688                             (JavaFieldMetadata) association.getMetadata(JavaFieldMetadata.ID);
689 
690                     final boolean useJava5 = hasJavaSourceSupport(5);
691 
692                     String adder;
693 
694                     boolean requireSetter = false;
695 
696                     if (wrappedItems) {
697                         sc.add(tagComparison);
698 
699                         sc.add("{");
700                         sc.indent();
701 
702                         if (javaFieldMetadata.isSetter()) {
703                             String associationType = type;
704                             String defaultValue = association.getDefaultValue();
705                             if (useJava5) {
706                                 // defaultValue looks like java.util.ArrayList/*<Type>*/()
707                                 defaultValue = defaultValue.replace("/*", "").replace("*/", "");
708 
709                                 associationType = type + '<' + association.getTo() + '>';
710                             }
711                             sc.add(associationType + " " + associationName + " = " + defaultValue + ";");
712 
713                             requireSetter = true;
714 
715                             adder = associationName + ".add";
716                         } else {
717                             adder = objectName + ".add" + association.getTo();
718                         }
719 
720                         if (!inModel && locationTracker != null) {
721                             sc.add(locationTracker.getName() + " " + LOCATION_VAR + "s;");
722                             writeNewSetLocation(field, objectName, LOCATION_VAR + "s", sc);
723                         }
724 
725                         sc.add("while ( parser.nextTag() == XmlPullParser.START_TAG )");
726 
727                         sc.add("{");
728                         sc.indent();
729 
730                         sc.add("if ( \"" + valuesTagName + "\".equals( parser.getName() ) )");
731 
732                         sc.add("{");
733                         sc.indent();
734                     } else {
735                         sc.add((addElse ? "else " : "") + "if ( \"" + valuesTagName
736                                 + "\".equals( parser.getName() ) )");
737 
738                         sc.add("{");
739                         sc.indent();
740 
741                         if (javaFieldMetadata.isGetter() && javaFieldMetadata.isSetter()) {
742                             String associationType = type;
743                             String defaultValue = association.getDefaultValue();
744                             if (useJava5) {
745                                 // defaultValue looks like java.util.ArrayList/*<Type>*/()
746                                 defaultValue = defaultValue.replace("/*", "").replace("*/", "");
747 
748                                 associationType = type + '<' + association.getTo() + '>';
749                             }
750 
751                             sc.add(associationType + " " + associationName + " = " + objectName + ".get" + capFieldName
752                                     + "();");
753 
754                             sc.add("if ( " + associationName + " == null )");
755 
756                             sc.add("{");
757                             sc.indent();
758 
759                             sc.add(associationName + " = " + defaultValue + ";");
760 
761                             requireSetter = true;
762 
763                             sc.unindent();
764                             sc.add("}");
765 
766                             adder = associationName + ".add";
767                         } else {
768                             adder = objectName + ".add" + association.getTo();
769                         }
770 
771                         if (!inModel && locationTracker != null) {
772                             sc.add(locationTracker.getName() + " " + LOCATION_VAR + "s = " + objectName + ".get"
773                                     + capitalise(singular(locationField)) + "( \"" + field.getName()
774                                     + "\" );");
775                             sc.add("if ( " + LOCATION_VAR + "s == null )");
776                             sc.add("{");
777                             sc.indent();
778                             writeNewSetLocation(field, objectName, LOCATION_VAR + "s", sc);
779                             sc.unindent();
780                             sc.add("}");
781                         }
782                     }
783 
784                     if (inModel) {
785                         sc.add(adder + "( parse" + association.getTo() + "( parser, strict" + trackingArgs + " ) );");
786                     } else {
787                         String key;
788                         if (ModelDefault.SET.equals(type)) {
789                             key = "?";
790                         } else {
791                             key = (useJava5 ? "Integer.valueOf" : "new java.lang.Integer") + "( " + associationName
792                                     + ".size() )";
793                         }
794                         writePrimitiveField(
795                                 association, association.getTo(), associationName, LOCATION_VAR + "s", key, "add", sc);
796                     }
797 
798                     if (wrappedItems) {
799                         sc.unindent();
800                         sc.add("}");
801 
802                         sc.add("else");
803 
804                         sc.add("{");
805                         sc.addIndented("checkUnknownElement( parser, strict );");
806                         sc.add("}");
807 
808                         sc.unindent();
809                         sc.add("}");
810                     }
811                     if (requireSetter) {
812                         sc.add(objectName + ".set" + capFieldName + "( " + associationName + " );");
813                     }
814                     sc.unindent();
815                     sc.add("}");
816 
817                 } else {
818                     // Map or Properties
819 
820                     sc.add(tagComparison);
821 
822                     sc.add("{");
823                     sc.indent();
824 
825                     if (locationTracker != null) {
826                         sc.add(locationTracker.getName() + " " + LOCATION_VAR + "s;");
827                         writeNewSetLocation(field, objectName, LOCATION_VAR + "s", sc);
828                     }
829 
830                     if (xmlAssociationMetadata.isMapExplode()) {
831                         sc.add("while ( parser.nextTag() == XmlPullParser.START_TAG )");
832 
833                         sc.add("{");
834                         sc.indent();
835 
836                         sc.add("if ( \"" + valuesTagName + "\".equals( parser.getName() ) )");
837 
838                         sc.add("{");
839                         sc.indent();
840 
841                         sc.add("String key = null;");
842 
843                         sc.add("String value = null;");
844 
845                         writeNewLocation(LOCATION_VAR, sc);
846 
847                         sc.add("// " + xmlAssociationMetadata.getMapStyle() + " mode.");
848 
849                         sc.add("while ( parser.nextTag() == XmlPullParser.START_TAG )");
850 
851                         sc.add("{");
852                         sc.indent();
853 
854                         sc.add("if ( \"key\".equals( parser.getName() ) )");
855 
856                         sc.add("{");
857                         sc.addIndented("key = parser.nextText();");
858                         sc.add("}");
859 
860                         sc.add("else if ( \"value\".equals( parser.getName() ) )");
861 
862                         sc.add("{");
863                         sc.indent();
864                         writeNewLocation(LOCATION_VAR, sc);
865                         sc.add("value = parser.nextText()" + (xmlFieldMetadata.isTrim() ? ".trim()" : "") + ";");
866                         sc.unindent();
867                         sc.add("}");
868 
869                         sc.add("else");
870 
871                         sc.add("{");
872                         sc.addIndented("parser.nextText();");
873                         sc.add("}");
874 
875                         sc.unindent();
876                         sc.add("}");
877 
878                         sc.add(objectName + ".add" + capitalise(singularName) + "( key, value );");
879                         writeSetLocation("key", LOCATION_VAR + "s", LOCATION_VAR, sc);
880 
881                         sc.unindent();
882                         sc.add("}");
883 
884                         sc.add("parser.next();");
885 
886                         sc.unindent();
887                         sc.add("}");
888                     } else {
889                         // INLINE Mode
890 
891                         sc.add("while ( parser.nextTag() == XmlPullParser.START_TAG )");
892 
893                         sc.add("{");
894                         sc.indent();
895 
896                         sc.add("String key = parser.getName();");
897 
898                         writeNewSetLocation("key", LOCATION_VAR + "s", null, sc);
899 
900                         sc.add("String value = parser.nextText()" + (xmlFieldMetadata.isTrim() ? ".trim()" : "") + ";");
901 
902                         sc.add(objectName + ".add" + capitalise(singularName) + "( key, value );");
903 
904                         sc.unindent();
905                         sc.add("}");
906                     }
907 
908                     sc.unindent();
909                     sc.add("}");
910                 }
911             }
912         }
913     }
914 
915     private void writePrimitiveField(
916             ModelField field,
917             String type,
918             String objectName,
919             String locatorName,
920             String locationKey,
921             String setterName,
922             JSourceCode sc) {
923         XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
924 
925         String tagName = resolveTagName(field, xmlFieldMetadata);
926 
927         String parserGetter;
928         if (xmlFieldMetadata.isAttribute()) {
929             parserGetter = "value"; // local variable created in the parsing block
930         } else {
931             parserGetter = "parser.nextText()";
932         }
933 
934         /* TODO: this and a default
935                 if ( xmlFieldMetadata.isRequired() )
936                 {
937                     parserGetter = "getRequiredAttributeValue( " + parserGetter + ", \"" + tagName + "\", parser, strict )";
938                 }
939         */
940 
941         if (xmlFieldMetadata.isTrim()) {
942             parserGetter = "interpolatedTrimmed( " + parserGetter + ", \"" + tagName + "\" )";
943         }
944 
945         String keyCapture = "";
946         writeNewLocation(null, sc);
947         if (locationTracker != null && "?".equals(locationKey)) {
948             if (hasJavaSourceSupport(5)) {
949                 sc.add(type + " _key;");
950             } else {
951                 sc.add("Object _key;");
952             }
953             locationKey = "_key";
954             keyCapture = "_key = ";
955         } else {
956             writeSetLocation(locationKey, locatorName, null, sc);
957         }
958 
959         if ("boolean".equals(type) || "Boolean".equals(type)) {
960             sc.add(objectName + "." + setterName + "( " + keyCapture + "getBooleanValue( " + parserGetter + ", \""
961                     + tagName + "\", parser, \"" + field.getDefaultValue() + "\" ) );");
962         } else if ("char".equals(type)) {
963             sc.add(objectName + "." + setterName + "( " + keyCapture + "getCharacterValue( " + parserGetter + ", \""
964                     + tagName + "\", parser ) );");
965         } else if ("double".equals(type)) {
966             sc.add(objectName + "." + setterName + "( " + keyCapture + "getDoubleValue( " + parserGetter + ", \""
967                     + tagName + "\", parser, strict ) );");
968         } else if ("float".equals(type)) {
969             sc.add(objectName + "." + setterName + "( " + keyCapture + "getFloatValue( " + parserGetter + ", \""
970                     + tagName + "\", parser, strict ) );");
971         } else if ("int".equals(type)) {
972             sc.add(objectName + "." + setterName + "( " + keyCapture + "getIntegerValue( " + parserGetter + ", \""
973                     + tagName + "\", parser, strict ) );");
974         } else if ("long".equals(type)) {
975             sc.add(objectName + "." + setterName + "( " + keyCapture + "getLongValue( " + parserGetter + ", \""
976                     + tagName + "\", parser, strict ) );");
977         } else if ("short".equals(type)) {
978             sc.add(objectName + "." + setterName + "( " + keyCapture + "getShortValue( " + parserGetter + ", \""
979                     + tagName + "\", parser, strict ) );");
980         } else if ("byte".equals(type)) {
981             sc.add(objectName + "." + setterName + "( " + keyCapture + "getByteValue( " + parserGetter + ", \""
982                     + tagName + "\", parser, strict ) );");
983         } else if ("String".equals(type)) {
984             // TODO: other Primitive types
985             sc.add(objectName + "." + setterName + "( " + keyCapture + parserGetter + " );");
986         } else if ("Date".equals(type)) {
987             String format = xmlFieldMetadata.getFormat();
988             sc.add("String dateFormat = " + (format != null ? "\"" + format + "\"" : "null") + ";");
989             sc.add(objectName + "." + setterName + "( " + keyCapture + "getDateValue( " + parserGetter + ", \""
990                     + tagName + "\", dateFormat, parser ) );");
991         } else if ("DOM".equals(type)) {
992             String locationBuilderParam = "";
993             if (isLocationTracking() && domAsXpp3) {
994                 locationBuilderParam = ", new Xpp3DomBuilderInputLocationBuilder( " + LOCATION_VAR + " )";
995             }
996             sc.add(objectName + "." + setterName + "( " + keyCapture
997                     + (domAsXpp3 ? "org.codehaus.plexus.util.xml.Xpp3DomBuilder.build" : "buildDom")
998                     + "( parser, " + xmlFieldMetadata.isTrim() + locationBuilderParam + " ) );");
999 
1000             requiresDomSupport = true;
1001         } else {
1002             throw new IllegalArgumentException("Unknown type: " + type);
1003         }
1004 
1005         if (keyCapture.length() > 0) {
1006             writeSetLocation(locationKey, locatorName, null, sc);
1007         }
1008     }
1009 
1010     private void writeBuildDomMethod(JClass jClass) {
1011         if (domAsXpp3) {
1012             // no need, Xpp3DomBuilder provided by plexus-utils
1013             return;
1014         }
1015 
1016         jClass.addField(new JField(new JClass("org.w3c.dom.Document"), "_doc_"));
1017         JMethod method = new JMethod("initDoc", null, null);
1018         method.getModifiers().makePrivate();
1019         method.addException(new JClass("javax.xml.parsers.ParserConfigurationException"));
1020 
1021         JSourceCode sc = method.getSourceCode();
1022         sc.add(
1023                 "javax.xml.parsers.DocumentBuilderFactory dbfac = javax.xml.parsers.DocumentBuilderFactory.newInstance();");
1024         sc.add("javax.xml.parsers.DocumentBuilder docBuilder = dbfac.newDocumentBuilder();");
1025         sc.add("_doc_ = docBuilder.newDocument();");
1026         jClass.addMethod(method);
1027 
1028         String type = "org.w3c.dom.Element";
1029         method = new JMethod("buildDom", new JType(type), null);
1030         method.getModifiers().makePrivate();
1031         method.addParameter(new JParameter(new JType("XmlPullParser"), "parser"));
1032         method.addParameter(new JParameter(JType.BOOLEAN, "trim"));
1033         method.addException(new JClass("XmlPullParserException"));
1034         method.addException(new JClass("IOException"));
1035 
1036         sc = method.getSourceCode();
1037 
1038         sc.add("java.util.Stack elements = new java.util.Stack();");
1039 
1040         sc.add("java.util.Stack values = new java.util.Stack();");
1041 
1042         sc.add("int eventType = parser.getEventType();");
1043 
1044         sc.add("boolean spacePreserve = false;");
1045 
1046         sc.add("while ( eventType != XmlPullParser.END_DOCUMENT )");
1047         sc.add("{");
1048         sc.indent();
1049 
1050         sc.add("if ( eventType == XmlPullParser.START_TAG )");
1051         sc.add("{");
1052         sc.indent();
1053         sc.add("spacePreserve = false;");
1054         sc.add("String rawName = parser.getName();");
1055 
1056         sc.add("org.w3c.dom.Element element = _doc_.createElement( rawName );");
1057 
1058         sc.add("if ( !elements.empty() )");
1059         sc.add("{");
1060         sc.indent();
1061         sc.add(type + " parent = (" + type + ") elements.peek();");
1062 
1063         sc.add("parent.appendChild( element );");
1064         sc.unindent();
1065         sc.add("}");
1066 
1067         sc.add("elements.push( element );");
1068 
1069         sc.add("if ( parser.isEmptyElementTag() )");
1070         sc.add("{");
1071         sc.addIndented("values.push( null );");
1072         sc.add("}");
1073         sc.add("else");
1074         sc.add("{");
1075         sc.addIndented("values.push( new StringBuilder() );");
1076         sc.add("}");
1077 
1078         sc.add("int attributesSize = parser.getAttributeCount();");
1079 
1080         sc.add("for ( int i = 0; i < attributesSize; i++ )");
1081         sc.add("{");
1082         sc.indent();
1083         sc.add("String name = parser.getAttributeName( i );");
1084 
1085         sc.add("String value = parser.getAttributeValue( i );");
1086 
1087         sc.add("element.setAttribute( name, value );");
1088         sc.add("spacePreserve = spacePreserve || ( \"xml:space\".equals( name ) && \"preserve\".equals( value ) );");
1089         sc.unindent();
1090         sc.add("}");
1091         sc.unindent();
1092         sc.add("}");
1093         sc.add("else if ( eventType == XmlPullParser.TEXT )");
1094         sc.add("{");
1095         sc.indent();
1096         sc.add("StringBuilder valueBuffer = (StringBuilder) values.peek();");
1097 
1098         sc.add("String text = parser.getText();");
1099 
1100         sc.add("if ( trim && !spacePreserve )");
1101         sc.add("{");
1102         sc.addIndented("text = text.trim();");
1103         sc.add("}");
1104 
1105         sc.add("valueBuffer.append( text );");
1106         sc.unindent();
1107         sc.add("}");
1108         sc.add("else if ( eventType == XmlPullParser.END_TAG )");
1109         sc.add("{");
1110         sc.indent();
1111 
1112         sc.add(type + " element = (" + type + ") elements.pop();");
1113 
1114         sc.add("// this Object could be null if it is a singleton tag");
1115         sc.add("Object accumulatedValue = values.pop();");
1116 
1117         sc.add("if ( !element.hasChildNodes() )");
1118         sc.add("{");
1119         sc.addIndented("element.setTextContent( ( accumulatedValue == null ) ? null : accumulatedValue.toString() );");
1120         sc.add("}");
1121 
1122         sc.add("if ( values.empty() )");
1123         sc.add("{");
1124         sc.addIndented("return element;");
1125         sc.add("}");
1126         sc.unindent();
1127         sc.add("}");
1128 
1129         sc.add("eventType = parser.next();");
1130 
1131         sc.unindent();
1132         sc.add("}");
1133 
1134         sc.add("throw new IllegalStateException( \"End of document found before returning to 0 depth\" );");
1135 
1136         jClass.addMethod(method);
1137     }
1138 
1139     private void writeBuildDomLocationTrackingMethod(JClass jClass) {
1140         if (!domAsXpp3) {
1141             // no need, input location tracking available only for Xpp3
1142             return;
1143         }
1144 
1145         JClass builderClass = jClass.createInnerClass("Xpp3DomBuilderInputLocationBuilder");
1146         builderClass.getModifiers().makePrivate();
1147         builderClass.getModifiers().setStatic(true);
1148         builderClass.addInterface("org.codehaus.plexus.util.xml.Xpp3DomBuilder.InputLocationBuilder");
1149 
1150         JField field = new JField(new JType(locationTracker.getName()), "rootLocation");
1151         field.getModifiers().setFinal(true);
1152         builderClass.addField(field);
1153 
1154         JConstructor constructor = new JConstructor(builderClass);
1155         constructor.addParameter(new JParameter(new JType(locationTracker.getName()), "rootLocation"));
1156         constructor.getSourceCode().add("this.rootLocation = rootLocation;");
1157         builderClass.addConstructor(constructor);
1158 
1159         JMethod method = new JMethod("toInputLocation", new JType("Object"), null);
1160         method.addParameter(new JParameter(new JType("XmlPullParser"), "parser"));
1161         method.getSourceCode().add("return " + buildNewLocation("rootLocation.getSource()") + ";");
1162         builderClass.addMethod(method);
1163     }
1164 
1165     private void writeHelpers(JClass jClass) {
1166         jClass.addMethod(getTrimmedValueMethod());
1167         jClass.addMethod(getInterpolatedTrimmed());
1168         jClass.addMethod(getRequiredAttributeValueMethod());
1169         jClass.addMethod(getBooleanValueMethod());
1170         jClass.addMethod(getBooleanValue2Method());
1171         jClass.addMethod(getCharacterValueMethod());
1172         jClass.addMethod(
1173                 convertNumericalType("getIntegerValue", JType.INT, "Integer.valueOf( s ).intValue()", "an integer"));
1174         jClass.addMethod(convertNumericalType(
1175                 "getShortValue", JType.SHORT, "Short.valueOf( s ).shortValue()", "a short integer"));
1176         jClass.addMethod(convertNumericalType("getByteValue", JType.BYTE, "Byte.valueOf( s ).byteValue()", "a byte"));
1177         jClass.addMethod(
1178                 convertNumericalType("getLongValue", JType.LONG, "Long.valueOf( s ).longValue()", "a long integer"));
1179         jClass.addMethod(convertNumericalType(
1180                 "getFloatValue", JType.FLOAT, "Float.valueOf( s ).floatValue()", "a floating point number"));
1181         jClass.addMethod(convertNumericalType(
1182                 "getDoubleValue", JType.DOUBLE, "Double.valueOf( s ).doubleValue()", "a floating point number"));
1183         jClass.addMethod(getDateValueMethod());
1184         jClass.addMethod(getDateValue2Method());
1185         jClass.addMethod(getCheckFieldWithDuplicateMethod());
1186         jClass.addMethod(getCheckUnknonwElement2Method());
1187         jClass.addMethod(getCheckUnknownAttributeMethod());
1188         jClass.addMethod(getNextTagMethod());
1189     }
1190 
1191     private JMethod getCheckUnknonwElement2Method() {
1192         JMethod method;
1193         JSourceCode sc;
1194         method = new JMethod("checkUnknownElement", null, null);
1195         method.getModifiers().makePrivate();
1196 
1197         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1198         method.addParameter(new JParameter(JType.BOOLEAN, "strict"));
1199         method.addException(new JClass("XmlPullParserException"));
1200         method.addException(new JClass("IOException"));
1201 
1202         sc = method.getSourceCode();
1203 
1204         sc.add("if ( strict )");
1205 
1206         sc.add("{");
1207         sc.addIndented(
1208                 "throw new XmlPullParserException( \"Unrecognised tag: '\" + parser.getName() + \"'\", parser, null );");
1209         sc.add("}");
1210 
1211         sc.add("");
1212 
1213         sc.add("for ( int unrecognizedTagCount = 1; unrecognizedTagCount > 0; )");
1214         sc.add("{");
1215         sc.indent();
1216         sc.add("int eventType = parser.next();");
1217         sc.add("if ( eventType == XmlPullParser.START_TAG )");
1218         sc.add("{");
1219         sc.addIndented("unrecognizedTagCount++;");
1220         sc.add("}");
1221         sc.add("else if ( eventType == XmlPullParser.END_TAG )");
1222         sc.add("{");
1223         sc.addIndented("unrecognizedTagCount--;");
1224         sc.add("}");
1225         sc.unindent();
1226         sc.add("}");
1227         return method;
1228     }
1229 
1230     private JMethod getNextTagMethod() {
1231         JMethod method;
1232         JSourceCode sc;
1233         method = new JMethod("nextTag", JType.INT, null);
1234         method.addException(new JClass("IOException"));
1235         method.addException(new JClass("XmlPullParserException"));
1236         method.getModifiers().makePrivate();
1237 
1238         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1239 
1240         sc = method.getSourceCode();
1241 
1242         sc.add("int eventType = parser.next();");
1243         sc.add("if ( eventType == XmlPullParser.TEXT )");
1244         sc.add("{");
1245         sc.addIndented("eventType = parser.next();");
1246         sc.add("}");
1247         sc.add("if ( eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_TAG )");
1248         sc.add("{");
1249         sc.addIndented(
1250                 "throw new XmlPullParserException( \"expected START_TAG or END_TAG not \" + XmlPullParser.TYPES[eventType], parser, null );");
1251         sc.add("}");
1252         sc.add("return eventType;");
1253         return method;
1254     }
1255 
1256     private JMethod getCheckUnknownAttributeMethod() {
1257         JMethod method;
1258         JSourceCode sc;
1259         method = new JMethod("checkUnknownAttribute", null, null);
1260         method.getModifiers().makePrivate();
1261 
1262         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1263         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1264         method.addParameter(new JParameter(new JClass("String"), "tagName"));
1265         method.addParameter(new JParameter(JType.BOOLEAN, "strict"));
1266         method.addException(new JClass("XmlPullParserException"));
1267         method.addException(new JClass("IOException"));
1268 
1269         sc = method.getSourceCode();
1270 
1271         if (strictXmlAttributes) {
1272             sc.add(
1273                     "// strictXmlAttributes = true for model: if strict == true, not only elements are checked but attributes too");
1274             sc.add("if ( strict )");
1275 
1276             sc.add("{");
1277             sc.addIndented(
1278                     "throw new XmlPullParserException( \"Unknown attribute '\" + attribute + \"' for tag '\" + tagName + \"'\", parser, null );");
1279             sc.add("}");
1280         } else {
1281             sc.add(
1282                     "// strictXmlAttributes = false for model: always ignore unknown XML attribute, even if strict == true");
1283         }
1284         return method;
1285     }
1286 
1287     private JMethod getCheckFieldWithDuplicateMethod() {
1288         JMethod method;
1289         JSourceCode sc;
1290         method = new JMethod("checkFieldWithDuplicate", JType.BOOLEAN, null);
1291         method.getModifiers().makePrivate();
1292 
1293         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1294         method.addParameter(new JParameter(new JClass("String"), "tagName"));
1295         method.addParameter(new JParameter(new JClass("String"), "alias"));
1296         method.addParameter(new JParameter(
1297                 new JCollectionType("java.util.Set", new JType("String"), hasJavaSourceSupport(5)), "parsed"));
1298         method.addException(new JClass("XmlPullParserException"));
1299 
1300         sc = method.getSourceCode();
1301 
1302         sc.add("if ( !( parser.getName().equals( tagName ) || parser.getName().equals( alias ) ) )");
1303 
1304         sc.add("{");
1305         sc.addIndented("return false;");
1306         sc.add("}");
1307 
1308         sc.add("if ( !parsed.add( tagName ) )");
1309 
1310         sc.add("{");
1311         sc.addIndented("throw new XmlPullParserException( \"Duplicated tag: '\" + tagName + \"'\", parser, null );");
1312         sc.add("}");
1313 
1314         sc.add("return true;");
1315         return method;
1316     }
1317 
1318     private JMethod getDateValue2Method() {
1319         JMethod method;
1320         method = new JMethod("getDateValue", new JClass("java.util.Date"), null);
1321         method.addException(new JClass("XmlPullParserException"));
1322         method.getModifiers().makePrivate();
1323 
1324         method.addParameter(new JParameter(new JClass("String"), "s"));
1325         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1326         method.addParameter(new JParameter(new JClass("String"), "dateFormat"));
1327         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1328         method.addException(new JClass("XmlPullParserException"));
1329 
1330         writeDateParsingHelper(method.getSourceCode(), "new XmlPullParserException( e.getMessage(), parser, e )");
1331         return method;
1332     }
1333 
1334     private JMethod getDateValueMethod() {
1335         JMethod method;
1336         JSourceCode sc;
1337         method = new JMethod("getDateValue", new JClass("java.util.Date"), null);
1338         method.addException(new JClass("XmlPullParserException"));
1339         method.getModifiers().makePrivate();
1340 
1341         method.addParameter(new JParameter(new JClass("String"), "s"));
1342         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1343         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1344         method.addException(new JClass("XmlPullParserException"));
1345 
1346         sc = method.getSourceCode();
1347 
1348         sc.add("return getDateValue( s, attribute, null, parser );");
1349         return method;
1350     }
1351 
1352     private JMethod getCharacterValueMethod() {
1353         JMethod method;
1354         JSourceCode sc;
1355         method = new JMethod("getCharacterValue", JType.CHAR, null);
1356         method.addException(new JClass("XmlPullParserException"));
1357         method.getModifiers().makePrivate();
1358 
1359         method.addParameter(new JParameter(new JClass("String"), "s"));
1360         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1361         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1362 
1363         sc = method.getSourceCode();
1364 
1365         sc.add("if ( s != null )");
1366 
1367         sc.add("{");
1368         sc.addIndented("return s.charAt( 0 );");
1369         sc.add("}");
1370 
1371         sc.add("return 0;");
1372         return method;
1373     }
1374 
1375     private JMethod getBooleanValue2Method() {
1376         JMethod method;
1377         JSourceCode sc;
1378         method = new JMethod("getBooleanValue", JType.BOOLEAN, null);
1379         method.addException(new JClass("XmlPullParserException"));
1380         method.getModifiers().makePrivate();
1381 
1382         method.addParameter(new JParameter(new JClass("String"), "s"));
1383         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1384         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1385         method.addParameter(new JParameter(new JClass("String"), "defaultValue"));
1386 
1387         sc = method.getSourceCode();
1388 
1389         sc.add("if ( s != null && s.length() != 0 )");
1390 
1391         sc.add("{");
1392         sc.addIndented("return Boolean.valueOf( s ).booleanValue();");
1393         sc.add("}");
1394 
1395         sc.add("if ( defaultValue != null )");
1396 
1397         sc.add("{");
1398         sc.addIndented("return Boolean.valueOf( defaultValue ).booleanValue();");
1399         sc.add("}");
1400 
1401         sc.add("return false;");
1402         return method;
1403     }
1404 
1405     private JMethod getBooleanValueMethod() {
1406         JMethod method;
1407         JSourceCode sc;
1408         method = new JMethod("getBooleanValue", JType.BOOLEAN, null);
1409         method.addException(new JClass("XmlPullParserException"));
1410         method.getModifiers().makePrivate();
1411 
1412         method.addParameter(new JParameter(new JClass("String"), "s"));
1413         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1414         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1415 
1416         sc = method.getSourceCode();
1417 
1418         sc.add("return getBooleanValue( s, attribute, parser, null );");
1419         return method;
1420     }
1421 
1422     private JMethod getRequiredAttributeValueMethod() {
1423         JMethod method;
1424         JSourceCode sc;
1425         method = new JMethod("getRequiredAttributeValue", new JClass("String"), null);
1426         method.addException(new JClass("XmlPullParserException"));
1427         method.getModifiers().makePrivate();
1428 
1429         method.addParameter(new JParameter(new JClass("String"), "s"));
1430         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1431         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1432         method.addParameter(new JParameter(JClass.BOOLEAN, "strict"));
1433 
1434         sc = method.getSourceCode();
1435 
1436         sc.add("if ( s == null )");
1437 
1438         sc.add("{");
1439         sc.indent();
1440 
1441         sc.add("if ( strict )");
1442 
1443         sc.add("{");
1444         sc.addIndented(
1445                 "throw new XmlPullParserException( \"Missing required value for attribute '\" + attribute + \"'\", parser, null );");
1446         sc.add("}");
1447 
1448         sc.unindent();
1449         sc.add("}");
1450 
1451         sc.add("return s;");
1452         return method;
1453     }
1454 
1455     private JMethod getTrimmedValueMethod() {
1456         JMethod method = new JMethod("getTrimmedValue", new JClass("String"), null);
1457         method.getModifiers().makePrivate();
1458 
1459         method.addParameter(new JParameter(new JClass("String"), "s"));
1460 
1461         JSourceCode sc = method.getSourceCode();
1462 
1463         sc.add("if ( s != null )");
1464 
1465         sc.add("{");
1466         sc.addIndented("s = s.trim();");
1467         sc.add("}");
1468 
1469         sc.add("return s;");
1470         return method;
1471     }
1472 
1473     private JMethod getInterpolatedTrimmed() {
1474         JMethod method = new JMethod("interpolatedTrimmed", new JClass("String"), null);
1475         method.getModifiers().makePrivate();
1476 
1477         method.addParameter(new JParameter(new JClass("String"), "value"));
1478         method.addParameter(new JParameter(new JClass("String"), "context"));
1479 
1480         JSourceCode sc = method.getSourceCode();
1481 
1482         sc.add("return getTrimmedValue( contentTransformer.transform( value, context ) );");
1483         return method;
1484     }
1485 
1486     private JMethod convertNumericalType(String methodName, JType returnType, String expression, String typeDesc) {
1487         JMethod method = new JMethod(methodName, returnType, null);
1488         method.addException(new JClass("XmlPullParserException"));
1489         method.getModifiers().makePrivate();
1490 
1491         method.addParameter(new JParameter(new JClass("String"), "s"));
1492         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1493         method.addParameter(new JParameter(new JClass("XmlPullParser"), "parser"));
1494         method.addParameter(new JParameter(JClass.BOOLEAN, "strict"));
1495 
1496         JSourceCode sc = method.getSourceCode();
1497 
1498         sc.add("if ( s != null )");
1499 
1500         sc.add("{");
1501         sc.indent();
1502 
1503         sc.add("try");
1504 
1505         sc.add("{");
1506         sc.addIndented("return " + expression + ";");
1507         sc.add("}");
1508 
1509         sc.add("catch ( NumberFormatException nfe )");
1510 
1511         sc.add("{");
1512         sc.indent();
1513 
1514         sc.add("if ( strict )");
1515 
1516         sc.add("{");
1517         sc.addIndented("throw new XmlPullParserException( \"Unable to parse element '\" + attribute + \"', must be "
1518                 + typeDesc + "\", parser, nfe );");
1519         sc.add("}");
1520 
1521         sc.unindent();
1522         sc.add("}");
1523 
1524         sc.unindent();
1525         sc.add("}");
1526 
1527         sc.add("return 0;");
1528 
1529         return method;
1530     }
1531 
1532     private void addTrackingParameters(JMethod method) {
1533         if (sourceTracker != null) {
1534             method.addParameter(new JParameter(new JClass(sourceTracker.getName()), SOURCE_PARAM));
1535         }
1536     }
1537 
1538     private void writeNewSetLocation(ModelField field, String objectName, String trackerVariable, JSourceCode sc) {
1539         writeNewSetLocation("\"" + field.getName() + "\"", objectName, trackerVariable, sc);
1540     }
1541 
1542     private void writeNewSetLocation(String key, String objectName, String trackerVariable, JSourceCode sc) {
1543         writeNewLocation(trackerVariable, sc);
1544         writeSetLocation(key, objectName, trackerVariable, sc);
1545     }
1546 
1547     private void writeNewLocation(String trackerVariable, JSourceCode sc) {
1548         if (locationTracker == null) {
1549             return;
1550         }
1551 
1552         sc.add(((trackerVariable != null) ? trackerVariable : LOCATION_VAR) + " = " + buildNewLocation(SOURCE_PARAM)
1553                 + ";");
1554     }
1555 
1556     private String buildNewLocation(String source) {
1557         return "new " + locationTracker.getName() + "( parser.getLineNumber(), parser.getColumnNumber()"
1558                 + ((sourceTracker != null) ? ", " + source : "") + " )";
1559     }
1560 
1561     private void writeSetLocation(String key, String objectName, String trackerVariable, JSourceCode sc) {
1562         if (locationTracker == null) {
1563             return;
1564         }
1565 
1566         String variable = (trackerVariable != null) ? trackerVariable : LOCATION_VAR;
1567 
1568         sc.add(objectName + ".set" + capitalise(singular(locationField)) + "( " + key + ", " + variable + " );");
1569     }
1570 }