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 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.JField;
33  import org.codehaus.modello.plugin.java.javasource.JMethod;
34  import org.codehaus.modello.plugin.java.javasource.JParameter;
35  import org.codehaus.modello.plugin.java.javasource.JSourceCode;
36  import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
37  import org.codehaus.modello.plugin.java.javasource.JType;
38  import org.codehaus.modello.plugin.java.metadata.JavaFieldMetadata;
39  import org.codehaus.modello.plugin.model.ModelClassMetadata;
40  import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata;
41  import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata;
42  import org.codehaus.modello.plugins.xml.metadata.XmlModelMetadata;
43  
44  import java.io.IOException;
45  import java.util.List;
46  import java.util.Properties;
47  
48  /**
49   * @author <a href="mailto:jason@modello.org">Jason van Zyl </a>
50   * @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse </a>
51   */
52  public class Xpp3WriterGenerator
53      extends AbstractXpp3Generator
54  {
55      private boolean requiresDomSupport;
56  
57      public void generate( Model model, Properties parameters )
58          throws ModelloException
59      {
60          initialize( model, parameters );
61  
62          requiresDomSupport = false;
63  
64          try
65          {
66              generateXpp3Writer();
67          }
68          catch ( IOException ex )
69          {
70              throw new ModelloException( "Exception while generating XPP3 Writer.", ex );
71          }
72      }
73  
74      private void generateXpp3Writer()
75          throws ModelloException, IOException
76      {
77          Model objectModel = getModel();
78  
79          String packageName = objectModel.getDefaultPackageName( isPackageWithVersion(), getGeneratedVersion() )
80              + ".io.xpp3";
81  
82          String marshallerName = getFileName( "Xpp3Writer" );
83  
84          JSourceWriter sourceWriter = newJSourceWriter( packageName, marshallerName );
85  
86          JClass jClass = new JClass( packageName + '.' + marshallerName );
87          initHeader( jClass );
88          suppressAllWarnings( objectModel, jClass );
89  
90          jClass.addImport( "org.codehaus.plexus.util.xml.pull.XmlSerializer" );
91          jClass.addImport( "org.codehaus.plexus.util.xml.pull.MXSerializer" );
92          jClass.addImport( "java.io.OutputStream" );
93          jClass.addImport( "java.io.Writer" );
94          jClass.addImport( "java.util.Iterator" );
95  
96          JField namespaceField = new JField( new JClass( "String" ), "NAMESPACE" );
97          namespaceField.getModifiers().setFinal( true );
98          namespaceField.getModifiers().setStatic( true );
99          namespaceField.setInitString( "null" );
100         jClass.addField( namespaceField );
101 
102         JField commentField = new JField( new JClass( "String" ), "fileComment" );
103         commentField.setInitString( "null" );
104         jClass.addField( commentField );
105 
106         // Add setComment method
107         JMethod setComment = new JMethod( "setFileComment" );
108 
109         setComment.addParameter( new JParameter( new JClass( "String" ), "fileComment" ) );
110         JSourceCode setCommentSourceCode = setComment.getSourceCode();
111         setCommentSourceCode.add( "this.fileComment = fileComment;" );
112         jClass.addMethod( setComment );
113 
114         addModelImports( jClass, null );
115 
116         String root = objectModel.getRoot( getGeneratedVersion() );
117 
118         ModelClass rootClass = objectModel.getClass( root, getGeneratedVersion() );
119 
120         String rootElement = resolveTagName( rootClass );
121 
122         // ----------------------------------------------------------------------
123         // Write the write( Writer, Model ) method which will do the unmarshalling.
124         // ----------------------------------------------------------------------
125 
126         JMethod marshall = new JMethod( "write" );
127 
128         String rootElementParameterName = uncapitalise( root );
129         marshall.addParameter( new JParameter( new JClass( "Writer" ), "writer" ) );
130         marshall.addParameter( new JParameter( new JClass( root ), rootElementParameterName ) );
131 
132         marshall.addException( new JClass( "java.io.IOException" ) );
133 
134         JSourceCode sc = marshall.getSourceCode();
135 
136         sc.add( "XmlSerializer serializer = new MXSerializer();" );
137 
138         sc.add(
139             "serializer.setProperty( \"http://xmlpull.org/v1/doc/properties.html#serializer-indentation\", \"  \" );" );
140 
141         sc.add(
142             "serializer.setProperty( \"http://xmlpull.org/v1/doc/properties.html#serializer-line-separator\", \"\\n\" );" );
143 
144         sc.add( "serializer.setOutput( writer );" );
145 
146         sc.add( "serializer.startDocument( " + rootElementParameterName + ".getModelEncoding(), null );" );
147 
148         sc.add( "write" + root + "( " + rootElementParameterName + ", \"" + rootElement + "\", serializer );" );
149 
150         sc.add( "serializer.endDocument();" );
151 
152         jClass.addMethod( marshall );
153 
154         // ----------------------------------------------------------------------
155         // Write the write( OutputStream, Model ) method which will do the unmarshalling.
156         // ----------------------------------------------------------------------
157 
158         marshall = new JMethod( "write" );
159 
160         marshall.addParameter( new JParameter( new JClass( "OutputStream" ), "stream" ) );
161         marshall.addParameter( new JParameter( new JClass( root ), rootElementParameterName ) );
162 
163         marshall.addException( new JClass( "java.io.IOException" ) );
164 
165         sc = marshall.getSourceCode();
166 
167         sc.add( "XmlSerializer serializer = new MXSerializer();" );
168 
169         sc.add(
170             "serializer.setProperty( \"http://xmlpull.org/v1/doc/properties.html#serializer-indentation\", \"  \" );" );
171 
172         sc.add(
173             "serializer.setProperty( \"http://xmlpull.org/v1/doc/properties.html#serializer-line-separator\", \"\\n\" );" );
174 
175         sc.add( "serializer.setOutput( stream, " + rootElementParameterName + ".getModelEncoding() );" );
176 
177         sc.add( "serializer.startDocument( " + rootElementParameterName + ".getModelEncoding(), null );" );
178 
179         sc.add( "write" + root + "( " + rootElementParameterName + ", \"" + rootElement + "\", serializer );" );
180 
181         sc.add( "serializer.endDocument();" );
182 
183         jClass.addMethod( marshall );
184 
185         writeAllClasses( objectModel, jClass );
186 
187         if ( requiresDomSupport )
188         {
189             createWriteDomMethod( jClass );
190         }
191 
192         jClass.print( sourceWriter );
193 
194         sourceWriter.close();
195     }
196 
197     private void writeAllClasses( Model objectModel, JClass jClass )
198         throws ModelloException
199     {
200         for ( ModelClass clazz : getClasses( objectModel ) )
201         {
202             writeClass( clazz, jClass );
203         }
204     }
205 
206     private void writeClass( ModelClass modelClass, JClass jClass )
207         throws ModelloException
208     {
209         String className = modelClass.getName();
210 
211         String uncapClassName = uncapitalise( className );
212 
213         JMethod marshall = new JMethod( "write" + className );
214 
215         marshall.addParameter( new JParameter( new JClass( className ), uncapClassName ) );
216         marshall.addParameter( new JParameter( new JClass( "String" ), "tagName" ) );
217         marshall.addParameter( new JParameter( new JClass( "XmlSerializer" ), "serializer" ) );
218 
219         marshall.addException( new JClass( "java.io.IOException" ) );
220 
221         marshall.getModifiers().makePrivate();
222 
223         JSourceCode sc = marshall.getSourceCode();
224 
225         ModelClassMetadata classMetadata = (ModelClassMetadata) modelClass.getMetadata( ModelClassMetadata.ID );
226 
227         String namespace = null;
228         XmlModelMetadata xmlModelMetadata = (XmlModelMetadata) modelClass.getModel().getMetadata( XmlModelMetadata.ID );
229 
230         // add namespace information for root element only
231         if ( classMetadata.isRootElement() && ( xmlModelMetadata.getNamespace() != null ) )
232         {
233             sc.add( "if ( this.fileComment != null )" );
234             sc.add( "{" );
235             sc.add( "serializer.comment(this.fileComment);" );
236             sc.add( "}" );
237 
238             namespace = xmlModelMetadata.getNamespace( getGeneratedVersion() );
239             sc.add( "serializer.setPrefix( \"\", \"" + namespace + "\" );" );
240         }
241 
242         if ( ( namespace != null ) && ( xmlModelMetadata.getSchemaLocation() != null ) )
243         {
244             String url = xmlModelMetadata.getSchemaLocation( getGeneratedVersion() );
245 
246             sc.add( "serializer.setPrefix( \"xsi\", \"http://www.w3.org/2001/XMLSchema-instance\" );" );
247 
248             sc.add( "serializer.startTag( NAMESPACE, tagName );" );
249 
250             sc.add( "serializer.attribute( \"\", \"xsi:schemaLocation\", \"" + namespace + " " + url + "\" );" );
251         }
252         else
253         {
254             sc.add( "serializer.startTag( NAMESPACE, tagName );" );
255         }
256 
257         ModelField contentField = null;
258 
259         String contentValue = null;
260 
261         List<ModelField> modelFields = getFieldsForXml( modelClass, getGeneratedVersion() );
262 
263         // XML attributes
264         for ( ModelField field : modelFields )
265         {
266             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID );
267 
268             JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata( JavaFieldMetadata.ID );
269 
270             String fieldTagName = resolveTagName( field, xmlFieldMetadata );
271 
272             String type = field.getType();
273 
274             String value = uncapClassName + "." + getPrefix( javaFieldMetadata ) + capitalise( field.getName() ) + "()";
275 
276             if ( xmlFieldMetadata.isContent() )
277             {
278                 contentField = field;
279                 contentValue = value;
280                 continue;
281             }
282 
283             if ( xmlFieldMetadata.isAttribute() )
284             {
285                 sc.add( getValueChecker( type, value, field ) );
286 
287                 sc.add( "{" );
288                 sc.addIndented( "serializer.attribute( NAMESPACE, \"" + fieldTagName + "\", "
289                                 + getValue( field.getType(), value, xmlFieldMetadata ) + " );" );
290                 sc.add( "}" );
291             }
292 
293         }
294 
295         if ( contentField != null )
296         {
297             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) contentField.getMetadata( XmlFieldMetadata.ID );
298             sc.add( "serializer.text( " + getValue( contentField.getType(), contentValue, xmlFieldMetadata ) + " );" );
299         }
300 
301         // XML tags
302         for ( ModelField field : modelFields )
303         {
304             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID );
305 
306             if ( xmlFieldMetadata.isContent() )
307             {
308                 // skip field with type Content
309                 continue;
310             }
311 
312             JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata( JavaFieldMetadata.ID );
313 
314             String fieldTagName = resolveTagName( field, xmlFieldMetadata );
315 
316             String type = field.getType();
317 
318             String value = uncapClassName + "." + getPrefix( javaFieldMetadata ) + capitalise( field.getName() ) + "()";
319 
320             if ( xmlFieldMetadata.isAttribute() )
321             {
322                 continue;
323             }
324 
325             if ( field instanceof ModelAssociation )
326             {
327                 ModelAssociation association = (ModelAssociation) field;
328 
329                 String associationName = association.getName();
330 
331                 if ( association.isOneMultiplicity() )
332                 {
333                     sc.add( getValueChecker( type, value, association ) );
334 
335                     sc.add( "{" );
336                     sc.addIndented( "write" + association.getTo() + "( (" + association.getTo() + ") " + value + ", \""
337                                     + fieldTagName + "\", serializer );" );
338                     sc.add( "}" );
339                 }
340                 else
341                 {
342                     //MANY_MULTIPLICITY
343 
344                     XmlAssociationMetadata xmlAssociationMetadata =
345                         (XmlAssociationMetadata) association.getAssociationMetadata( XmlAssociationMetadata.ID );
346 
347                     String valuesTagName = resolveTagName( fieldTagName, xmlAssociationMetadata );
348 
349                     type = association.getType();
350                     String toType = association.getTo();
351 
352                     boolean wrappedItems = xmlAssociationMetadata.isWrappedItems();
353 
354                     if ( ModelDefault.LIST.equals( type ) || ModelDefault.SET.equals( type ) )
355                     {
356                         sc.add( getValueChecker( type, value, association ) );
357 
358                         sc.add( "{" );
359                         sc.indent();
360 
361                         if ( wrappedItems )
362                         {
363                             sc.add( "serializer.startTag( NAMESPACE, " + "\"" + fieldTagName + "\" );" );
364                         }
365 
366                         sc.add( "for ( Iterator iter = " + value + ".iterator(); iter.hasNext(); )" );
367 
368                         sc.add( "{" );
369                         sc.indent();
370 
371                         if ( isClassInModel( association.getTo(), modelClass.getModel() ) )
372                         {
373                             sc.add( toType + " o = (" + toType + ") iter.next();" );
374 
375                             sc.add( "write" + toType + "( o, \"" + valuesTagName + "\", serializer );" );
376                         }
377                         else
378                         {
379                             sc.add( toType + " " + singular( uncapitalise( field.getName() ) ) + " = (" + toType
380                                 + ") iter.next();" );
381 
382                             sc.add( "serializer.startTag( NAMESPACE, " + "\"" + valuesTagName + "\" ).text( "
383                                 + singular( uncapitalise( field.getName() ) ) + " ).endTag( NAMESPACE, " + "\""
384                                 + valuesTagName + "\" );" );
385                         }
386 
387                         sc.unindent();
388                         sc.add( "}" );
389 
390                         if ( wrappedItems )
391                         {
392                             sc.add( "serializer.endTag( NAMESPACE, " + "\"" + fieldTagName + "\" );" );
393                         }
394 
395                         sc.unindent();
396                         sc.add( "}" );
397                     }
398                     else
399                     {
400                         //Map or Properties
401 
402                         sc.add( getValueChecker( type, value, field ) );
403 
404                         sc.add( "{" );
405                         sc.indent();
406 
407                         if ( wrappedItems )
408                         {
409                             sc.add( "serializer.startTag( NAMESPACE, " + "\"" + fieldTagName + "\" );" );
410                         }
411 
412                         sc.add( "for ( Iterator iter = " + value + ".keySet().iterator(); iter.hasNext(); )" );
413 
414                         sc.add( "{" );
415                         sc.indent();
416 
417                         sc.add( "String key = (String) iter.next();" );
418 
419                         sc.add( "String value = (String) " + value + ".get( key );" );
420 
421                         if ( xmlAssociationMetadata.isMapExplode() )
422                         {
423                             sc.add( "serializer.startTag( NAMESPACE, \"" + singular( associationName ) + "\" );" );
424                             sc.add(
425                                 "serializer.startTag( NAMESPACE, \"key\" ).text( key ).endTag( NAMESPACE, \"key\" );" );
426                             sc.add(
427                                 "serializer.startTag( NAMESPACE, \"value\" ).text( value ).endTag( NAMESPACE, \"value\" );" );
428                             sc.add( "serializer.endTag( NAMESPACE, \"" + singular( associationName ) + "\" );" );
429                         }
430                         else
431                         {
432                             sc.add(
433                                 "serializer.startTag( NAMESPACE, \"\" + key + \"\" ).text( value ).endTag( NAMESPACE, \"\" + key + \"\" );" );
434                         }
435 
436                         sc.unindent();
437                         sc.add( "}" );
438 
439                         if ( wrappedItems )
440                         {
441                             sc.add( "serializer.endTag( NAMESPACE, " + "\"" + fieldTagName + "\" );" );
442                         }
443 
444                         sc.unindent();
445                         sc.add( "}" );
446                     }
447                 }
448             }
449             else
450             {
451                 sc.add( getValueChecker( type, value, field ) );
452 
453                 sc.add( "{" );
454                 if ( "DOM".equals( field.getType() ) )
455                 {
456                     if ( domAsXpp3 )
457                     {
458                         jClass.addImport( "org.codehaus.plexus.util.xml.Xpp3Dom" );
459     
460                         sc.addIndented( "((Xpp3Dom) " + value + ").writeToSerializer( NAMESPACE, serializer );" );
461                     }
462                     else
463                     {
464                         sc.addIndented( "writeDom( (org.w3c.dom.Element) " + value + ", serializer );" );
465                     }
466 
467                     requiresDomSupport = true;
468                 }
469                 else
470                 {
471                     sc.addIndented( "serializer.startTag( NAMESPACE, " + "\"" + fieldTagName + "\" ).text( "
472                         + getValue( field.getType(), value, xmlFieldMetadata ) + " ).endTag( NAMESPACE, " + "\""
473                         + fieldTagName + "\" );" );
474                 }
475                 sc.add( "}" );
476             }
477         }
478 
479         sc.add( "serializer.endTag( NAMESPACE, tagName );" );
480 
481         jClass.addMethod( marshall );
482     }
483 
484     private void createWriteDomMethod( JClass jClass )
485     {
486         if ( domAsXpp3 )
487         {
488             return;
489         }
490         String type = "org.w3c.dom.Element";
491         JMethod method = new JMethod( "writeDom" );
492         method.getModifiers().makePrivate();
493 
494         method.addParameter( new JParameter( new JType( type ), "dom" ) );
495         method.addParameter( new JParameter( new JClass( "XmlSerializer" ), "serializer" ) );
496 
497         method.addException( new JClass( "java.io.IOException" ) );
498 
499         JSourceCode sc = method.getSourceCode();
500 
501         // start element
502         sc.add( "serializer.startTag( NAMESPACE, dom.getTagName() );" );
503 
504         // attributes
505         sc.add( "org.w3c.dom.NamedNodeMap attributes = dom.getAttributes();" );
506         sc.add( "for ( int i = 0; i < attributes.getLength(); i++ )" );
507         sc.add( "{" );
508 
509         sc.indent();
510         sc.add( "org.w3c.dom.Node attribute = attributes.item( i );" );
511         sc.add( "serializer.attribute( NAMESPACE, attribute.getNodeName(), attribute.getNodeValue() );" );
512         sc.unindent();
513 
514         sc.add( "}" );
515 
516         // child nodes & text
517         sc.add( "org.w3c.dom.NodeList children = dom.getChildNodes();" );
518         sc.add( "for ( int i = 0; i < children.getLength(); i++ )" );
519         sc.add( "{" );
520         sc.indent();
521         sc.add( "org.w3c.dom.Node node = children.item( i );" );
522         sc.add( "if ( node instanceof org.w3c.dom.Element)" );
523         sc.add( "{" );
524         sc.addIndented( "writeDom( (org.w3c.dom.Element) children.item( i ), serializer );" );
525         sc.add( "}" );
526         sc.add( "else" );
527         sc.add( "{" );
528         sc.addIndented( "serializer.text( node.getTextContent() );" );
529         sc.add( "}" );
530         sc.unindent();
531         sc.add( "}" );
532 
533         sc.add( "serializer.endTag( NAMESPACE, dom.getTagName() );" );
534 
535         jClass.addMethod( method );
536     }
537 }