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