001: package com.sun.xml.txw2.output;
002:
003: import org.xml.sax.Attributes;
004: import org.xml.sax.ContentHandler;
005: import org.xml.sax.SAXException;
006: import org.xml.sax.ext.LexicalHandler;
007: import org.xml.sax.helpers.XMLFilterImpl;
008:
009: import java.util.Stack;
010:
011: /**
012: * {@link XMLFilterImpl} that does indentation to SAX events.
013: *
014: * @author Kohsuke Kawaguchi
015: */
016: public class IndentingXMLFilter extends XMLFilterImpl implements
017: LexicalHandler {
018: private LexicalHandler lexical;
019:
020: public IndentingXMLFilter() {
021: }
022:
023: public IndentingXMLFilter(ContentHandler handler) {
024: setContentHandler(handler);
025: }
026:
027: public IndentingXMLFilter(ContentHandler handler,
028: LexicalHandler lexical) {
029: setContentHandler(handler);
030: setLexicalHandler(lexical);
031: }
032:
033: public LexicalHandler getLexicalHandler() {
034: return lexical;
035: }
036:
037: public void setLexicalHandler(LexicalHandler lexical) {
038: this .lexical = lexical;
039: }
040:
041: /**
042: * Return the current indent step.
043: *
044: * <p>Return the current indent step: each start tag will be
045: * indented by this number of spaces times the number of
046: * ancestors that the element has.</p>
047: *
048: * @return The number of spaces in each indentation step,
049: * or 0 or less for no indentation.
050: * @see #setIndentStep(int)
051: *
052: * @deprecated
053: * Only return the length of the indent string.
054: */
055: public int getIndentStep() {
056: return indentStep.length();
057: }
058:
059: /**
060: * Set the current indent step.
061: *
062: * @param indentStep The new indent step (0 or less for no
063: * indentation).
064: * @see #getIndentStep()
065: *
066: * @deprecated
067: * Should use the version that takes string.
068: */
069: public void setIndentStep(int indentStep) {
070: StringBuilder s = new StringBuilder();
071: for (; indentStep > 0; indentStep--)
072: s.append(' ');
073: setIndentStep(s.toString());
074: }
075:
076: public void setIndentStep(String s) {
077: this .indentStep = s;
078: }
079:
080: ////////////////////////////////////////////////////////////////////
081: // Override methods from XMLWriter.
082: ////////////////////////////////////////////////////////////////////
083:
084: /**
085: * Write a start tag.
086: *
087: * <p>Each tag will begin on a new line, and will be
088: * indented by the current indent step times the number
089: * of ancestors that the element has.</p>
090: *
091: * <p>The newline and indentation will be passed on down
092: * the filter chain through regular characters events.</p>
093: *
094: * @param uri The element's Namespace URI.
095: * @param localName The element's local name.
096: * @param qName The element's qualified (prefixed) name.
097: * @param atts The element's attribute list.
098: * @exception org.xml.sax.SAXException If there is an error
099: * writing the start tag, or if a filter further
100: * down the chain raises an exception.
101: * @see XMLWriter#startElement(String, String, String,Attributes)
102: */
103: public void startElement(String uri, String localName,
104: String qName, Attributes atts) throws SAXException {
105: stateStack.push(SEEN_ELEMENT);
106: state = SEEN_NOTHING;
107: if (depth > 0) {
108: writeNewLine();
109: }
110: doIndent();
111: super .startElement(uri, localName, qName, atts);
112: depth++;
113: }
114:
115: private void writeNewLine() throws SAXException {
116: super .characters(NEWLINE, 0, NEWLINE.length);
117: }
118:
119: private static final char[] NEWLINE = { '\n' };
120:
121: /**
122: * Write an end tag.
123: *
124: * <p>If the element has contained other elements, the tag
125: * will appear indented on a new line; otherwise, it will
126: * appear immediately following whatever came before.</p>
127: *
128: * <p>The newline and indentation will be passed on down
129: * the filter chain through regular characters events.</p>
130: *
131: * @param uri The element's Namespace URI.
132: * @param localName The element's local name.
133: * @param qName The element's qualified (prefixed) name.
134: * @exception org.xml.sax.SAXException If there is an error
135: * writing the end tag, or if a filter further
136: * down the chain raises an exception.
137: * @see XMLWriter#endElement(String, String, String)
138: */
139: public void endElement(String uri, String localName, String qName)
140: throws SAXException {
141: depth--;
142: if (state == SEEN_ELEMENT) {
143: writeNewLine();
144: doIndent();
145: }
146: super .endElement(uri, localName, qName);
147: state = stateStack.pop();
148: }
149:
150: // /**
151: // * Write a empty element tag.
152: // *
153: // * <p>Each tag will appear on a new line, and will be
154: // * indented by the current indent step times the number
155: // * of ancestors that the element has.</p>
156: // *
157: // * <p>The newline and indentation will be passed on down
158: // * the filter chain through regular characters events.</p>
159: // *
160: // * @param uri The element's Namespace URI.
161: // * @param localName The element's local name.
162: // * @param qName The element's qualified (prefixed) name.
163: // * @param atts The element's attribute list.
164: // * @exception org.xml.sax.SAXException If there is an error
165: // * writing the empty tag, or if a filter further
166: // * down the chain raises an exception.
167: // * @see XMLWriter#emptyElement(String, String, String, Attributes)
168: // */
169: // public void emptyElement (String uri, String localName,
170: // String qName, Attributes atts)
171: // throws SAXException
172: // {
173: // state = SEEN_ELEMENT;
174: // if (depth > 0) {
175: // super.characters("\n");
176: // }
177: // doIndent();
178: // super.emptyElement(uri, localName, qName, atts);
179: // }
180:
181: /**
182: * Write a sequence of characters.
183: *
184: * @param ch The characters to write.
185: * @param start The starting position in the array.
186: * @param length The number of characters to use.
187: * @exception org.xml.sax.SAXException If there is an error
188: * writing the characters, or if a filter further
189: * down the chain raises an exception.
190: * @see XMLWriter#characters(char[], int, int)
191: */
192: public void characters(char ch[], int start, int length)
193: throws SAXException {
194: state = SEEN_DATA;
195: super .characters(ch, start, length);
196: }
197:
198: public void comment(char ch[], int start, int length)
199: throws SAXException {
200: if (depth > 0) {
201: writeNewLine();
202: }
203: doIndent();
204: if (lexical != null)
205: lexical.comment(ch, start, length);
206: }
207:
208: public void startDTD(String name, String publicId, String systemId)
209: throws SAXException {
210: if (lexical != null)
211: lexical.startDTD(name, publicId, systemId);
212: }
213:
214: public void endDTD() throws SAXException {
215: if (lexical != null)
216: lexical.endDTD();
217: }
218:
219: public void startEntity(String name) throws SAXException {
220: if (lexical != null)
221: lexical.startEntity(name);
222: }
223:
224: public void endEntity(String name) throws SAXException {
225: if (lexical != null)
226: lexical.endEntity(name);
227: }
228:
229: public void startCDATA() throws SAXException {
230: if (lexical != null)
231: lexical.startCDATA();
232: }
233:
234: public void endCDATA() throws SAXException {
235: if (lexical != null)
236: lexical.endCDATA();
237: }
238:
239: ////////////////////////////////////////////////////////////////////
240: // Internal methods.
241: ////////////////////////////////////////////////////////////////////
242:
243: /**
244: * Print indentation for the current level.
245: *
246: * @exception org.xml.sax.SAXException If there is an error
247: * writing the indentation characters, or if a filter
248: * further down the chain raises an exception.
249: */
250: private void doIndent() throws SAXException {
251: if (depth > 0) {
252: char[] ch = indentStep.toCharArray();
253: for (int i = 0; i < depth; i++)
254: characters(ch, 0, ch.length);
255: }
256: }
257:
258: ////////////////////////////////////////////////////////////////////
259: // Constants.
260: ////////////////////////////////////////////////////////////////////
261:
262: private final static Object SEEN_NOTHING = new Object();
263: private final static Object SEEN_ELEMENT = new Object();
264: private final static Object SEEN_DATA = new Object();
265:
266: ////////////////////////////////////////////////////////////////////
267: // Internal state.
268: ////////////////////////////////////////////////////////////////////
269:
270: private Object state = SEEN_NOTHING;
271: private Stack<Object> stateStack = new Stack<Object>();
272:
273: private String indentStep = "";
274: private int depth = 0;
275: }
|