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