View Javadoc
1   package org.codehaus.modello.plugin.stax;
2   
3   /*
4    * Copyright (c) 2004, Codehaus.org
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.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   * @author <a href="mailto:jason@modello.org">Jason van Zyl </a>
54   * @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse </a>
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         // Write the write( Writer, Model ) method which will do the unmarshalling.
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         // currently, only woodstox supports Windows line endings. It works with Java 6/RI and stax <= 1.1.1 as well
134         // but we have no way to detect them
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         // Write the write( OutputStream, Model ) method which will do the unmarshalling.
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         // currently, only woodstox supports Windows line endings. It works with Java 6/RI and stax <= 1.1.1 as well
182         // but we have no way to detect them
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         // add namespace information for root element only
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         // XML attributes
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         // XML tags
326         for (ModelField field : modelFields) {
327             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
328 
329             if (xmlFieldMetadata.isContent()) {
330                 // skip field with type Content
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                         // if xml.reference, then store as a reference instead
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                     // MANY_MULTIPLICITY
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                         // Map or Properties
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         // start element
558         sc.add("serializer.writeStartElement( dom.get" + (domAsXpp3 ? "Name" : "TagName") + "() );");
559 
560         // attributes
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         // child nodes & text
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 }