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