View Javadoc

1   package com.bradmcevoy.http;
2   
3   import org.apache.commons.io.output.ByteArrayOutputStream;
4   import java.io.FileNotFoundException;
5   import java.io.IOException;
6   import java.io.InputStream;
7   import java.io.OutputStream;
8   import java.io.PrintWriter;
9   import java.io.Writer;
10  
11  import org.slf4j.Logger;
12  import org.slf4j.LoggerFactory;
13  
14  import com.bradmcevoy.io.FileUtils;
15  
16  /**
17   * Lightweight XML generation. Gives the programmer fine grained control
18   * of the generated xml, including whitespace.
19   * <P/>
20   * The XML is not guaranteed to be parseable.
21   *
22   * @author brad
23   */
24  public class XmlWriter {
25  
26      private Logger log = LoggerFactory.getLogger(XmlWriter.class);
27  
28      public enum Type {
29  
30          OPENING,
31          CLOSING,
32          NO_CONTENT
33      };
34      protected final Writer writer;
35  
36      public XmlWriter(OutputStream out) {
37          this.writer = new PrintWriter(out, true);
38      }
39  
40      /**
41       * Append the given raw String to the ouput. No encoding is applied
42       *
43       * @param value
44       */
45      private void append(String value) {
46          try {
47              writer.write(value);
48          } catch (IOException ex) {
49              throw new RuntimeException(ex);
50          }
51      }
52  
53      /**
54       * Append the given character to the output. No encoding is applied
55       *
56       * @param c
57       */
58      private void append(char c) {
59          try {
60              writer.write((int) c);
61          } catch (IOException ex) {
62              throw new RuntimeException(ex);
63          }
64      }
65  
66      /**
67       * Convenience method to write a single element containing a piece of text
68       *
69       *
70       * @param namespace - optional, namespace prefix
71       * @param namespaceInfo - optional, namespace url
72       * @param name - the local name of the element to create
73       * @param value - the raw text to insert into the element
74       */
75      public void writeProperty(String namespace, String namespaceInfo, String name, String value) {
76          writeElement(namespace, namespaceInfo, name, Type.OPENING);
77          append(value);
78          writeElement(namespace, namespaceInfo, name, Type.CLOSING);
79  
80      }
81  
82      public void writeProperty(String namespace, String name, String value) {
83          if (value == null) {
84              writeProperty(namespace, name);
85          } else {
86              writeElement(namespace, name, Type.OPENING);
87              append(value);
88              writeElement(namespace, name, Type.CLOSING);
89          }
90      }
91  
92      public void writeProperty(String namespace, String name) {
93          writeElement(namespace, name, Type.NO_CONTENT);
94      }
95  
96      public void writeProperty(String name) {
97          writeElement(null, name, Type.NO_CONTENT);
98      }
99  
100 
101     public void writeElement(String namespace, String name, Type type) {
102         writeElement(namespace, null, name, type);
103     }
104 
105     /**
106      * Write an opening tag
107      *
108      * @param namespace
109      * @param name
110      */
111     public void open(String namespace, String name) {
112         writeElement(namespace, name, Type.OPENING);
113     }
114 
115     /**
116      * Write a closing tag, Eg </name>
117      *
118      * @param namespace
119      * @param name
120      */
121     public void close(String namespace, String name) {
122         writeElement(namespace, name, Type.CLOSING);
123     }
124 
125     /**
126      * Write an opening tag
127      *
128      * @param name
129      */
130     public void open(String name) {
131         writeElement(null, name, Type.OPENING);
132     }
133 
134     /**
135      * Write a closing tag for the given name
136      *
137      * @param name
138      */
139     public void close(String name) {
140         writeElement(null, name, Type.CLOSING);
141     }
142 
143     /**
144      * Represents an element which is currently being written
145      *
146      */
147     public class Element {
148 
149         private final Element parent;
150         private final String nsPrefix;
151         private final String name;
152         private boolean openEnded;
153 
154         /**
155          * Create the element and write the first part of the opening tag
156          *
157          * Eg <name
158          *
159          * @param name
160          */
161         Element(Element parent, String name) {
162             this(parent, null, name);
163         }
164 
165         /**
166          * Create the element and write the first part of the opening tag
167          *
168          * Eg <name
169          *
170          * @param nsPrefix
171          * @param name
172          */
173         Element(Element parent, String nsPrefix, String name) {
174             this.parent = parent;
175             this.name = name;
176             this.nsPrefix = nsPrefix;
177             append("<");
178             if (nsPrefix != null) {
179                 append(nsPrefix);
180                 append(":");
181             }
182             append(name);
183         }
184 
185         /**
186          * Write a name/value attribute pair
187          *
188          * @param name
189          * @param value
190          * @return
191          */
192         public Element writeAtt(String name, String value) {
193             append(" ");
194             append(name);
195             append("=");
196             append((char) 34);
197             append(value == null ? "" : value);
198             append((char) 34);
199             return this;
200         }
201 
202         /**
203          * Write the text into the element. Will finish the opening tag if required
204          *
205          * @param text
206          * @return
207          */
208         public Element writeText(String text) {
209             return writeText(text, true);
210         }
211         
212         public Element writeText(String text, boolean newline) {
213             if (!openEnded) {
214                 open(newline);
215             }
216             append(text);
217             return this;
218         }
219 
220         /**
221          * Completes the opening tag which is started in the constructor. And
222          * writes a new line
223          *
224          * Eg >
225          *
226          * @return
227          */
228         public Element open() {
229             return open(true);
230         }
231 
232         public Element open(boolean newline) {
233             openEnded = true;
234             append(">");
235             if (newline) {
236                 append("\n");
237             }
238             return this;
239         }
240 
241         /**
242          * Closes the tag by determining its current state. Can close with a no-content
243          * tag </name> if no content has been written, or with write a close tag
244          *
245          * @return - the parent element
246          */
247         public Element close() {
248             return close(false);
249         }
250         public Element close(boolean newline) {
251             if (openEnded) {
252                 if (nsPrefix != null) {
253                     append("</" + nsPrefix + ":" + name + ">\n");
254                 } else {
255                     append("</" + name + ">\n");
256                 }
257                 if (newline) {
258                     append("\n");
259                 }
260                 return parent;
261             } else {
262                 if (newline) {
263                     append("\n");
264                 }
265                 return noContent();
266             }
267         }
268 
269         /**
270          * Write a self closing tag, eg />
271          *
272          * @return - the parent element
273          */
274         public Element noContent() {
275             append("/>\n");
276             return parent;
277         }
278         public Element noContent(boolean newLine) {
279             append("/>");
280             if(newLine) {
281                 append("\n");
282             }
283             return parent;
284         }
285 
286         /**
287          * Start a new element, completing the open tag if required
288          *
289          * @param name
290          * @return
291          */
292         public Element begin(String name) {
293             return begin(null, name);
294         }
295 
296         public Element begin(String prefix, String name) {
297             if (!openEnded) {
298                 open();
299             }
300 
301             Element el = new Element(this, prefix, name);
302             return el;
303         }
304 
305         /**
306          * Write a property element like -
307          * <name>value</name>
308          *
309          * @param name
310          * @param value
311          * @return
312          */
313         public Element prop(String name, String value) {
314             begin(name).writeText(value,false).close(true);
315             return this;
316         }
317 
318         public Element prop(String name, Integer value) {
319             if (value != null) {
320                 prop(name, value.toString());
321             } else {
322                 begin(name).noContent();
323             }
324             return this;
325         }
326     }
327 
328     public Element begin(String name) {
329         Element el = new Element(null, name);
330         return el;
331     }
332 
333     public Element begin(String nsPrefix, String name) {
334         Element el = new Element(null, nsPrefix, name);
335         return el;
336     }
337 
338     public void writeElement(String nsPrefix, String nsUrl, String name, Type type) {
339         if ((nsPrefix != null) && (nsPrefix.length() > 0)) {
340             switch (type) {
341                 case OPENING:
342                     if (nsUrl != null) {
343                         append("<" + nsPrefix + ":" + name + " xmlns:" + nsPrefix + "=\"" + nsUrl + "\">");
344                     } else {
345                         append("<" + nsPrefix + ":" + name + ">");
346                     }
347                     break;
348                 case CLOSING:
349                     append("</" + nsPrefix + ":" + name + ">\n");
350                     break;
351                 case NO_CONTENT:
352                 default:
353                     if (nsUrl != null) {
354                         append("<" + nsPrefix + ":" + name + " xmlns:" + nsPrefix + "=\"" + nsUrl + "\"/>");
355                     } else {
356                         append("<" + nsPrefix + ":" + name + "/>");
357                     }
358                     break;
359             }
360         } else {
361             switch (type) {
362                 case OPENING:
363                     append("<" + name + ">");
364                     break;
365                 case CLOSING:
366                     append("</" + name + ">\n");
367                     break;
368                 case NO_CONTENT:
369                 default:
370                     append("<" + name + "/>");
371                     break;
372             }
373         }
374     }
375 
376     /**
377      * Append plain text.
378      *
379      * @param text Text to append
380      */
381     public void writeText(String text) {
382         append(text);
383     }
384 
385     /**
386      * Write a CDATA segment.
387      *
388      * @param data Data to append
389      */
390     public void writeData(String data) {
391         append("<![CDATA[" + data + "]]>");
392     }
393 
394     public void writeXMLHeader() {
395         append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
396     }
397 
398     /**
399      * Send data and reinitializes buffer.
400      */
401     public void flush() {
402         try {
403             writer.flush();
404         } catch (IOException ex) {
405             throw new RuntimeException(ex);
406         }
407     }
408 
409     public void sample(InputStream in) {
410         log.debug("outputting sample");
411         try {
412             ByteArrayOutputStream out = FileUtils.readIn(in);
413             writer.write(out.toString());
414         } catch (FileNotFoundException ex) {
415             log.error("", ex);
416         } catch (IOException ex) {
417             log.error("", ex);
418         } finally {
419             FileUtils.close(in);
420         }
421     }
422 
423     public void newLine() {
424         append("\n");
425     }
426 }