View Javadoc
1   package org.codehaus.modello.plugin.dom4j;
2   
3   /*
4    * Copyright (c) 2006, Codehaus.
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.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.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.plugin.model.ModelClassMetadata;
44  import org.codehaus.modello.plugins.xml.AbstractXmlJavaGenerator;
45  import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata;
46  import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata;
47  import org.codehaus.modello.plugins.xml.metadata.XmlModelMetadata;
48  
49  /**
50   * <p>
51   * Generate a writer that uses Dom4J.
52   * </p>
53   * TODO: chunks are lifted from xpp3, including the tests. Can we abstract it in some way?
54   *
55   * @author <a href="mailto:brett@codehaus.org">Brett Porter</a>
56   */
57  @Named("dom4j-writer")
58  public class Dom4jWriterGenerator extends AbstractXmlJavaGenerator {
59  
60      private boolean requiresDomSupport;
61  
62      @Override
63      public void generate(Model model, Map<String, Object> parameters) throws ModelloException {
64          initialize(model, parameters);
65  
66          requiresDomSupport = false;
67  
68          try {
69              generateDom4jWriter();
70          } catch (IOException ex) {
71              throw new ModelloException("Exception while generating Dom4j Writer.", ex);
72          }
73      }
74  
75      private void generateDom4jWriter() throws ModelloException, IOException {
76          Model objectModel = getModel();
77  
78          String packageName =
79                  objectModel.getDefaultPackageName(isPackageWithVersion(), getGeneratedVersion()) + ".io.dom4j";
80  
81          String marshallerName = getFileName("Dom4jWriter");
82  
83          JSourceWriter sourceWriter = newJSourceWriter(packageName, marshallerName);
84  
85          JClass jClass = new JClass(packageName + '.' + marshallerName);
86          initHeader(jClass);
87          suppressAllWarnings(objectModel, jClass);
88  
89          jClass.addImport("java.io.OutputStream");
90          jClass.addImport("java.io.Writer");
91          jClass.addImport("java.util.Arrays");
92          jClass.addImport("java.util.Iterator");
93          jClass.addImport("java.util.Locale");
94          jClass.addImport("java.text.DateFormat");
95          jClass.addImport("org.dom4j.Document");
96          jClass.addImport("org.dom4j.DocumentException");
97          jClass.addImport("org.dom4j.DocumentFactory");
98          jClass.addImport("org.dom4j.Element");
99          jClass.addImport("org.dom4j.io.OutputFormat");
100         jClass.addImport("org.dom4j.io.XMLWriter");
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         String variableName = uncapitalise(root);
110 
111         // ----------------------------------------------------------------------
112         // Write the write( Reader, Model ) method which will do the unmarshalling.
113         // ----------------------------------------------------------------------
114 
115         JMethod marshall = new JMethod("write");
116 
117         marshall.addParameter(new JParameter(new JClass("Writer"), "writer"));
118         marshall.addParameter(new JParameter(new JClass(root), variableName));
119 
120         marshall.addException(new JClass("java.io.IOException"));
121 
122         JSourceCode sc = marshall.getSourceCode();
123 
124         sc.add("Document document = new DocumentFactory().createDocument();");
125 
126         sc.add("write" + root + "( " + variableName + ", \"" + rootElement + "\", document );");
127 
128         // TODO: pretty printing optional
129         sc.add("OutputFormat format = OutputFormat.createPrettyPrint();");
130         sc.add("format.setLineSeparator( System.getProperty( \"line.separator\" ) );");
131         sc.add("XMLWriter serializer = new XMLWriter( writer, format );");
132 
133         sc.add("serializer.write( document );");
134 
135         jClass.addMethod(marshall);
136 
137         // ----------------------------------------------------------------------
138         // Write the write( OutputStream, Model ) method which will do the unmarshalling.
139         // ----------------------------------------------------------------------
140 
141         marshall = new JMethod("write");
142 
143         marshall.addParameter(new JParameter(new JClass("OutputStream"), "stream"));
144         marshall.addParameter(new JParameter(new JClass(root), variableName));
145 
146         marshall.addException(new JClass("java.io.IOException"));
147 
148         sc = marshall.getSourceCode();
149 
150         sc.add("Document document = new DocumentFactory().createDocument();");
151 
152         sc.add("write" + root + "( " + variableName + ", \"" + rootElement + "\", document );");
153 
154         // TODO: pretty printing optional
155         sc.add("OutputFormat format = OutputFormat.createPrettyPrint();");
156         sc.add("format.setLineSeparator( System.getProperty( \"line.separator\" ) );");
157         sc.add("format.setEncoding( " + variableName + ".getModelEncoding() );");
158         sc.add("XMLWriter serializer = new XMLWriter( stream, format );");
159 
160         sc.add("serializer.write( document );");
161 
162         jClass.addMethod(marshall);
163 
164         writeAllClasses(objectModel, jClass);
165 
166         if (requiresDomSupport) {
167             jClass.addImport("org.codehaus.plexus.util.xml.Xpp3Dom");
168             writeDomHelpers(jClass);
169         }
170 
171         jClass.print(sourceWriter);
172 
173         sourceWriter.close();
174     }
175 
176     private void writeAllClasses(Model objectModel, JClass jClass) throws ModelloException {
177         for (ModelClass clazz : getClasses(objectModel)) {
178             writeClass(clazz, jClass);
179         }
180     }
181 
182     private void writeClass(ModelClass modelClass, JClass jClass) throws ModelloException {
183         String className = modelClass.getName();
184 
185         String uncapClassName = uncapitalise(className);
186 
187         JMethod marshall = new JMethod("write" + className);
188         marshall.getModifiers().makePrivate();
189 
190         marshall.addParameter(new JParameter(new JClass(className), uncapClassName));
191         marshall.addParameter(new JParameter(new JClass("String"), "tagName"));
192 
193         ModelClassMetadata classMetadata = (ModelClassMetadata) modelClass.getMetadata(ModelClassMetadata.ID);
194 
195         marshall.addParameter(new JParameter(new JClass("org.dom4j.Branch"), "parentElement"));
196 
197         marshall.addException(new JClass("java.io.IOException"));
198 
199         JSourceCode sc = marshall.getSourceCode();
200 
201         sc.add("if ( " + uncapClassName + " != null )");
202 
203         sc.add("{");
204         sc.indent();
205 
206         XmlModelMetadata xmlModelMetadata =
207                 (XmlModelMetadata) modelClass.getModel().getMetadata(XmlModelMetadata.ID);
208 
209         // add namespace information for root element only
210         if (classMetadata.isRootElement() && (xmlModelMetadata.getNamespace() != null)) {
211             String namespace = xmlModelMetadata.getNamespace(getGeneratedVersion());
212             sc.add("Element element = parentElement.addElement( tagName, \"" + namespace + "\" );");
213 
214             if (xmlModelMetadata.getSchemaLocation() != null) {
215                 String url = xmlModelMetadata.getSchemaLocation(getGeneratedVersion());
216 
217                 sc.add("element.addAttribute( \"xmlns:xsi\", \"http://www.w3.org/2001/XMLSchema-instance\" );");
218                 sc.add("element.addAttribute( \"xsi:schemaLocation\", \"" + namespace + " " + url + "\" );");
219             }
220         } else {
221             sc.add("Element element = parentElement.addElement( tagName );");
222         }
223 
224         ModelField contentField = null;
225 
226         String contentValue = null;
227 
228         List<ModelField> modelFields = getFieldsForXml(modelClass, getGeneratedVersion());
229 
230         // XML attributes
231         for (ModelField field : modelFields) {
232             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
233 
234             JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata(JavaFieldMetadata.ID);
235 
236             String fieldTagName = resolveTagName(field, xmlFieldMetadata);
237 
238             String type = field.getType();
239 
240             String value = uncapClassName + "." + getPrefix(javaFieldMetadata) + capitalise(field.getName()) + "()";
241 
242             if (xmlFieldMetadata.isContent()) {
243                 contentField = field;
244                 contentValue = value;
245                 continue;
246             }
247 
248             if (xmlFieldMetadata.isAttribute()) {
249                 sc.add(getValueChecker(type, value, field));
250 
251                 sc.add("{");
252                 sc.addIndented("element.addAttribute( \"" + fieldTagName + "\", "
253                         + getValue(field.getType(), value, xmlFieldMetadata) + " );");
254                 sc.add("}");
255             }
256         }
257 
258         if (contentField != null) {
259             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) contentField.getMetadata(XmlFieldMetadata.ID);
260             sc.add("element.setText( " + getValue(contentField.getType(), contentValue, xmlFieldMetadata) + " );");
261         }
262 
263         // XML tags
264         for (ModelField field : modelFields) {
265             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
266 
267             if (xmlFieldMetadata.isContent()) {
268                 // skip field with type Content
269                 continue;
270             }
271 
272             if (!xmlFieldMetadata.isAttribute()) {
273                 processField(field, xmlFieldMetadata, uncapClassName, sc, modelClass, jClass);
274             }
275         }
276 
277         sc.unindent();
278         sc.add("}");
279 
280         jClass.addMethod(marshall);
281     }
282 
283     private void processField(
284             ModelField field,
285             XmlFieldMetadata xmlFieldMetadata,
286             String uncapClassName,
287             JSourceCode sc,
288             ModelClass modelClass,
289             JClass jClass)
290             throws ModelloException {
291         JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata(JavaFieldMetadata.ID);
292 
293         String fieldTagName = resolveTagName(field, xmlFieldMetadata);
294 
295         String type = field.getType();
296 
297         String value = uncapClassName + "." + getPrefix(javaFieldMetadata) + capitalise(field.getName()) + "()";
298 
299         if (field instanceof ModelAssociation) {
300             ModelAssociation association = (ModelAssociation) field;
301 
302             String associationName = association.getName();
303 
304             if (association.isOneMultiplicity()) {
305                 sc.add(getValueChecker(type, value, association));
306 
307                 sc.add("{");
308                 sc.addIndented("write" + association.getTo() + "( " + value + ", \"" + fieldTagName + "\", element );");
309                 sc.add("}");
310             } else {
311                 // MANY_MULTIPLICITY
312 
313                 XmlAssociationMetadata xmlAssociationMetadata =
314                         (XmlAssociationMetadata) association.getAssociationMetadata(XmlAssociationMetadata.ID);
315 
316                 String valuesTagName = resolveTagName(fieldTagName, xmlAssociationMetadata);
317 
318                 type = association.getType();
319                 String toType = association.getTo();
320 
321                 boolean wrappedItems = xmlAssociationMetadata.isWrappedItems();
322 
323                 if (ModelDefault.LIST.equals(type) || ModelDefault.SET.equals(type)) {
324                     sc.add(getValueChecker(type, value, association));
325 
326                     sc.add("{");
327                     sc.indent();
328 
329                     sc.add("Element listElement = element;");
330 
331                     if (wrappedItems) {
332                         sc.add("listElement = element.addElement( \"" + fieldTagName + "\" );");
333                     }
334 
335                     sc.add("for ( Iterator iter = " + value + ".iterator(); iter.hasNext(); )");
336 
337                     sc.add("{");
338                     sc.indent();
339 
340                     if (isClassInModel(association.getTo(), modelClass.getModel())) {
341                         sc.add(toType + " o = (" + toType + ") iter.next();");
342 
343                         sc.add("write" + toType + "( o, \"" + valuesTagName + "\", listElement );");
344                     } else {
345                         sc.add(toType + " " + singular(uncapitalise(field.getName())) + " = (" + toType
346                                 + ") iter.next();");
347 
348                         sc.add("listElement.addElement( \"" + valuesTagName + "\" ).setText( "
349                                 + singular(uncapitalise(field.getName())) + " );");
350                     }
351 
352                     sc.unindent();
353                     sc.add("}");
354 
355                     sc.unindent();
356                     sc.add("}");
357                 } else {
358                     // Map or Properties
359 
360                     sc.add(getValueChecker(type, value, field));
361 
362                     sc.add("{");
363                     sc.indent();
364 
365                     sc.add("Element listElement = element;");
366 
367                     if (wrappedItems) {
368                         sc.add("listElement = element.addElement( \"" + fieldTagName + "\" );");
369                     }
370 
371                     sc.add("for ( Iterator iter = " + value + ".keySet().iterator(); iter.hasNext(); )");
372 
373                     sc.add("{");
374                     sc.indent();
375 
376                     sc.add("String key = (String) iter.next();");
377 
378                     sc.add("String value = (String) " + value + ".get( key );");
379 
380                     if (xmlAssociationMetadata.isMapExplode()) {
381                         sc.add("Element assocElement = listElement.addElement( \"" + singular(associationName)
382                                 + "\" );");
383                         sc.add("assocElement.addElement( \"key\" ).setText( key );");
384                         sc.add("assocElement.addElement( \"value\" ).setText( value );");
385                     } else {
386                         sc.add("listElement.addElement( key ).setText( value );");
387                     }
388 
389                     sc.unindent();
390                     sc.add("}");
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             if ("DOM".equals(field.getType())) {
403                 sc.add("writeXpp3DomToElement( (Xpp3Dom) " + value + ", element );");
404 
405                 requiresDomSupport = true;
406             } else {
407                 sc.add("element.addElement( \"" + fieldTagName + "\" ).setText( "
408                         + getValue(field.getType(), value, xmlFieldMetadata) + " );");
409             }
410 
411             sc.unindent();
412             sc.add("}");
413         }
414     }
415 
416     private void writeDomHelpers(JClass jClass) {
417         JMethod method = new JMethod("writeXpp3DomToElement");
418         method.getModifiers().makePrivate();
419 
420         method.addParameter(new JParameter(new JClass("Xpp3Dom"), "xpp3Dom"));
421         method.addParameter(new JParameter(new JClass("Element"), "parentElement"));
422 
423         JSourceCode sc = method.getSourceCode();
424 
425         sc.add("Element element = parentElement.addElement( xpp3Dom.getName() );");
426 
427         sc.add("if ( xpp3Dom.getValue() != null )");
428         sc.add("{");
429         sc.addIndented("element.setText( xpp3Dom.getValue() );");
430         sc.add("}");
431 
432         sc.add("for ( Iterator i = Arrays.asList( xpp3Dom.getAttributeNames() ).iterator(); i.hasNext(); )");
433         sc.add("{");
434         sc.indent();
435 
436         sc.add("String name = (String) i.next();");
437         sc.add("element.addAttribute( name, xpp3Dom.getAttribute( name ) );");
438 
439         sc.unindent();
440         sc.add("}");
441 
442         sc.add("for ( Iterator i = Arrays.asList( xpp3Dom.getChildren() ).iterator(); i.hasNext(); )");
443         sc.add("{");
444         sc.indent();
445 
446         sc.add("Xpp3Dom child = (Xpp3Dom) i.next();");
447         sc.add("writeXpp3DomToElement( child, element );");
448 
449         sc.unindent();
450         sc.add("}");
451 
452         jClass.addMethod(method);
453     }
454 }