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