View Javadoc

1   package org.codehaus.modello.plugin.snakeyaml;
2   
3   /*
4    * Copyright (c) 2004-2013, 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.JParameter;
40  import org.codehaus.modello.plugin.java.javasource.JSourceCode;
41  import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
42  import org.codehaus.modello.plugin.java.metadata.JavaFieldMetadata;
43  import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata;
44  import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata;
45  
46  /**
47   * @author <a href="mailto:simonetripodi@apache.org">Simone Tripodi</a>
48   */
49  public class SnakeYamlWriterGenerator
50      extends AbstractSnakeYamlGenerator
51  {
52  
53      public void generate( Model model, Properties parameters )
54          throws ModelloException
55      {
56          initialize( model, parameters );
57  
58          try
59          {
60              generateSnakeYamlWriter();
61          }
62          catch ( IOException ex )
63          {
64              throw new ModelloException( "Exception while generating SnakeYaml Writer.", ex );
65          }
66      }
67  
68      private void generateSnakeYamlWriter()
69          throws ModelloException, IOException
70      {
71          Model objectModel = getModel();
72  
73          String packageName = objectModel.getDefaultPackageName( isPackageWithVersion(), getGeneratedVersion() )
74              + ".io.snakeyaml";
75  
76          String marshallerName = getFileName( "SnakeYamlWriter" );
77  
78          JSourceWriter sourceWriter = newJSourceWriter( packageName, marshallerName );
79  
80          JClass jClass = new JClass( packageName + '.' + marshallerName );
81          initHeader( jClass );
82  
83          jClass.addImport( "org.yaml.snakeyaml.DumperOptions" );
84          jClass.addImport( "org.yaml.snakeyaml.DumperOptions.Version" );
85          jClass.addImport( "org.yaml.snakeyaml.emitter.Emitable" );
86          jClass.addImport( "org.yaml.snakeyaml.emitter.Emitter" );
87          jClass.addImport( "org.yaml.snakeyaml.events.DocumentEndEvent" );
88          jClass.addImport( "org.yaml.snakeyaml.events.DocumentStartEvent" );
89          jClass.addImport( "org.yaml.snakeyaml.events.ImplicitTuple" );
90          jClass.addImport( "org.yaml.snakeyaml.events.MappingEndEvent" );
91          jClass.addImport( "org.yaml.snakeyaml.events.MappingStartEvent" );
92          jClass.addImport( "org.yaml.snakeyaml.events.ScalarEvent" );
93          jClass.addImport( "org.yaml.snakeyaml.events.SequenceEndEvent" );
94          jClass.addImport( "org.yaml.snakeyaml.events.SequenceStartEvent" );
95          jClass.addImport( "org.yaml.snakeyaml.events.StreamEndEvent" );
96          jClass.addImport( "org.yaml.snakeyaml.events.StreamStartEvent" );
97          jClass.addImport( "java.io.IOException" );
98          jClass.addImport( "java.io.OutputStream" );
99          jClass.addImport( "java.io.OutputStreamWriter" );
100         jClass.addImport( "java.io.Writer" );
101 
102         addModelImports( jClass, null );
103 
104         JField factoryField = new JField( new JClass( "DumperOptions" ), "dumperOptions" );
105         factoryField.getModifiers().setFinal( true );
106         factoryField.setInitString( "new DumperOptions()" );
107         jClass.addField( factoryField );
108 
109         JConstructor jacksonWriterConstructor = new JConstructor( jClass );
110         JSourceCode sc = jacksonWriterConstructor.getSourceCode();
111         sc.add( "dumperOptions.setAllowUnicode( true );" );
112         sc.add( "dumperOptions.setPrettyFlow( true );" );
113         sc.add( "dumperOptions.setVersion( Version.V1_1 );" );
114 
115         jClass.addConstructor( jacksonWriterConstructor );
116 
117         String root = objectModel.getRoot( getGeneratedVersion() );
118 
119         // ----------------------------------------------------------------------
120         // Write the write( Writer, Model ) method which will do the unmarshalling.
121         // ----------------------------------------------------------------------
122 
123         JMethod marshall = new JMethod( "write" );
124 
125         String rootElementParameterName = uncapitalise( root );
126         marshall.addParameter( new JParameter( new JClass( "Writer" ), "writer" ) );
127         marshall.addParameter( new JParameter( new JClass( root ), rootElementParameterName ) );
128 
129         marshall.addException( new JClass( "IOException" ) );
130 
131         sc = marshall.getSourceCode();
132 
133         sc.add( "Emitable generator = new Emitter( writer, dumperOptions );" );
134 
135         sc.add( "generator.emit( new StreamStartEvent( null, null ) );" );
136 
137         sc.add( "generator.emit( new DocumentStartEvent( null, null, dumperOptions.isExplicitStart(), dumperOptions.getVersion(), dumperOptions.getTags() ) );" );
138 
139         sc.add( "write" + root + "( " + rootElementParameterName + ", generator );" );
140 
141         sc.add( "generator.emit( new DocumentEndEvent( null, null, dumperOptions.isExplicitEnd() ) );" );
142 
143         sc.add( "generator.emit( new StreamEndEvent( null, null ) );" );
144 
145         jClass.addMethod( marshall );
146 
147         // ----------------------------------------------------------------------
148         // Write the write( OutputStream, Model ) method which will do the unmarshalling.
149         // ----------------------------------------------------------------------
150 
151         marshall = new JMethod( "write" );
152 
153         marshall.addParameter( new JParameter( new JClass( "OutputStream" ), "stream" ) );
154         marshall.addParameter( new JParameter( new JClass( root ), rootElementParameterName ) );
155 
156         marshall.addException( new JClass( "IOException" ) );
157 
158         sc = marshall.getSourceCode();
159 
160         sc.add( "write( new OutputStreamWriter( stream, "
161                         + rootElementParameterName
162                         + ".getModelEncoding() ), "
163                         + rootElementParameterName
164                         + " );" );
165 
166         jClass.addMethod( marshall );
167 
168         writeAllClasses( objectModel, jClass );
169 
170         jClass.print( sourceWriter );
171 
172         sourceWriter.close();
173     }
174 
175     private void writeAllClasses( Model objectModel, JClass jClass )
176         throws ModelloException
177     {
178         for ( ModelClass clazz : getClasses( objectModel ) )
179         {
180             writeClass( clazz, jClass );
181         }
182     }
183 
184     private void writeClass( ModelClass modelClass, JClass jClass )
185         throws ModelloException
186     {
187         String className = modelClass.getName();
188 
189         String uncapClassName = uncapitalise( className );
190 
191         JMethod marshall = new JMethod( "write" + className );
192 
193         marshall.addParameter( new JParameter( new JClass( className ), uncapClassName ) );
194         marshall.addParameter( new JParameter( new JClass( "Emitable" ), "generator" ) );
195 
196         marshall.addException( new JClass( "IOException" ) );
197 
198         marshall.getModifiers().makePrivate();
199 
200         JSourceCode sc = marshall.getSourceCode();
201 
202         sc.add( "generator.emit( new MappingStartEvent( null, null, true, null, null, false ) );" );
203 
204         ModelField contentField = null;
205 
206         String contentValue = null;
207 
208         List<ModelField> modelFields = getFieldsForXml( modelClass, getGeneratedVersion() );
209 
210         // XML attributes
211         for ( ModelField field : modelFields )
212         {
213             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID );
214 
215             JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata( JavaFieldMetadata.ID );
216 
217             String fieldTagName = resolveTagName( field, xmlFieldMetadata );
218 
219             String type = field.getType();
220 
221             String value = uncapClassName + "." + getPrefix( javaFieldMetadata ) + capitalise( field.getName() ) + "()";
222 
223             if ( xmlFieldMetadata.isContent() )
224             {
225                 contentField = field;
226                 contentValue = value;
227                 continue;
228             }
229 
230             if ( xmlFieldMetadata.isAttribute() )
231             {
232                 sc.add( getValueChecker( type, value, field ) );
233 
234                 sc.add( "{" );
235                 sc.indent();
236 
237                 writeScalarKey( sc, fieldTagName );
238                 writeScalar( sc, getValue( field.getType(), value, xmlFieldMetadata ) );
239 
240                 sc.unindent();
241                 sc.add( "}" );
242             }
243 
244         }
245 
246         if ( contentField != null )
247         {
248             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) contentField.getMetadata( XmlFieldMetadata.ID );
249             writeScalar( sc, getValue( contentField.getType(), contentValue, xmlFieldMetadata ) );
250         }
251 
252         // XML tags
253         for ( ModelField field : modelFields )
254         {
255             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata( XmlFieldMetadata.ID );
256 
257             if ( xmlFieldMetadata.isContent() )
258             {
259                 // skip field with type Content
260                 continue;
261             }
262 
263             JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata( JavaFieldMetadata.ID );
264 
265             String fieldTagName = resolveTagName( field, xmlFieldMetadata );
266 
267             String type = field.getType();
268 
269             String value = uncapClassName + "." + getPrefix( javaFieldMetadata ) + capitalise( field.getName() ) + "()";
270 
271             if ( xmlFieldMetadata.isAttribute() )
272             {
273                 continue;
274             }
275 
276             if ( field instanceof ModelAssociation )
277             {
278                 ModelAssociation association = (ModelAssociation) field;
279 
280                 if ( association.isOneMultiplicity() )
281                 {
282                     sc.add( getValueChecker( type, value, association ) );
283 
284                     sc.add( "{" );
285                     sc.indent();
286 
287                     writeScalarKey( sc, fieldTagName );
288                     sc.add( "write" + association.getTo() + "( (" + association.getTo() + ") " + value + ", generator );" );
289 
290                     sc.unindent();
291                     sc.add( "}" );
292                 }
293                 else
294                 {
295                     //MANY_MULTIPLICITY
296 
297                     XmlAssociationMetadata xmlAssociationMetadata =
298                         (XmlAssociationMetadata) association.getAssociationMetadata( XmlAssociationMetadata.ID );
299 
300                     type = association.getType();
301                     String toType = association.getTo();
302 
303                     if ( ModelDefault.LIST.equals( type ) || ModelDefault.SET.equals( type ) )
304                     {
305                         sc.add( getValueChecker( type, value, association ) );
306 
307                         sc.add( "{" );
308                         sc.indent();
309 
310                         writeScalarKey( sc, fieldTagName );
311                         sc.add( "generator.emit( new SequenceStartEvent( null, null, true, null, null, false ) );" );
312 
313                         if ( useJava5 )
314                         {
315                             sc.add( "for ( " + toType + " o : " + value + " )" );
316                         }
317                         else
318                         {
319                             sc.add( "for ( java.util.Iterator it = " + value + ".iterator(); it.hasNext(); )" );
320                         }
321 
322                         sc.add( "{" );
323                         sc.indent();
324 
325                         if ( !useJava5 )
326                         {
327                             sc.add( toType + " o = (" + toType + " ) it.next();" );
328                         }
329 
330                         if ( isClassInModel( association.getTo(), modelClass.getModel() ) )
331                         {
332                             sc.add( "write" + toType + "( o, generator );" );
333                         }
334                         else
335                         {
336                             writeScalar( sc, "o" );
337                         }
338 
339                         sc.unindent();
340                         sc.add( "}" );
341 
342                         sc.add( "generator.emit( new SequenceEndEvent( null, null ) );" );
343 
344                         sc.unindent();
345                         sc.add( "}" );
346                     }
347                     else
348                     {
349                         //Map or Properties
350 
351                         sc.add( getValueChecker( type, value, field ) );
352 
353                         sc.add( "{" );
354                         sc.indent();
355 
356                         writeScalarKey( sc, fieldTagName );
357 
358                         if ( xmlAssociationMetadata.isMapExplode() )
359                         {
360                             sc.add( "generator.emit( new SequenceStartEvent( null, null, true, null, null, false ) );" );
361                         }
362                         else
363                         {
364                             sc.add( "generator.emit( new MappingStartEvent( null, null, true, null, null, false ) );" );
365                         }
366 
367 
368 
369                         StringBuilder entryTypeBuilder = new StringBuilder( "java.util.Map.Entry" );
370 
371                         if ( useJava5 )
372                         {
373                             entryTypeBuilder.append( '<' );
374 
375                             if ( association.getType().equals( ModelDefault.PROPERTIES ) )
376                             {
377                                 entryTypeBuilder.append( "Object, Object" );
378                             }
379                             else
380                             {
381                                 entryTypeBuilder.append( "String, " ).append( association.getTo() );
382                             }
383 
384                             entryTypeBuilder.append( '>' );
385                         }
386 
387                         if ( useJava5 )
388                         {
389                             sc.add( "for ( " + entryTypeBuilder + " entry : " + value + ".entrySet() )" );
390                         }
391                         else
392                         {
393                             sc.add( "for ( java.util.Iterator it = " + value + ".entrySet().iterator(); it.hasNext(); )" );
394                         }
395 
396                         sc.add( "{" );
397                         sc.indent();
398 
399                         if ( !useJava5 )
400                         {
401                             sc.add( entryTypeBuilder + " entry = (" + entryTypeBuilder + ") it.next();" );
402                         }
403 
404                         sc.add( "final String key = String.valueOf( entry.getKey() );" );
405                         sc.add( "final String value = String.valueOf( entry.getValue() );" );
406 
407                         if ( xmlAssociationMetadata.isMapExplode() )
408                         {
409                             sc.add( "generator.emit( new MappingStartEvent( null, null, true, null, null, false ) );" );
410                             writeScalarKey( sc, "key" );
411                             writeScalar( sc, "key" );
412                             writeScalarKey( sc, "value" );
413                             writeScalar( sc, "value" );
414                             sc.add( "generator.emit( new MappingEndEvent( null, null ) );" );
415                         }
416                         else
417                         {
418                             writeScalar( sc, "key" );
419                             writeScalar( sc, "value" );
420                         }
421 
422                         sc.unindent();
423                         sc.add( "}" );
424 
425                         if ( xmlAssociationMetadata.isMapExplode() )
426                         {
427                             sc.add( "generator.emit( new SequenceEndEvent( null, null ) );" );
428                         }
429                         else
430                         {
431                             sc.add( "generator.emit( new MappingEndEvent( null, null ) );" );
432                         }
433 
434                         sc.unindent();
435                         sc.add( "}" );
436                     }
437                 }
438             }
439             else
440             {
441                 sc.add( getValueChecker( type, value, field ) );
442 
443                 sc.add( "{" );
444                 sc.indent();
445 
446                 writeScalarKey( sc, fieldTagName );
447                 writeScalar( sc, getValue( field.getType(), value, xmlFieldMetadata ) );
448 
449                 sc.unindent();
450                 sc.add( "}" );
451             }
452         }
453 
454         sc.add( "generator.emit( new MappingEndEvent( null, null ) );" );
455 
456         jClass.addMethod( marshall );
457     }
458 
459     private void writeScalarKey( JSourceCode sc, String key )
460     {
461         writeScalar( sc, "\"" + key + "\"" );
462     }
463 
464     private void writeScalar( JSourceCode sc, String value )
465     {
466         sc.add( "generator.emit( new ScalarEvent( null, null, new ImplicitTuple( true, true ), "
467                 + value
468                 + ", null, null, ' ' ) );" );
469     }
470 
471 }