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.plugin.java.javasource.JClass;
28  import org.codehaus.modello.plugin.java.javasource.JConstructor;
29  import org.codehaus.modello.plugin.java.javasource.JField;
30  import org.codehaus.modello.plugin.java.javasource.JMethod;
31  import org.codehaus.modello.plugin.java.javasource.JParameter;
32  import org.codehaus.modello.plugin.java.javasource.JSourceCode;
33  import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
34  import org.codehaus.modello.plugin.java.javasource.JType;
35  import org.codehaus.plexus.util.StringUtils;
36  
37  import java.io.IOException;
38  import java.util.ArrayList;
39  import java.util.List;
40  import java.util.Properties;
41  
42  /**
43   * Generates the IndentingXMLStreamWriter used by the writer for pretty printing.
44   * 
45   * @author Benjamin Bentmann
46   */
47  public class StaxSerializerGenerator
48      extends AbstractStaxGenerator
49  {
50  
51      public void generate( Model model, Properties parameters )
52          throws ModelloException
53      {
54          initialize( model, parameters );
55  
56          try
57          {
58              generateStaxSerializer();
59          }
60          catch ( IOException ex )
61          {
62              throw new ModelloException( "Exception while generating StAX serializer.", ex );
63          }
64      }
65  
66      private void generateStaxSerializer()
67          throws ModelloException, IOException
68      {
69          Model objectModel = getModel();
70  
71          String packageName =
72              objectModel.getDefaultPackageName( isPackageWithVersion(), getGeneratedVersion() ) + ".io.stax";
73  
74          String className = "IndentingXMLStreamWriter";
75  
76          JSourceWriter sourceWriter = newJSourceWriter( packageName, className );
77  
78          JClass jClass = new JClass( packageName + '.' + className );
79          jClass.getModifiers().makePackage();
80          jClass.addInterface( "XMLStreamWriter" );
81          initHeader( jClass );
82          suppressAllWarnings( objectModel, jClass );
83  
84          jClass.addImport( "javax.xml.namespace.NamespaceContext" );
85          jClass.addImport( "javax.xml.stream.XMLStreamException" );
86          jClass.addImport( "javax.xml.stream.XMLStreamWriter" );
87  
88          addField( jClass, "XMLStreamWriter", "out", null, false );
89          addField( jClass, "String", "NEW_LINE", "\"\\n\"", true );
90          addField( jClass, "String", "newLine", "NEW_LINE", false );
91          addField( jClass, "String", "indent", "\"  \"", false );
92          addField( jClass, "char[]", "linePrefix", "\"                        \".toCharArray()", false );
93          addField( jClass, "int", "depth", null, false );
94          addField( jClass, "byte[]", "states", "{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }", false );
95          addField( jClass, "int", "ELEMENT_HAS_DATA", "0x1", true );
96          addField( jClass, "int", "ELEMENT_HAS_MARKUP", "0x2", true );
97  
98          JConstructor constructor = jClass.createConstructor();
99          constructor.addParameter( new JParameter( new JType( "XMLStreamWriter" ), "out" ) );
100         constructor.getSourceCode().add( "this.out = out;" );
101 
102         JMethod jMethod;
103         JSourceCode sc;
104 
105         jMethod = new JMethod( "setNewLine" );
106         jMethod.addParameter( new JParameter( new JType( "String" ), "newLine" ) );
107         jMethod.getSourceCode().add( "this.newLine = newLine;" );
108         jClass.addMethod( jMethod );
109 
110         jMethod = new JMethod( "getLineSeparator", new JType( "String" ), null );
111         sc = jMethod.getSourceCode();
112         sc.add( "try" );
113         sc.add( "{" );
114         sc.addIndented( "return System.getProperty( \"line.separator\", NEW_LINE );" );
115         sc.add( "}" );
116         sc.add( "catch ( Exception e )" );
117         sc.add( "{" );
118         sc.addIndented( "return NEW_LINE;" );
119         sc.add( "}" );
120         jClass.addMethod( jMethod );
121 
122         jMethod = new JMethod( "beforeMarkup" );
123         jMethod.getModifiers().makePrivate();
124         sc = jMethod.getSourceCode();
125         sc.add( "int state = states[depth];" );
126         sc.add( "if ( ( state & ELEMENT_HAS_DATA ) == 0 && ( depth > 0 || state != 0 ) )" );
127         sc.add( "{" );
128         sc.indent();
129         sc.add( "newLine( depth );" );
130         sc.add( "if ( depth > 0 && indent.length() > 0 )" );
131         sc.add( "{" );
132         sc.addIndented( "afterMarkup();" );
133         sc.add( "}" );
134         sc.unindent();
135         sc.add( "}" );
136         jClass.addMethod( jMethod );
137 
138         jMethod = new JMethod( "afterMarkup" );
139         jMethod.getModifiers().makePrivate();
140         jMethod.getSourceCode().add( "states[depth] |= ELEMENT_HAS_MARKUP;" );
141         jClass.addMethod( jMethod );
142 
143         jMethod = new JMethod( "beforeStartElement" );
144         jMethod.getModifiers().makePrivate();
145         sc = jMethod.getSourceCode();
146         sc.add( "beforeMarkup();" );
147         sc.add( "if ( states.length <= depth + 1 )" );
148         sc.add( "{" );
149         sc.indent();
150         sc.add( "byte[] tmp = new byte[states.length * 2];" );
151         sc.add( "System.arraycopy( states, 0, tmp, 0, states.length );" );
152         sc.add( "states = tmp;" );
153         sc.unindent();
154         sc.add( "}" );
155         sc.add( "states[depth + 1] = 0;" );
156         jClass.addMethod( jMethod );
157 
158         jMethod = new JMethod( "afterStartElement" );
159         jMethod.getModifiers().makePrivate();
160         sc = jMethod.getSourceCode();
161         sc.add( "afterMarkup();" );
162         sc.add( "depth++;" );
163         jClass.addMethod( jMethod );
164 
165         jMethod = new JMethod( "beforeEndElement" );
166         jMethod.getModifiers().makePrivate();
167         sc = jMethod.getSourceCode();
168         sc.add( "if ( depth > 0 && states[depth] == ELEMENT_HAS_MARKUP )" );
169         sc.add( "{" );
170         sc.addIndented( "newLine( depth - 1 );" );
171         sc.add( "}" );
172         jClass.addMethod( jMethod );
173 
174         jMethod = new JMethod( "afterEndElement" );
175         jMethod.getModifiers().makePrivate();
176         sc = jMethod.getSourceCode();
177         sc.add( "if ( depth > 0 )" );
178         sc.add( "{" );
179         sc.indent();
180         sc.add( "depth--;" );
181         sc.add( "if ( depth <= 0 )" );
182         sc.add( "{" );
183         sc.addIndented( "newLine( 0 );" );
184         sc.add( "}" );
185         sc.unindent();
186         sc.add( "}" );
187         jClass.addMethod( jMethod );
188 
189         jMethod = new JMethod( "afterData" );
190         jMethod.getModifiers().makePrivate();
191         jMethod.getSourceCode().add( "states[depth] |= ELEMENT_HAS_DATA;" );
192         jClass.addMethod( jMethod );
193 
194         jMethod = new JMethod( "newLine" );
195         jMethod.addParameter( new JParameter( JType.INT, "depth" ) );
196         jMethod.getModifiers().makePrivate();
197         sc = jMethod.getSourceCode();
198         sc.add( "try" );
199         sc.add( "{" );
200         sc.indent();
201         sc.add( "out.writeCharacters( newLine );" );
202         sc.add( "int prefixLength = depth * indent.length();" );
203         sc.add( "while ( linePrefix.length < prefixLength )" );
204         sc.add( "{" );
205         sc.indent();
206         sc.add( "char[] tmp = new char[linePrefix.length * 2];" );
207         sc.add( "System.arraycopy( linePrefix, 0, tmp, 0, linePrefix.length );" );
208         sc.add( "System.arraycopy( linePrefix, 0, tmp, linePrefix.length, linePrefix.length );" );
209         sc.add( "linePrefix = tmp;" );
210         sc.unindent();
211         sc.add( "}" );
212         sc.add( "out.writeCharacters( linePrefix, 0, prefixLength );" );
213         sc.unindent();
214         sc.add( "}" );
215         sc.add( "catch ( Exception e )" );
216         sc.add( "{" );
217         sc.add( "}" );
218         jClass.addMethod( jMethod );
219 
220         jMethod = new JMethod( "close" );
221         jMethod.addException( new JClass( "XMLStreamException" ) );
222         jMethod.getSourceCode().add( "out.close();" );
223         jClass.addMethod( jMethod );
224 
225         jMethod = new JMethod( "flush" );
226         jMethod.addException( new JClass( "XMLStreamException" ) );
227         jMethod.getSourceCode().add( "out.flush();" );
228         jClass.addMethod( jMethod );
229 
230         jMethod = new JMethod( "getNamespaceContext", new JType( "NamespaceContext" ), null );
231         jMethod.getSourceCode().add( "return out.getNamespaceContext();" );
232         jClass.addMethod( jMethod );
233 
234         jMethod = new JMethod( "getPrefix", new JType( "String" ), null );
235         jMethod.addException( new JClass( "XMLStreamException" ) );
236         jMethod.addParameter( param( "String", "uri" ) );
237         jMethod.getSourceCode().add( "return out.getPrefix( uri );" );
238         jClass.addMethod( jMethod );
239 
240         jMethod = new JMethod( "getProperty", new JType( "Object" ), null );
241         jMethod.addException( new JClass( "IllegalArgumentException" ) );
242         jMethod.addParameter( param( "String", "name" ) );
243         jMethod.getSourceCode().add( "return out.getProperty( name );" );
244         jClass.addMethod( jMethod );
245 
246         jMethod = new JMethod( "setDefaultNamespace" );
247         jMethod.addException( new JClass( "XMLStreamException" ) );
248         jMethod.addParameter( param( "String", "uri" ) );
249         jMethod.getSourceCode().add( "out.setDefaultNamespace( uri );" );
250         jClass.addMethod( jMethod );
251 
252         jMethod = new JMethod( "setNamespaceContext" );
253         jMethod.addException( new JClass( "XMLStreamException" ) );
254         jMethod.addParameter( param( "NamespaceContext", "context" ) );
255         jMethod.getSourceCode().add( "out.setNamespaceContext( context );" );
256         jClass.addMethod( jMethod );
257 
258         jMethod = new JMethod( "setPrefix" );
259         jMethod.addException( new JClass( "XMLStreamException" ) );
260         jMethod.addParameter( param( "String", "prefix" ) );
261         jMethod.addParameter( param( "String", "uri" ) );
262         jMethod.getSourceCode().add( "out.setPrefix( prefix, uri );" );
263         jClass.addMethod( jMethod );
264 
265         add( jClass, "Attribute", null, null, param( "String", "localName" ), param( "String", "value" ) );
266         add( jClass, "Attribute", null, null, param( "String", "namespaceURI" ), param( "String", "localName" ),
267              param( "String", "value" ) );
268         add( jClass, "Attribute", null, null, param( "String", "prefix" ), param( "String", "namespaceURI" ),
269              param( "String", "localName" ), param( "String", "value" ) );
270 
271         add( jClass, "CData", null, "Data", param( "String", "data" ) );
272 
273         add( jClass, "Characters", null, "Data", param( "String", "text" ) );
274         add( jClass, "Characters", null, "Data", param( "char[]", "text" ), param( "int", "start" ), param( "int",
275                                                                                                             "len" ) );
276 
277         add( jClass, "Comment", "Markup", "Markup", param( "String", "data" ) );
278 
279         add( jClass, "DTD", "Markup", "Markup", param( "String", "dtd" ) );
280 
281         add( jClass, "DefaultNamespace", null, null, param( "String", "namespaceURI" ) );
282 
283         add( jClass, "EmptyElement", "Markup", "Markup", param( "String", "localName" ) );
284         add( jClass, "EmptyElement", "Markup", "Markup", param( "String", "namespaceURI" ), param( "String",
285                                                                                                    "localName" ) );
286         add( jClass, "EmptyElement", "Markup", "Markup", param( "String", "prefix" ),
287              param( "String", "namespaceURI" ), param( "String", "localName" ) );
288 
289         add( jClass, "EndDocument", null, null );
290 
291         add( jClass, "EndElement", "EndElement", "EndElement" );
292 
293         add( jClass, "EntityRef", null, "Data", param( "String", "name" ) );
294 
295         add( jClass, "Namespace", null, null, param( "String", "prefix" ), param( "String", "namespaceURI" ) );
296 
297         add( jClass, "ProcessingInstruction", "Markup", "Markup", param( "String", "target" ) );
298         add( jClass, "ProcessingInstruction", "Markup", "Markup", param( "String", "target" ), param( "String", "data" ) );
299 
300         add( jClass, "StartDocument", "Markup", "Markup" );
301         add( jClass, "StartDocument", "Markup", "Markup", param( "String", "version" ) );
302         add( jClass, "StartDocument", "Markup", "Markup", param( "String", "encoding" ), param( "String", "version" ) );
303 
304         add( jClass, "StartElement", "StartElement", "StartElement", param( "String", "localName" ) );
305         add( jClass, "StartElement", "StartElement", "StartElement", param( "String", "namespaceURI" ),
306              param( "String", "localName" ) );
307         add( jClass, "StartElement", "StartElement", "StartElement", param( "String", "prefix" ), param( "String",
308                                                                                                          "localName" ),
309              param( "String", "namespaceURI" ) );
310 
311         jClass.print( sourceWriter );
312 
313         sourceWriter.close();
314     }
315 
316     private void addField( JClass jClass, String fieldType, String fieldName, String initializer, boolean constant )
317     {
318         JField jField = new JField( new JType( fieldType ), fieldName );
319         jField.setInitString( initializer );
320         if ( constant )
321         {
322             jField.getModifiers().setFinal( true );
323             jField.getModifiers().setStatic( true );
324         }
325         jClass.addField( jField );
326     }
327 
328     private void add( JClass jClass, String name, String before, String after, JParameter... params )
329     {
330         List<String> names = new ArrayList<String>();
331 
332         JMethod jMethod = new JMethod( "write" + name );
333         jMethod.addException( new JClass( "XMLStreamException" ) );
334 
335         for ( JParameter param : params )
336         {
337             jMethod.addParameter( param );
338             names.add( param.getName() );
339         }
340 
341         JSourceCode sc = jMethod.getSourceCode();
342         if ( before != null )
343         {
344             sc.add( "before" + before + "();" );
345         }
346 
347         sc.add( "out.write" + name + "( " + StringUtils.join( names.iterator(), ", " ) + " );" );
348 
349         if ( after != null )
350         {
351             sc.add( "after" + after + "();" );
352         }
353 
354         jClass.addMethod( jMethod );
355     }
356 
357     private static JParameter param( String type, String name )
358     {
359         return new JParameter( new JType( type ), name );
360     }
361 
362 }