1 package org.codehaus.modello.plugin.stax;
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.Inject;
26 import javax.inject.Named;
27
28 import java.io.IOException;
29 import java.util.List;
30 import java.util.Properties;
31
32 import org.codehaus.modello.ModelloException;
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.JConstructor;
40 import org.codehaus.modello.plugin.java.javasource.JField;
41 import org.codehaus.modello.plugin.java.javasource.JMethod;
42 import org.codehaus.modello.plugin.java.javasource.JParameter;
43 import org.codehaus.modello.plugin.java.javasource.JSourceCode;
44 import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
45 import org.codehaus.modello.plugin.java.javasource.JType;
46 import org.codehaus.modello.plugin.java.metadata.JavaFieldMetadata;
47 import org.codehaus.modello.plugin.model.ModelClassMetadata;
48 import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata;
49 import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata;
50 import org.codehaus.modello.plugins.xml.metadata.XmlModelMetadata;
51
52
53
54
55
56 @Named("stax-writer")
57 public class StaxWriterGenerator extends AbstractStaxGenerator {
58
59 private boolean requiresDomSupport;
60
61 @Inject
62 private StaxSerializerGenerator serializerGenerator;
63
64 public void generate(Model model, Properties parameters) throws ModelloException {
65 initialize(model, parameters);
66
67 requiresDomSupport = false;
68
69 try {
70 generateStaxWriter();
71 } catch (IOException ex) {
72 throw new ModelloException("Exception while generating StAX Writer.", ex);
73 }
74
75 serializerGenerator.generate(model, parameters);
76 }
77
78 private void generateStaxWriter() throws ModelloException, IOException {
79 Model objectModel = getModel();
80
81 String packageName =
82 objectModel.getDefaultPackageName(isPackageWithVersion(), getGeneratedVersion()) + ".io.stax";
83
84 String marshallerName = getFileName("StaxWriter");
85
86 JSourceWriter sourceWriter = newJSourceWriter(packageName, marshallerName);
87
88 JClass jClass = new JClass(packageName + '.' + marshallerName);
89 initHeader(jClass);
90 suppressAllWarnings(objectModel, jClass);
91
92 jClass.addImport("java.io.IOException");
93 jClass.addImport("java.io.OutputStream");
94 jClass.addImport("java.io.Writer");
95 jClass.addImport("java.io.StringWriter");
96 jClass.addImport("java.text.DateFormat");
97 jClass.addImport("java.util.Iterator");
98 jClass.addImport("java.util.Locale");
99 jClass.addImport("java.util.jar.Manifest");
100 jClass.addImport("javax.xml.stream.*");
101
102 addModelImports(jClass, null);
103
104 jClass.addField(new JField(JType.INT, "curId"));
105 jClass.addField(new JField(new JType("java.util.Map"), "idMap"));
106 JConstructor constructor = new JConstructor(jClass);
107 constructor.getSourceCode().add("idMap = new java.util.HashMap();");
108 jClass.addConstructor(constructor);
109
110 String root = objectModel.getRoot(getGeneratedVersion());
111
112 ModelClass rootClass = objectModel.getClass(root, getGeneratedVersion());
113
114 String rootElement = resolveTagName(rootClass);
115
116
117
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("java.io.IOException"));
127 marshall.addException(new JClass("XMLStreamException"));
128
129 JSourceCode sc = marshall.getSourceCode();
130
131 sc.add("XMLOutputFactory factory = XMLOutputFactory.newInstance();");
132
133
134
135 sc.add("boolean supportWindowsLineEndings = false;");
136 sc.add("if ( factory.isPropertySupported( \"com.ctc.wstx.outputEscapeCr\" ) )");
137 sc.add("{");
138 sc.indent();
139 sc.add("factory.setProperty( \"com.ctc.wstx.outputEscapeCr\", Boolean.FALSE );");
140 sc.add("supportWindowsLineEndings = true;");
141 sc.unindent();
142 sc.add("}");
143
144 sc.add("if ( factory.isPropertySupported( \"org.codehaus.stax2.automaticEmptyElements\" ) )");
145 sc.add("{");
146 sc.addIndented("factory.setProperty( \"org.codehaus.stax2.automaticEmptyElements\", Boolean.FALSE );");
147 sc.add("}");
148
149 sc.add(
150 "IndentingXMLStreamWriter serializer = new IndentingXMLStreamWriter( factory.createXMLStreamWriter( writer ) );");
151
152 sc.add("if ( supportWindowsLineEndings )");
153 sc.add("{");
154 sc.addIndented("serializer.setNewLine( serializer.getLineSeparator() );");
155 sc.add("}");
156
157 sc.add("serializer.writeStartDocument( " + rootElementParameterName + ".getModelEncoding(), \"1.0\" );");
158
159 sc.add("write" + root + "( " + rootElementParameterName + ", \"" + rootElement + "\", serializer );");
160
161 sc.add("serializer.writeEndDocument();");
162
163 jClass.addMethod(marshall);
164
165
166
167
168
169 marshall = new JMethod("write");
170
171 marshall.addParameter(new JParameter(new JClass("OutputStream"), "stream"));
172 marshall.addParameter(new JParameter(new JClass(root), rootElementParameterName));
173
174 marshall.addException(new JClass("java.io.IOException"));
175 marshall.addException(new JClass("XMLStreamException"));
176
177 sc = marshall.getSourceCode();
178
179 sc.add("XMLOutputFactory factory = XMLOutputFactory.newInstance();");
180
181
182
183 sc.add("boolean supportWindowsLineEndings = false;");
184 sc.add("if ( factory.isPropertySupported( \"com.ctc.wstx.outputEscapeCr\" ) )");
185 sc.add("{");
186 sc.indent();
187 sc.add("factory.setProperty( \"com.ctc.wstx.outputEscapeCr\", Boolean.FALSE );");
188 sc.add("supportWindowsLineEndings = true;");
189 sc.unindent();
190 sc.add("}");
191
192 sc.add("if ( factory.isPropertySupported( \"org.codehaus.stax2.automaticEmptyElements\" ) )");
193 sc.add("{");
194 sc.addIndented("factory.setProperty( \"org.codehaus.stax2.automaticEmptyElements\", Boolean.FALSE );");
195 sc.add("}");
196
197 sc.add(
198 "IndentingXMLStreamWriter serializer = new IndentingXMLStreamWriter( factory.createXMLStreamWriter( stream, "
199 + rootElementParameterName + ".getModelEncoding() ) );");
200
201 sc.add("if ( supportWindowsLineEndings )");
202 sc.add("{");
203 sc.addIndented("serializer.setNewLine( serializer.getLineSeparator() );");
204 sc.add("}");
205
206 sc.add("serializer.writeStartDocument( " + rootElementParameterName + ".getModelEncoding(), \"1.0\" );");
207
208 sc.add("write" + root + "( " + rootElementParameterName + ", \"" + rootElement + "\", serializer );");
209
210 sc.add("serializer.writeEndDocument();");
211
212 jClass.addMethod(marshall);
213
214 writeAllClasses(objectModel, jClass);
215
216 if (requiresDomSupport) {
217 createWriteDomMethod(jClass);
218 }
219
220 jClass.print(sourceWriter);
221
222 sourceWriter.close();
223 }
224
225 private void writeAllClasses(Model objectModel, JClass jClass) throws ModelloException {
226 for (ModelClass clazz : getClasses(objectModel)) {
227 writeClass(clazz, jClass);
228 }
229 }
230
231 private void writeClass(ModelClass modelClass, JClass jClass) throws ModelloException {
232 String className = modelClass.getName();
233
234 String uncapClassName = uncapitalise(className);
235
236 JMethod marshall = new JMethod("write" + className);
237 marshall.getModifiers().makePrivate();
238
239 marshall.addParameter(new JParameter(new JClass(className), uncapClassName));
240 marshall.addParameter(new JParameter(new JClass("String"), "tagName"));
241 marshall.addParameter(new JParameter(new JClass("XMLStreamWriter"), "serializer"));
242
243 marshall.addException(new JClass("java.io.IOException"));
244 marshall.addException(new JClass("XMLStreamException"));
245
246 JSourceCode sc = marshall.getSourceCode();
247
248 sc.add("if ( " + uncapClassName + " != null )");
249
250 sc.add("{");
251 sc.indent();
252
253 ModelClassMetadata classMetadata = (ModelClassMetadata) modelClass.getMetadata(ModelClassMetadata.ID);
254
255 String namespace = null;
256 XmlModelMetadata xmlModelMetadata =
257 (XmlModelMetadata) modelClass.getModel().getMetadata(XmlModelMetadata.ID);
258
259
260 if (classMetadata.isRootElement() && (xmlModelMetadata.getNamespace() != null)) {
261 namespace = xmlModelMetadata.getNamespace(getGeneratedVersion());
262 sc.add("serializer.setDefaultNamespace( \"" + namespace + "\" );");
263 }
264
265 sc.add("serializer.writeStartElement( tagName );");
266
267 if (namespace != null) {
268 sc.add("serializer.writeDefaultNamespace( \"" + namespace + "\" );");
269
270 if (xmlModelMetadata.getSchemaLocation() != null) {
271 String url = xmlModelMetadata.getSchemaLocation(getGeneratedVersion());
272
273 sc.add("serializer.setPrefix( \"xsi\", \"http://www.w3.org/2001/XMLSchema-instance\" );");
274 sc.add("serializer.writeNamespace( \"xsi\", \"http://www.w3.org/2001/XMLSchema-instance\" );");
275 sc.add(
276 "serializer.writeAttribute( \"http://www.w3.org/2001/XMLSchema-instance\", \"schemaLocation\", \""
277 + namespace + " " + url + "\" );");
278 }
279 }
280
281 if (isAssociationPartToClass(modelClass)) {
282 if (modelClass.getIdentifierFields(getGeneratedVersion()).size() != 1) {
283 writeIdMapCheck(sc, uncapClassName, "modello.id");
284 }
285 }
286
287 ModelField contentField = null;
288
289 String contentValue = null;
290
291 List<ModelField> modelFields = getFieldsForXml(modelClass, getGeneratedVersion());
292
293
294 for (ModelField field : modelFields) {
295 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
296
297 String fieldTagName = resolveTagName(field, xmlFieldMetadata);
298
299 String type = field.getType();
300
301 String value = getFieldValue(uncapClassName, field);
302
303 if (xmlFieldMetadata.isContent()) {
304 contentField = field;
305 contentValue = value;
306 continue;
307 }
308
309 if (xmlFieldMetadata.isAttribute()) {
310 sc.add(getValueChecker(type, value, field));
311
312 sc.add("{");
313 sc.addIndented("serializer.writeAttribute( \"" + fieldTagName + "\", "
314 + getValue(field.getType(), value, xmlFieldMetadata) + " );");
315 sc.add("}");
316 }
317 }
318
319 if (contentField != null) {
320 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) contentField.getMetadata(XmlFieldMetadata.ID);
321 sc.add("serializer.writeCharacters( " + getValue(contentField.getType(), contentValue, xmlFieldMetadata)
322 + " );");
323 }
324
325
326 for (ModelField field : modelFields) {
327 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
328
329 if (xmlFieldMetadata.isContent()) {
330
331 continue;
332 }
333
334 String fieldTagName = resolveTagName(field, xmlFieldMetadata);
335
336 String type = field.getType();
337
338 String value = getFieldValue(uncapClassName, field);
339
340 if (xmlFieldMetadata.isAttribute()) {
341 continue;
342 }
343
344 if (field instanceof ModelAssociation) {
345 ModelAssociation association = (ModelAssociation) field;
346
347 String associationName = association.getName();
348
349 ModelField referenceIdentifierField = getReferenceIdentifierField(association);
350
351 if (association.isOneMultiplicity()) {
352 sc.add(getValueChecker(type, value, association));
353 sc.add("{");
354 sc.indent();
355
356 if (referenceIdentifierField != null) {
357
358
359 sc.add("serializer.writeStartElement( \"" + fieldTagName + "\" );");
360
361 writeElementAttribute(sc, referenceIdentifierField, value);
362
363 sc.add("serializer.writeEndElement();");
364 } else {
365 sc.add("write" + association.getTo() + "( (" + association.getTo() + ") " + value + ", \""
366 + fieldTagName + "\", serializer );");
367 }
368
369 sc.unindent();
370 sc.add("}");
371 } else {
372
373
374 XmlAssociationMetadata xmlAssociationMetadata =
375 (XmlAssociationMetadata) association.getAssociationMetadata(XmlAssociationMetadata.ID);
376
377 String valuesTagName = resolveTagName(fieldTagName, xmlAssociationMetadata);
378
379 type = association.getType();
380 String toType = association.getTo();
381
382 boolean wrappedItems = xmlAssociationMetadata.isWrappedItems();
383
384 if (ModelDefault.LIST.equals(type) || ModelDefault.SET.equals(type)) {
385 sc.add(getValueChecker(type, value, association));
386
387 sc.add("{");
388 sc.indent();
389
390 if (wrappedItems) {
391 sc.add("serializer.writeStartElement( " + "\"" + fieldTagName + "\" );");
392 }
393
394 sc.add("for ( Iterator iter = " + value + ".iterator(); iter.hasNext(); )");
395
396 sc.add("{");
397 sc.indent();
398
399 if (isClassInModel(association.getTo(), modelClass.getModel())) {
400 sc.add(toType + " o = (" + toType + ") iter.next();");
401
402 if (referenceIdentifierField != null) {
403 sc.add("serializer.writeStartElement( \"" + valuesTagName + "\" );");
404
405 writeElementAttribute(sc, referenceIdentifierField, "o");
406
407 sc.add("serializer.writeEndElement();");
408 } else {
409 sc.add("write" + toType + "( o, \"" + valuesTagName + "\", serializer );");
410 }
411 } else {
412 sc.add(toType + " " + singular(uncapitalise(field.getName())) + " = (" + toType
413 + ") iter.next();");
414
415 sc.add("serializer.writeStartElement( " + "\"" + valuesTagName + "\" );");
416 sc.add("serializer.writeCharacters( " + singular(uncapitalise(field.getName())) + " );");
417 sc.add("serializer.writeEndElement();");
418 }
419
420 sc.unindent();
421 sc.add("}");
422
423 if (wrappedItems) {
424 sc.add("serializer.writeEndElement();");
425 }
426
427 sc.unindent();
428 sc.add("}");
429 } else {
430
431
432 sc.add(getValueChecker(type, value, field));
433
434 sc.add("{");
435 sc.indent();
436
437 if (wrappedItems) {
438 sc.add("serializer.writeStartElement( " + "\"" + fieldTagName + "\" );");
439 }
440
441 sc.add("for ( Iterator iter = " + value + ".keySet().iterator(); iter.hasNext(); )");
442
443 sc.add("{");
444 sc.indent();
445
446 sc.add("String key = (String) iter.next();");
447
448 sc.add("String value = (String) " + value + ".get( key );");
449
450 if (xmlAssociationMetadata.isMapExplode()) {
451 sc.add("serializer.writeStartElement( \"" + singular(associationName) + "\" );");
452 sc.add("serializer.writeStartElement( \"key\" );");
453 sc.add("serializer.writeCharacters( key );");
454 sc.add("serializer.writeEndElement();");
455 sc.add("serializer.writeStartElement( \"value\" );");
456 sc.add("serializer.writeCharacters( value );");
457 sc.add("serializer.writeEndElement();");
458 sc.add("serializer.writeEndElement();");
459 } else {
460 sc.add("serializer.writeStartElement( \"\" + key + \"\" );");
461 sc.add("serializer.writeCharacters( value );");
462 sc.add("serializer.writeEndElement();");
463 }
464
465 sc.unindent();
466 sc.add("}");
467
468 if (wrappedItems) {
469 sc.add("serializer.writeEndElement();");
470 }
471
472 sc.unindent();
473 sc.add("}");
474 }
475 }
476 } else {
477 sc.add(getValueChecker(type, value, field));
478
479 sc.add("{");
480 sc.indent();
481
482 if ("DOM".equals(field.getType())) {
483 sc.add("writeDom( (" + (domAsXpp3 ? "Xpp3Dom" : "org.w3c.dom.Element") + ") " + value
484 + ", serializer );");
485
486 requiresDomSupport = true;
487 } else {
488 sc.add("serializer.writeStartElement( " + "\"" + fieldTagName + "\" );");
489 sc.add("serializer.writeCharacters( " + getValue(field.getType(), value, xmlFieldMetadata) + " );");
490 sc.add("serializer.writeEndElement();");
491 }
492
493 sc.unindent();
494 sc.add("}");
495 }
496 }
497
498 sc.add("serializer.writeEndElement();");
499
500 sc.unindent();
501 sc.add("}");
502
503 jClass.addMethod(marshall);
504 }
505
506 private void writeElementAttribute(JSourceCode sc, ModelField referenceIdentifierField, String value) {
507 if (referenceIdentifierField instanceof DummyIdModelField) {
508 writeIdMapCheck(sc, value, referenceIdentifierField.getName());
509 } else {
510 String v = getValue(
511 referenceIdentifierField.getType(),
512 getFieldValue(value, referenceIdentifierField),
513 (XmlFieldMetadata) referenceIdentifierField.getMetadata(XmlFieldMetadata.ID));
514 sc.add("serializer.writeAttribute( \"" + referenceIdentifierField.getName() + "\", " + v + " );");
515 }
516 }
517
518 private static void writeIdMapCheck(JSourceCode sc, String value, String attributeName) {
519 sc.add("if ( !idMap.containsKey( " + value + " ) )");
520 sc.add("{");
521 sc.indent();
522
523 sc.add("++curId;");
524 sc.add("String id = String.valueOf( curId );");
525 sc.add("idMap.put( " + value + ", id );");
526 sc.add("serializer.writeAttribute( \"" + attributeName + "\", id );");
527
528 sc.unindent();
529 sc.add("}");
530 sc.add("else");
531 sc.add("{");
532 sc.addIndented("serializer.writeAttribute( \"" + attributeName + "\", (String) idMap.get( " + value + " ) );");
533 sc.add("}");
534 }
535
536 private String getFieldValue(String uncapClassName, ModelField field) {
537 JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata(JavaFieldMetadata.ID);
538
539 return uncapClassName + "." + getPrefix(javaFieldMetadata) + capitalise(field.getName()) + "()";
540 }
541
542 private void createWriteDomMethod(JClass jClass) {
543 if (domAsXpp3) {
544 jClass.addImport("org.codehaus.plexus.util.xml.Xpp3Dom");
545 }
546 String type = domAsXpp3 ? "Xpp3Dom" : "org.w3c.dom.Element";
547 JMethod method = new JMethod("writeDom");
548 method.getModifiers().makePrivate();
549
550 method.addParameter(new JParameter(new JType(type), "dom"));
551 method.addParameter(new JParameter(new JType("XMLStreamWriter"), "serializer"));
552
553 method.addException(new JClass("XMLStreamException"));
554
555 JSourceCode sc = method.getSourceCode();
556
557
558 sc.add("serializer.writeStartElement( dom.get" + (domAsXpp3 ? "Name" : "TagName") + "() );");
559
560
561 if (domAsXpp3) {
562 sc.add("String[] attributeNames = dom.getAttributeNames();");
563 sc.add("for ( int i = 0; i < attributeNames.length; i++ )");
564 sc.add("{");
565
566 sc.indent();
567 sc.add("String attributeName = attributeNames[i];");
568 sc.add("serializer.writeAttribute( attributeName, dom.getAttribute( attributeName ) );");
569 sc.unindent();
570
571 sc.add("}");
572 } else {
573 sc.add("org.w3c.dom.NamedNodeMap attributes = dom.getAttributes();");
574 sc.add("for ( int i = 0; i < attributes.getLength(); i++ )");
575 sc.add("{");
576
577 sc.indent();
578 sc.add("org.w3c.dom.Node attribute = attributes.item( i );");
579 sc.add("serializer.writeAttribute( attribute.getNodeName(), attribute.getNodeValue() );");
580 sc.unindent();
581
582 sc.add("}");
583 }
584
585
586 if (domAsXpp3) {
587 sc.add("Xpp3Dom[] children = dom.getChildren();");
588 sc.add("for ( int i = 0; i < children.length; i++ )");
589 sc.add("{");
590 sc.addIndented("writeDom( children[i], serializer );");
591 sc.add("}");
592
593 sc.add("String value = dom.getValue();");
594 sc.add("if ( value != null )");
595 sc.add("{");
596 sc.addIndented("serializer.writeCharacters( value );");
597 sc.add("}");
598 } else {
599 sc.add("org.w3c.dom.NodeList children = dom.getChildNodes();");
600 sc.add("for ( int i = 0; i < children.getLength(); i++ )");
601 sc.add("{");
602 sc.indent();
603 sc.add("org.w3c.dom.Node node = children.item( i );");
604 sc.add("if ( node instanceof org.w3c.dom.Element)");
605 sc.add("{");
606 sc.addIndented("writeDom( (org.w3c.dom.Element) children.item( i ), serializer );");
607 sc.add("}");
608 sc.add("else");
609 sc.add("{");
610 sc.addIndented("serializer.writeCharacters( node.getTextContent() );");
611 sc.add("}");
612 sc.unindent();
613 sc.add("}");
614 }
615
616 sc.add("serializer.writeEndElement();");
617
618 jClass.addMethod(method);
619 }
620 }