View Javadoc

1   /*
2    * Copyright (c) 2003, Henri Yandell
3    * All rights reserved.
4    * 
5    * Redistribution and use in source and binary forms, with or 
6    * without modification, are permitted provided that the 
7    * following conditions are met:
8    * 
9    * + Redistributions of source code must retain the above copyright notice, 
10   *   this list of conditions and the following disclaimer.
11   * 
12   * + Redistributions in binary form must reproduce the above copyright notice, 
13   *   this list of conditions and the following disclaimer in the documentation 
14   *   and/or other materials provided with the distribution.
15   * 
16   * + Neither the name of XmlWriter nor the names of its contributors 
17   *   may be used to endorse or promote products derived from this software 
18   *   without specific prior written permission.
19   * 
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
23   * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
30   * POSSIBILITY OF SUCH DAMAGE.
31   */
32  package com.generationjava.io.xml;
33  
34  import java.io.IOException;
35  import java.io.Writer;
36  
37  import java.util.Stack;
38  
39  /***
40   * Makes writing XML much much easier. 
41   * Improved from 
42   * <a href="http://builder.com.com/article.jhtml?id=u00220020318yan01.htm&page=1&vf=tt">article</a>
43   *
44   * @author <a href="mailto:bayard@apache.org">Henri Yandell</a>
45   * @author <a href="mailto:pete@fingertipsoft.com">Peter Cassetta</a>
46   * @version 1.0
47   */
48  public class SimpleXmlWriter extends AbstractXmlWriter {
49  
50      private Writer writer;      // underlying writer
51      private Stack stack;        // of xml entity names
52      private StringBuffer attrs; // current attribute string
53      private boolean empty;      // is the current node empty
54      private boolean closed;     // is the current node closed...
55  
56      private String namespace;   // the current default namespace
57  
58      /***
59       * Create an SimpleXmlWriter on top of an existing java.io.Writer.
60       */
61      public SimpleXmlWriter(Writer writer) {
62          this.writer = writer;
63          this.closed = true;
64          this.stack = new Stack();
65      }
66  
67      /***
68       * The default namespace. Once this is turned on, any new entities 
69       * will have this namespace, regardless of scope.
70       *
71       * @param String nname of the namespace
72       */
73      public void setDefaultNamespace(String namespace) {
74          this.namespace = namespace;
75      }
76  
77      public String getDefaultNamespace() {
78          if(this.namespace == null) {
79              return "";
80          } else {
81              return this.namespace;
82          }
83      }
84  
85      /***
86       * Output the version, encoding and standalone nature of an xml file.
87       */
88      public XmlWriter writeXmlVersion(String version, String encoding, String standalone) throws IOException {
89          this.writer.write("<?xml version=\"");
90          this.writer.write(version);
91          if(encoding != null) {
92              this.writer.write("\" encoding=\"");
93              this.writer.write(encoding);
94          }
95          if(standalone != null) {
96              this.writer.write("\" standalone=\"");
97              this.writer.write(standalone);
98          }
99          this.writer.write("\"?>");
100         return this;
101     }
102 
103     /***
104      * Begin to write out an entity. Unlike the helper tags, this tag 
105      * will need to be ended with the endEntity method.
106      *
107      * @param name String name of tag
108      */
109     public XmlWriter writeEntity(String name) throws IOException {
110         
111         if(this.namespace == null) {
112             return openEntity(name);
113         } else {
114             return openEntity(this.namespace+":"+name);
115         }
116     }
117 
118     /***
119      * Begin to output an entity. 
120      *
121      * @param String name of entity.
122      */
123     private SimpleXmlWriter openEntity(String name) throws IOException {
124         boolean wasClosed = this.closed;
125         closeOpeningTag();
126         this.closed = false;
127         this.writer.write("<");
128         this.writer.write(name);
129         stack.add(name);
130         this.empty = true;
131         return this;
132     }
133 
134     // close off the opening tag
135     private void closeOpeningTag() throws IOException {
136         if (!this.closed) {
137             writeAttributes();
138             this.closed = true;
139             this.writer.write(">");
140         }
141     }
142 
143     // write out all current attributes
144     private void writeAttributes() throws IOException {
145         if (this.attrs != null) {
146             this.writer.write(this.attrs.toString());
147             this.attrs.setLength(0);
148             this.empty = false;
149         }
150     }
151 
152     /***
153      * Write an attribute out for the current entity. 
154      * Any xml characters in the value are escaped.
155      * Currently it does not actually throw the exception, but 
156      * the api is set that way for future changes.
157      *
158      * @param String name of attribute.
159      * @param Object value of attribute.
160      */
161     public XmlWriter writeAttribute(String attr, Object value) throws IOException {
162 
163         // maintain api
164         if (false) throw new IOException();
165 
166         if (this.attrs == null) {
167             this.attrs = new StringBuffer();
168         }
169         this.attrs.append(" ");
170         this.attrs.append(attr);
171         this.attrs.append("=\"");
172         this.attrs.append(XmlUtils.escapeXml(""+value));
173         this.attrs.append("\"");
174         return this;
175     }
176 
177     /***
178      * End the current entity. This will throw an exception 
179      * if it is called when there is not a currently open 
180      * entity.
181      */
182     public XmlWriter endEntity() throws IOException {
183         if(this.stack.empty()) {
184             throw new IOException("Called endEntity too many times. ");
185         }
186         String name = (String)this.stack.pop();
187         if (name != null) {
188             if (this.empty) {
189                 writeAttributes();
190                 this.writer.write("/>");
191             } else {
192                 this.writer.write("</");
193                 this.writer.write(name);
194                 this.writer.write(">");
195             }
196             this.empty = false;
197             this.closed = true;
198         }
199         return this;
200     }
201 
202     /***
203      * Close this writer. It does not close the underlying 
204      * writer, but does flush it and throw an exception if 
205      * there are as yet unclosed tags.
206      */
207     public void close() throws IOException {
208         this.writer.flush();
209         if(!this.stack.empty()) {
210             throw new IOException("Tags are not all closed. "+
211                 "Possibly, "+this.stack.pop()+" is unclosed. ");
212         }
213     }
214 
215     /***
216      * Output body text. Any xml characters are escaped. 
217      */
218     public XmlWriter writeText(Object text) throws IOException {
219         
220         closeOpeningTag();
221         this.empty = false;
222         this.writer.write(XmlUtils.escapeXml(""+text));
223         return this;
224     }
225 
226     /***
227      * Write out a chunk of CDATA. This helper method surrounds the 
228      * passed in data with the CDATA tag.
229      *
230      * @param String of CDATA text.
231      */
232     public XmlWriter writeCData(String cdata) throws IOException {
233         
234         writeChunk("<![CDATA[ "+cdata+" ]]>");
235         return this;
236     }
237 
238     /***
239      * Write out a chunk of comment. This helper method surrounds the 
240      * passed in data with the xml comment tag.
241      *
242      * @param String of text to comment.
243      */
244     public XmlWriter writeComment(String comment) throws IOException {
245         
246         writeChunk("<!--"+comment+"-->");
247         return this;
248     }
249     private void writeChunk(String data) throws IOException {
250         closeOpeningTag();
251         this.empty = false;
252         this.writer.write(data);
253     }
254 
255     public Writer getWriter() {
256         return this.writer;
257     }
258 
259 }