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