1
20 package org.crosswire.common.xml;
21
22 import java.io.IOException;
23 import java.io.StringWriter;
24 import java.io.Writer;
25
26 import org.xml.sax.Attributes;
27 import org.xml.sax.ContentHandler;
28 import org.xml.sax.Locator;
29
30
37 public class PrettySerializingContentHandler implements ContentHandler {
38
42 public PrettySerializingContentHandler() {
43 this(FormatType.AS_IS);
44 }
45
46
54 public PrettySerializingContentHandler(FormatType theFormat) {
55 this(theFormat, null);
56 }
57
58
68 public PrettySerializingContentHandler(FormatType theFormat, Writer theWriter) {
69 formatting = theFormat;
70 writer = theWriter == null ? new StringWriter() : theWriter;
71 }
72
73
78 @Override
79 public String toString() {
80 return writer.toString();
81 }
82
83
88 public void setDocumentLocator(Locator locator) {
89 }
90
91
96 public void startDocument() {
97 }
99
100
105 public void endDocument() {
106 }
107
108
114 public void startPrefixMapping(String prefix, String uri) {
115 }
116
117
122 public void endPrefixMapping(String prefix) {
123 }
124
125
131 public void startElement(String uri, String localname, String qname, Attributes attrs) {
132 if (depth > 0) {
133 handlePending();
134 }
135
136 write(getTagStart());
137 write(decorateTagName(localname));
138
139 for (int i = 0; i < attrs.getLength(); i++) {
140 write(' ');
141 write(decorateAttributeName(XMLUtil.getAttributeName(attrs, i)));
142 write("='");
143 write(decorateAttributeValue(XMLUtil.escape(attrs.getValue(i))));
144 write('\'');
145 }
146
147 pendingEndTag = true;
148 depth++;
149 }
150
151
157 public void endElement(String uri, String localname, String qname) {
158 depth--;
159 if (pendingEndTag) {
162 if (formatting.isAnalytic() && depth > 0) {
163 emitWhitespace(depth - 1);
164 }
165 write(getTagEnd());
170 }
176 if (formatting.isClassic()) {
179 emitWhitespace(depth);
180 }
181
182 write(getEndTagStart());
183
184 write(decorateTagName(localname));
185
186 if (formatting.isAnalytic()) {
187 emitWhitespace(depth);
188 }
189
190 write(getTagEnd());
191 pendingEndTag = false;
193 lookingForChars = false;
194 }
195
196
201 public void characters(char[] chars, int start, int length) {
202 if (!lookingForChars) {
203 handlePending();
204 }
205
206 String s = new String(chars, start, length);
207 write(decorateCharacters(s));
208 lookingForChars = true;
209 }
210
211
216 public void ignorableWhitespace(char[] chars, int start, int length) {
217 characters(chars, start, length);
218 }
219
220
226 public void processingInstruction(String target, String data) {
227 handlePending();
228
229 write(getPIStart());
230 write(target);
231 write(' ');
232 write(decorateCharacters(data));
233 write(getPIEnd());
234
235 if (formatting.isMultiline()) {
236 write(getNewline());
237 }
238 }
239
240
245 public void skippedEntity(String name) {
246 }
247
248 protected String getTagStart() {
249 return "<";
250 }
251
252 protected String getTagEnd() {
253 return ">";
254 }
255
256 protected String getEmptyTagEnd() {
257 return "/>";
258 }
259
260 protected String getEndTagStart() {
261 return "</";
262 }
263
264 protected String getPIStart() {
265 return "<!";
266 }
267
268 protected String getPIEnd() {
269 return "!>";
270 }
271
272 protected String getNewline() {
273 return "\n";
274 }
275
276 protected String decorateTagName(String tagName) {
277 return tagName;
278 }
279
280 protected String decorateAttributeName(String attrName) {
281 return attrName;
282 }
283
284 protected String decorateAttributeValue(String attrValue) {
285 return attrValue;
286 }
287
288 protected String decorateCharacters(String characters) {
289 return characters;
290 }
291
292 protected String decorateIndent(int indentLevel) {
293 return new String(indentation, 0, indentLevel).intern();
294 }
295
296 protected void write(String obj) {
297 try {
298 writer.write(obj);
299 } catch (IOException e) {
300 e.printStackTrace(System.err);
301 }
302 }
303
304 protected void write(char obj) {
305 try {
306 writer.write(obj);
307 } catch (IOException e) {
308 e.printStackTrace(System.err);
309 }
310 }
311
312 private void handlePending() {
313 if (pendingEndTag) {
314 pendingEndTag = false;
315
316 if (formatting.isAnalytic()) {
317 emitWhitespace(depth);
318 }
319
320 write(getTagEnd());
321
322 }
323 if (formatting.isClassic()) {
324 emitWhitespace(depth);
325 }
326 lookingForChars = false;
327 }
328
329 private void emitWhitespace(int indentLevel) {
330 write(getNewline());
331 if (formatting.isIndented()) {
332 write(decorateIndent(indentLevel));
333 }
334 }
335
336
339 private static char[] indentation = {
340 '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t',
341 '\t', '\t', '\t', '\t', '\t', '\t',
342 };
343
344
348 private int depth;
349
350
355 private boolean lookingForChars;
356
357
362 private boolean pendingEndTag;
363
364 private FormatType formatting;
365
366 private Writer writer;
367 }
368