1 package org.codehaus.modello.plugin.sax;
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.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.JConstructor;
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.AbstractXmlJavaGenerator;
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("sax-writer")
57 public class SaxWriterGenerator extends AbstractXmlJavaGenerator {
58
59 private boolean requiresDomSupport;
60
61 @Override
62 public void generate(Model model, Map<String, Object> parameters) throws ModelloException {
63 initialize(model, parameters);
64
65 requiresDomSupport = false;
66
67 try {
68 generateSaxWriter();
69 } catch (IOException ex) {
70 throw new ModelloException("Exception while generating SAX Writer.", ex);
71 }
72 }
73
74 private void generateSaxWriter() throws ModelloException, IOException {
75 Model objectModel = getModel();
76
77 String packageName =
78 objectModel.getDefaultPackageName(isPackageWithVersion(), getGeneratedVersion()) + ".io.sax";
79
80 String marshallerName = getFileName("SaxWriter");
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.IOException");
89 jClass.addImport("java.io.OutputStream");
90 jClass.addImport("java.io.OutputStreamWriter");
91 jClass.addImport("java.io.UnsupportedEncodingException");
92 jClass.addImport("java.io.Writer");
93 jClass.addImport("java.util.Iterator");
94 jClass.addImport("java.util.Properties");
95 jClass.addImport("javax.xml.transform.OutputKeys");
96 jClass.addImport("javax.xml.transform.TransformerException");
97 jClass.addImport("javax.xml.transform.TransformerFactory");
98 jClass.addImport("javax.xml.transform.sax.SAXTransformerFactory");
99 jClass.addImport("javax.xml.transform.sax.TransformerHandler");
100 jClass.addImport("javax.xml.transform.stream.StreamResult");
101 jClass.addImport("org.xml.sax.ContentHandler");
102 jClass.addImport("org.xml.sax.SAXException");
103 jClass.addImport("org.xml.sax.helpers.AttributesImpl");
104
105 JField namespaceField = new JField(new JClass("String"), "NAMESPACE");
106 namespaceField.getModifiers().setFinal(true);
107 namespaceField.getModifiers().setStatic(true);
108 namespaceField.setInitString("\"\"");
109 jClass.addField(namespaceField);
110
111 JField factoryField = new JField(new JClass("SAXTransformerFactory"), "transformerFactory");
112 factoryField.getModifiers().setFinal(true);
113 factoryField.setInitString("(SAXTransformerFactory) TransformerFactory.newInstance()");
114 jClass.addField(factoryField);
115
116 addModelImports(jClass, null);
117
118 String root = objectModel.getRoot(getGeneratedVersion());
119
120 ModelClass rootClass = objectModel.getClass(root, getGeneratedVersion());
121
122 String rootElement = resolveTagName(rootClass);
123
124 JConstructor saxWriterConstructor = new JConstructor(jClass);
125 JSourceCode sc = saxWriterConstructor.getSourceCode();
126 sc.add("transformerFactory.setAttribute( \"indent-number\", 2 );");
127
128 jClass.addConstructor(saxWriterConstructor);
129
130
131
132
133
134 JMethod marshall = new JMethod("write");
135
136 String rootElementParameterName = uncapitalise(root);
137 marshall.addParameter(new JParameter(new JClass("Writer"), "writer"));
138 marshall.addParameter(new JParameter(new JClass(root), rootElementParameterName));
139
140 marshall.addException(new JClass("SAXException"));
141 marshall.addException(new JClass("TransformerException"));
142
143 sc = marshall.getSourceCode();
144
145 sc.add("TransformerHandler transformerHandler = transformerFactory.newTransformerHandler();");
146
147 sc.add("Properties format = new Properties();");
148 sc.add("format.put( OutputKeys.ENCODING, " + rootElementParameterName + ".getModelEncoding() );");
149 sc.add("format.put( OutputKeys.INDENT, \"yes\" );");
150 sc.add("format.put( OutputKeys.MEDIA_TYPE, \"text/xml\" );");
151 sc.add("format.put( OutputKeys.METHOD, \"xml\" );");
152
153 sc.add("transformerHandler.getTransformer().setOutputProperties( format );");
154 sc.add("transformerHandler.setResult( new StreamResult( writer ) );");
155
156 sc.add("write( transformerHandler, " + rootElementParameterName + " );");
157
158 jClass.addMethod(marshall);
159
160
161
162
163
164 marshall = new JMethod("write");
165
166 marshall.addParameter(new JParameter(new JClass("OutputStream"), "stream"));
167 marshall.addParameter(new JParameter(new JClass(root), rootElementParameterName));
168
169 marshall.addException(new JClass("SAXException"));
170 marshall.addException(new JClass("TransformerException"));
171 marshall.addException(new JClass("UnsupportedEncodingException"));
172
173 sc = marshall.getSourceCode();
174
175 sc.add("write( new OutputStreamWriter( stream, " + rootElementParameterName + ".getModelEncoding() ), "
176 + rootElementParameterName + " );");
177
178 jClass.addMethod(marshall);
179
180
181
182
183
184 marshall = new JMethod("write");
185
186 marshall.addParameter(new JParameter(new JClass("ContentHandler"), "contentHandler"));
187 marshall.addParameter(new JParameter(new JClass(root), rootElementParameterName));
188
189 marshall.addException(new JClass("SAXException"));
190
191 sc = marshall.getSourceCode();
192
193 sc.add("write( contentHandler, " + rootElementParameterName + ", true );");
194
195 jClass.addMethod(marshall);
196
197
198
199
200
201 marshall = new JMethod("write");
202
203 marshall.addParameter(new JParameter(new JClass("ContentHandler"), "contentHandler"));
204 marshall.addParameter(new JParameter(new JClass(root), rootElementParameterName));
205 marshall.addParameter(new JParameter(JType.BOOLEAN, "startDocument"));
206
207 marshall.addException(new JClass("SAXException"));
208
209 sc = marshall.getSourceCode();
210
211 sc.add("if ( startDocument )");
212 sc.add("{");
213 sc.addIndented("contentHandler.startDocument();");
214 sc.add("}");
215
216 sc.add("AttributesImpl attributes = new AttributesImpl();");
217
218 sc.add("write" + root + "( " + rootElementParameterName + ", \"" + rootElement
219 + "\", contentHandler, attributes );");
220
221 sc.add("if ( startDocument )");
222 sc.add("{");
223 sc.addIndented("contentHandler.endDocument();");
224 sc.add("}");
225
226 jClass.addMethod(marshall);
227
228
229
230
231
232 marshall = new JMethod("writeText");
233 marshall.getModifiers().makePrivate();
234
235 marshall.addParameter(new JParameter(new JClass("String"), "text"));
236 marshall.addParameter(new JParameter(new JClass("ContentHandler"), "contentHandler"));
237
238 marshall.addException(new JClass("SAXException"));
239
240 sc = marshall.getSourceCode();
241
242 sc.add("contentHandler.characters( text.toCharArray(), 0, text.length() );");
243
244
245
246 jClass.addMethod(marshall);
247
248 writeAllClasses(objectModel, jClass);
249
250 if (requiresDomSupport) {
251 createWriteDomMethod(jClass);
252 createXpp3DomMethod(jClass);
253 }
254
255 jClass.print(sourceWriter);
256
257 sourceWriter.close();
258 }
259
260 private void writeAllClasses(Model objectModel, JClass jClass) throws ModelloException {
261 for (ModelClass clazz : getClasses(objectModel)) {
262 writeClass(clazz, jClass);
263 }
264 }
265
266 private void writeClass(ModelClass modelClass, JClass jClass) throws ModelloException {
267 String className = modelClass.getName();
268
269 String uncapClassName = uncapitalise(className);
270
271 JMethod marshall = new JMethod("write" + className);
272
273 marshall.addParameter(new JParameter(new JClass(className), uncapClassName));
274 marshall.addParameter(new JParameter(new JClass("String"), "tagName"));
275 marshall.addParameter(new JParameter(new JClass("ContentHandler"), "contentHandler"));
276 marshall.addParameter(new JParameter(new JClass("AttributesImpl"), "attributes"));
277
278 marshall.addException(new JClass("SAXException"));
279
280 marshall.getModifiers().makePrivate();
281
282 JSourceCode sc = marshall.getSourceCode();
283
284 ModelClassMetadata classMetadata = (ModelClassMetadata) modelClass.getMetadata(ModelClassMetadata.ID);
285
286 String namespace = null;
287 XmlModelMetadata xmlModelMetadata =
288 (XmlModelMetadata) modelClass.getModel().getMetadata(XmlModelMetadata.ID);
289
290 ModelField contentField = null;
291
292 String contentValue = null;
293
294 List<ModelField> modelFields = getFieldsForXml(modelClass, getGeneratedVersion());
295
296 boolean needsToCleanAttributes = false;
297
298
299 for (ModelField field : modelFields) {
300 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
301
302 JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata(JavaFieldMetadata.ID);
303
304 String fieldTagName = resolveTagName(field, xmlFieldMetadata);
305
306 String type = field.getType();
307
308 String value = uncapClassName + "." + getPrefix(javaFieldMetadata) + capitalise(field.getName()) + "()";
309
310 if (xmlFieldMetadata.isContent()) {
311 contentField = field;
312 contentValue = value;
313 continue;
314 }
315
316 if (xmlFieldMetadata.isAttribute()) {
317 sc.add(getValueChecker(type, value, field));
318
319 sc.add("{");
320 sc.addIndented("attributes.addAttribute( NAMESPACE, \""
321 + fieldTagName
322 + "\", \""
323 + fieldTagName
324 + "\", \"CDATA\", "
325 + getValue(field.getType(), value, xmlFieldMetadata)
326 + " );");
327 sc.add("}");
328
329 needsToCleanAttributes = true;
330 }
331 }
332
333
334 if (classMetadata.isRootElement() && (xmlModelMetadata.getNamespace() != null)) {
335 namespace = xmlModelMetadata.getNamespace(getGeneratedVersion());
336 sc.add("contentHandler.startPrefixMapping( \"\", \"" + namespace + "\" );");
337 }
338
339 if ((namespace != null) && (xmlModelMetadata.getSchemaLocation() != null)) {
340 String url = xmlModelMetadata.getSchemaLocation(getGeneratedVersion());
341
342 sc.add("contentHandler.startPrefixMapping( \"xsi\", \"http://www.w3.org/2001/XMLSchema-instance\" );");
343
344 sc.add(
345 "attributes.addAttribute( \"http://www.w3.org/2001/XMLSchema-instance\", \"schemaLocation\", \"xsi:schemaLocation\", \"CDATA\", \""
346 + namespace + " " + url + "\" );");
347 }
348
349 sc.add("contentHandler.startElement( NAMESPACE, tagName, tagName, attributes );");
350
351 if (needsToCleanAttributes) {
352 sc.add("attributes.clear();");
353 }
354
355 if (contentField != null) {
356 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) contentField.getMetadata(XmlFieldMetadata.ID);
357 sc.add("writeText( " + getValue(contentField.getType(), contentValue, xmlFieldMetadata)
358 + ", contentHandler );");
359 }
360
361
362 for (ModelField field : modelFields) {
363 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
364
365 if (xmlFieldMetadata.isContent()) {
366
367 continue;
368 }
369
370 JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata(JavaFieldMetadata.ID);
371
372 String fieldTagName = resolveTagName(field, xmlFieldMetadata);
373
374 String type = field.getType();
375
376 String value = uncapClassName + "." + getPrefix(javaFieldMetadata) + capitalise(field.getName()) + "()";
377
378 if (xmlFieldMetadata.isAttribute()) {
379 continue;
380 }
381
382 if (field instanceof ModelAssociation) {
383 ModelAssociation association = (ModelAssociation) field;
384
385 String associationName = association.getName();
386
387 if (association.isOneMultiplicity()) {
388 sc.add(getValueChecker(type, value, association));
389
390 sc.add("{");
391 sc.addIndented("write" + association.getTo() + "( (" + association.getTo() + ") " + value + ", \""
392 + fieldTagName + "\", contentHandler, attributes );");
393 sc.add("}");
394 } else {
395
396
397 XmlAssociationMetadata xmlAssociationMetadata =
398 (XmlAssociationMetadata) association.getAssociationMetadata(XmlAssociationMetadata.ID);
399
400 String valuesTagName = resolveTagName(fieldTagName, xmlAssociationMetadata);
401
402 type = association.getType();
403 String toType = association.getTo();
404
405 boolean wrappedItems = xmlAssociationMetadata.isWrappedItems();
406
407 if (ModelDefault.LIST.equals(type) || ModelDefault.SET.equals(type)) {
408 sc.add(getValueChecker(type, value, association));
409
410 sc.add("{");
411 sc.indent();
412
413 if (wrappedItems) {
414 sc.add("contentHandler.startElement( NAMESPACE, \"" + fieldTagName + "\", \"" + fieldTagName
415 + "\", attributes );");
416 }
417
418 sc.add("for ( Iterator iter = " + value + ".iterator(); iter.hasNext(); )");
419
420 sc.add("{");
421 sc.indent();
422
423 if (isClassInModel(association.getTo(), modelClass.getModel())) {
424 sc.add(toType + " o = (" + toType + ") iter.next();");
425
426 sc.add("write" + toType + "( o, \"" + valuesTagName + "\", contentHandler, attributes );");
427 } else {
428 sc.add(toType + " " + singular(uncapitalise(field.getName())) + " = (" + toType
429 + ") iter.next();");
430
431 sc.add("contentHandler.startElement( NAMESPACE, \"" + valuesTagName + "\", \""
432 + valuesTagName + "\", attributes );");
433 sc.add("writeText( " + singular(uncapitalise(field.getName())) + ", contentHandler );");
434 sc.add("contentHandler.endElement( NAMESPACE, \"" + valuesTagName + "\", \"" + valuesTagName
435 + "\" );");
436 }
437
438 sc.unindent();
439 sc.add("}");
440
441 if (wrappedItems) {
442 sc.add("contentHandler.endElement( NAMESPACE, \"" + fieldTagName + "\", \"" + fieldTagName
443 + "\" );");
444 }
445
446 sc.unindent();
447 sc.add("}");
448 } else {
449
450
451 sc.add(getValueChecker(type, value, field));
452
453 sc.add("{");
454 sc.indent();
455
456 if (wrappedItems) {
457 sc.add("contentHandler.startElement( NAMESPACE, \"" + fieldTagName + "\", \"" + fieldTagName
458 + "\", attributes );");
459 }
460
461 sc.add("for ( Iterator iter = " + value + ".keySet().iterator(); iter.hasNext(); )");
462
463 sc.add("{");
464 sc.indent();
465
466 sc.add("String key = (String) iter.next();");
467
468 sc.add(association.getTo() + " value = (" + association.getTo() + ") " + value
469 + ".get( key );");
470
471 if (xmlAssociationMetadata.isMapExplode()) {
472 sc.add("contentHandler.startElement( NAMESPACE, \"" + singular(associationName) + "\", \""
473 + singular(associationName) + "\", attributes );");
474
475 sc.add("contentHandler.startElement( NAMESPACE, \"key\", \"key\", attributes );");
476 sc.add("writeText( key, contentHandler );");
477 sc.add("contentHandler.endElement( NAMESPACE, \"key\", \"key\" );");
478
479 if (isClassInModel(
480 association.getTo(),
481 association.getModelClass().getModel())) {
482 sc.add("write" + association.getTo()
483 + "( value, \"value\", contentHandler, attributes );");
484 } else {
485 sc.add("contentHandler.startElement( NAMESPACE, \"value\", \"value\", attributes );");
486 sc.add("writeText( " + getValue(association.getTo(), "value", xmlFieldMetadata)
487 + ", contentHandler );");
488 sc.add("contentHandler.endElement( NAMESPACE, \"value\", \"value\" );");
489 }
490
491 sc.add("contentHandler.endElement( NAMESPACE, \"" + singular(associationName) + "\", \""
492 + singular(associationName) + "\" );");
493 } else {
494 if (isClassInModel(
495 association.getTo(),
496 association.getModelClass().getModel())) {
497 sc.add("write" + association.getTo() + "( value, key, contentHandler, attributes );");
498 } else {
499 sc.add("contentHandler.startElement( NAMESPACE, key, key, attributes );");
500 sc.add("writeText( " + getValue(association.getTo(), "value", xmlFieldMetadata)
501 + ", contentHandler );");
502 sc.add("contentHandler.endElement( NAMESPACE, key, key );");
503 }
504 }
505
506 sc.unindent();
507 sc.add("}");
508
509 if (wrappedItems) {
510 sc.add("contentHandler.endElement( NAMESPACE, \"" + fieldTagName + "\", \"" + fieldTagName
511 + "\" );");
512 }
513
514 sc.unindent();
515 sc.add("}");
516 }
517 }
518 } else {
519 sc.add(getValueChecker(type, value, field));
520
521 sc.add("{");
522 if ("DOM".equals(field.getType())) {
523 if (domAsXpp3) {
524 sc.addIndented("writeDom( (Xpp3Dom) " + value + ", contentHandler, attributes );");
525 } else {
526 sc.addIndented("writeDom( (Element) " + value + ", contentHandler );");
527 }
528
529 requiresDomSupport = true;
530 } else {
531 sc.indent();
532 sc.add("contentHandler.startElement( NAMESPACE, \"" + fieldTagName + "\", \"" + fieldTagName
533 + "\", attributes );");
534 sc.add("writeText( " + getValue(field.getType(), value, xmlFieldMetadata) + ", contentHandler );");
535 sc.add("contentHandler.endElement( NAMESPACE, \"" + fieldTagName + "\", \"" + fieldTagName
536 + "\" );");
537 sc.unindent();
538 }
539 sc.add("}");
540 }
541 }
542
543 sc.add("contentHandler.endElement( NAMESPACE, tagName, tagName );");
544
545 jClass.addMethod(marshall);
546 }
547
548 private void createXpp3DomMethod(JClass jClass) {
549 jClass.addImport("org.codehaus.plexus.util.xml.Xpp3Dom");
550
551 JMethod method = new JMethod("writeDom");
552 method.getModifiers().makePrivate();
553
554 method.addParameter(new JParameter(new JType("Xpp3Dom"), "dom"));
555 method.addParameter(new JParameter(new JClass("ContentHandler"), "contentHandler"));
556 method.addParameter(new JParameter(new JClass("AttributesImpl"), "attributes"));
557
558 method.addException(new JClass("SAXException"));
559
560 JSourceCode sc = method.getSourceCode();
561
562 sc.add("String[] attributeNames = dom.getAttributeNames();");
563 sc.add("if ( attributeNames != null && attributeNames.length > 0 )");
564 sc.add("{");
565 sc.indent();
566 sc.add("String attributeName;");
567 sc.add("String attributeValue;");
568 sc.add("for ( int i = 0; i < attributeNames.length; i++ )");
569 sc.add("{");
570 sc.indent();
571 sc.add("attributeName = attributeNames[i];");
572 sc.add("attributeValue = dom.getAttribute( attributeName );");
573 sc.add("attributes.addAttribute( NAMESPACE, attributeName, attributeName, \"CDATA\", attributeValue );");
574 sc.unindent();
575 sc.add("}");
576 sc.unindent();
577 sc.add("}");
578
579 sc.add("contentHandler.startElement( NAMESPACE, dom.getName(), dom.getName(), attributes );");
580
581 sc.add("if ( attributeNames != null && attributeNames.length > 0 )");
582 sc.add("{");
583 sc.addIndented("attributes.clear();");
584 sc.add("}");
585
586 sc.add("Xpp3Dom[] children = dom.getChildren();");
587 sc.add("if ( children != null && children.length > 0 )");
588 sc.add("{");
589 sc.indent();
590 sc.add("for ( int i = 0; i < children.length; i++ )");
591 sc.add("{");
592 sc.addIndented("writeDom( children[i], contentHandler, attributes );");
593 sc.add("}");
594 sc.unindent();
595 sc.add("}");
596
597 sc.add("String value = dom.getValue();");
598 sc.add("if ( value != null )");
599 sc.add("{");
600 sc.addIndented("writeText( value, contentHandler );");
601 sc.add("}");
602 sc.add("contentHandler.endElement( NAMESPACE, dom.getName(), dom.getName() );");
603
604 jClass.addMethod(method);
605 }
606
607 private void createWriteDomMethod(JClass jClass) {
608 jClass.addImport("org.w3c.dom.Element");
609 jClass.addImport("javax.xml.transform.Transformer");
610 jClass.addImport("javax.xml.transform.dom.DOMSource");
611 jClass.addImport("javax.xml.transform.sax.SAXResult");
612
613 JMethod method = new JMethod("writeDom");
614 method.getModifiers().makePrivate();
615
616 method.addParameter(new JParameter(new JType("Element"), "dom"));
617 method.addParameter(new JParameter(new JClass("ContentHandler"), "contentHandler"));
618
619 method.addException(new JClass("SAXException"));
620
621 JSourceCode sc = method.getSourceCode();
622
623 sc.add("try");
624 sc.add("{");
625 sc.indent();
626 sc.add("Transformer transformer = transformerFactory.newTransformer();");
627 sc.add("DOMSource source = new DOMSource( dom );");
628 sc.add("SAXResult result = new SAXResult( contentHandler );");
629 sc.add("transformer.transform( source, result );");
630 sc.unindent();
631 sc.add("}");
632
633 sc.add("catch ( TransformerException e )");
634 sc.add("{");
635 sc.addIndented("throw new SAXException( \"Impossible to convert DOM element, see nested exceptions.\", e );");
636 sc.add("}");
637
638 jClass.addMethod(method);
639 }
640 }