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