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