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