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