View Javadoc

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