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