1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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;
51 private Stack stack;
52 private StringBuffer attrs;
53 private boolean empty;
54 private boolean closed;
55
56 private String 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
135 private void closeOpeningTag() throws IOException {
136 if (!this.closed) {
137 writeAttributes();
138 this.closed = true;
139 this.writer.write(">");
140 }
141 }
142
143
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
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 }