1 package org.codehaus.modello.plugin.xpp3;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
53
54
55 @Named("xpp3-writer")
56 public class Xpp3WriterGenerator extends AbstractXpp3Generator {
57 private String extendedClassnameSuffix;
58
59 protected void prepareLocationTracking(JClass jClass) {
60
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
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
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
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
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
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
306 for (ModelField field : modelFields) {
307 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
308
309 if (xmlFieldMetadata.isContent()) {
310
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
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
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
501 sc.add("serializer.startTag( NAMESPACE, dom.getTagName() );");
502
503
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
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 }