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