001: /*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 1999 The Apache Software Foundation. All rights
006: * reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Apache Software Foundation (http://www.apache.org/)."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The names "Xerces" and "Apache Software Foundation" must
028: * not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact apache@apache.org.
031: *
032: * 5. Products derived from this software may not be called "Apache",
033: * nor may "Apache" appear in their name, without prior written
034: * permission of the Apache Software Foundation.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This software consists of voluntary contributions made by many
051: * individuals on behalf of the Apache Software Foundation and was
052: * originally based on software copyright (c) 1999, International
053: * Business Machines, Inc., http://www.apache.org. For more
054: * information on the Apache Software Foundation, please see
055: * <http://www.apache.org/>.
056: */
057:
058: package org.apache.xml.serialize;
059:
060: import java.io.Writer;
061: import java.io.StringWriter;
062: import java.io.IOException;
063:
064: /**
065: * Extends {@link Printer} and adds support for indentation and line
066: * wrapping.
067: *
068: * @version $Revision: 1.4 $ $Date: 2001/07/20 20:37:06 $
069: * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a>
070: */
071: public class IndentPrinter extends Printer {
072:
073: /**
074: * Holds the currently accumulating text line. This buffer will constantly
075: * be reused by deleting its contents instead of reallocating it.
076: */
077: private StringBuffer _line;
078:
079: /**
080: * Holds the currently accumulating text that follows {@link #_line}.
081: * When the end of the part is identified by a call to {@link #printSpace}
082: * or {@link #breakLine}, this part is added to the accumulated line.
083: */
084: private StringBuffer _text;
085:
086: /**
087: * Counts how many white spaces come between the accumulated line and the
088: * current accumulated text. Multiple spaces at the end of the a line
089: * will not be printed.
090: */
091: private int _spaces;
092:
093: /**
094: * Holds the indentation for the current line that is now accumulating in
095: * memory and will be sent for printing shortly.
096: */
097: private int _this Indent;
098:
099: /**
100: * Holds the indentation for the next line to be printed. After this line is
101: * printed, {@link #_nextIndent} is assigned to {@link #_thisIndent}.
102: */
103: private int _nextIndent;
104:
105: IndentPrinter(Writer writer, OutputFormat format) {
106: super (writer, format);
107: // Initialize everything for a first/second run.
108: _line = new StringBuffer(80);
109: _text = new StringBuffer(20);
110: _spaces = 0;
111: _this Indent = _nextIndent = 0;
112: }
113:
114: /**
115: * Called by any of the DTD handlers to enter DTD mode.
116: * Once entered, all output will be accumulated in a string
117: * that can be printed as part of the document's DTD.
118: * This method may be called any number of time but will only
119: * have affect the first time it's called. To exist DTD state
120: * and get the accumulated DTD, call {@link #leaveDTD}.
121: */
122: public void enterDTD() {
123: // Can only enter DTD state once. Once we're out of DTD
124: // state, can no longer re-enter it.
125: if (_dtdWriter == null) {
126: _line.append(_text);
127: _text = new StringBuffer(20);
128: flushLine(false);
129: _dtdWriter = new StringWriter();
130: _docWriter = _writer;
131: _writer = _dtdWriter;
132: }
133: }
134:
135: /**
136: * Called by the root element to leave DTD mode and if any
137: * DTD parts were printer, will return a string with their
138: * textual content.
139: */
140: public String leaveDTD() {
141: // Only works if we're going out of DTD mode.
142: if (_writer == _dtdWriter) {
143: _line.append(_text);
144: _text = new StringBuffer(20);
145: flushLine(false);
146: _writer = _docWriter;
147: return _dtdWriter.toString();
148: } else
149: return null;
150: }
151:
152: /**
153: * Called to print additional text. Each time this method is called
154: * it accumulates more text. When a space is printed ({@link
155: * #printSpace}) all the accumulated text becomes one part and is
156: * added to the accumulate line. When a line is long enough, it can
157: * be broken at its text boundary.
158: *
159: * @param text The text to print
160: */
161: public void printText(String text) {
162: _text.append(text);
163: }
164:
165: public void printText(StringBuffer text) {
166: _text.append(text);
167: }
168:
169: public void printText(char ch) {
170: _text.append(ch);
171: }
172:
173: public void printText(char[] chars, int start, int length) {
174: _text.append(chars, start, length);
175: }
176:
177: /**
178: * Called to print a single space between text parts that may be
179: * broken into separate lines. Must not be called to print a space
180: * when preserving spaces. The text accumulated so far with {@link
181: * #printText} will be added to the accumulated line, and a space
182: * separator will be counted. If the line accumulated so far is
183: * long enough, it will be printed.
184: */
185: public void printSpace() {
186: // The line consists of the text accumulated in _line,
187: // followed by one or more spaces as counted by _spaces,
188: // followed by more space accumulated in _text:
189: // - Text is printed and accumulated into _text.
190: // - A space is printed, so _text is added to _line and
191: // a space is counted.
192: // - More text is printed and accumulated into _text.
193: // - A space is printed, the previous spaces are added
194: // to _line, the _text is added to _line, and a new
195: // space is counted.
196:
197: // If text was accumulated with printText(), then the space
198: // means we have to move that text into the line and
199: // start accumulating new text with printText().
200: if (_text.length() > 0) {
201: // If the text breaks a line bounary, wrap to the next line.
202: // The printed line size consists of the indentation we're going
203: // to use next, the accumulated line so far, some spaces and the
204: // accumulated text so far.
205: if (_format.getLineWidth() > 0
206: && _this Indent + _line.length() + _spaces
207: + _text.length() > _format.getLineWidth()) {
208: flushLine(false);
209: try {
210: // Print line and new line, then zero the line contents.
211: _writer.write(_format.getLineSeparator());
212: } catch (IOException except) {
213: // We don't throw an exception, but hold it
214: // until the end of the document.
215: if (_exception == null)
216: _exception = except;
217: }
218: }
219:
220: // Add as many spaces as we accumulaed before.
221: // At the end of this loop, _spaces is zero.
222: while (_spaces > 0) {
223: _line.append(' ');
224: --_spaces;
225: }
226: _line.append(_text);
227: _text = new StringBuffer(20);
228: }
229: // Starting a new word: accumulate the text between the line
230: // and this new word; not a new word: just add another space.
231: ++_spaces;
232: }
233:
234: /**
235: * Called to print a line consisting of the text accumulated so
236: * far. This is equivalent to calling {@link #printSpace} but
237: * forcing the line to print and starting a new line ({@link
238: * #printSpace} will only start a new line if the current line
239: * is long enough).
240: */
241: public void breakLine() {
242: breakLine(false);
243: }
244:
245: public void breakLine(boolean preserveSpace) {
246: // Equivalent to calling printSpace and forcing a flushLine.
247: if (_text.length() > 0) {
248: while (_spaces > 0) {
249: _line.append(' ');
250: --_spaces;
251: }
252: _line.append(_text);
253: _text = new StringBuffer(20);
254: }
255: flushLine(preserveSpace);
256: try {
257: // Print line and new line, then zero the line contents.
258: _writer.write(_format.getLineSeparator());
259: } catch (IOException except) {
260: // We don't throw an exception, but hold it
261: // until the end of the document.
262: if (_exception == null)
263: _exception = except;
264: }
265: }
266:
267: /**
268: * Flushes the line accumulated so far to the writer and get ready
269: * to accumulate the next line. This method is called by {@link
270: * #printText} and {@link #printSpace} when the accumulated line plus
271: * accumulated text are two long to fit on a given line. At the end of
272: * this method {@link #_line} is empty and {@link #_spaces} is zero.
273: */
274: public void flushLine(boolean preserveSpace) {
275: int indent;
276:
277: if (_line.length() > 0) {
278: try {
279:
280: if (_format.getIndenting() && !preserveSpace) {
281: // Make sure the indentation does not blow us away.
282: indent = _this Indent;
283: if ((2 * indent) > _format.getLineWidth()
284: && _format.getLineWidth() > 0)
285: indent = _format.getLineWidth() / 2;
286: // Print the indentation as spaces and set the current
287: // indentation to the next expected indentation.
288: while (indent > 0) {
289: _writer.write(' ');
290: --indent;
291: }
292: }
293: _this Indent = _nextIndent;
294:
295: // There is no need to print the spaces at the end of the line,
296: // they are simply stripped and replaced with a single line
297: // separator.
298: _spaces = 0;
299: _writer.write(_line.toString());
300:
301: _line = new StringBuffer(40);
302: } catch (IOException except) {
303: // We don't throw an exception, but hold it
304: // until the end of the document.
305: if (_exception == null)
306: _exception = except;
307: }
308: }
309: }
310:
311: /**
312: * Flush the output stream. Must be called when done printing
313: * the document, otherwise some text might be buffered.
314: */
315: public void flush() {
316: if (_line.length() > 0 || _text.length() > 0)
317: breakLine();
318: try {
319: _writer.flush();
320: } catch (IOException except) {
321: // We don't throw an exception, but hold it
322: // until the end of the document.
323: if (_exception == null)
324: _exception = except;
325: }
326: }
327:
328: /**
329: * Increment the indentation for the next line.
330: */
331: public void indent() {
332: _nextIndent += _format.getIndent();
333: }
334:
335: /**
336: * Decrement the indentation for the next line.
337: */
338: public void unindent() {
339: _nextIndent -= _format.getIndent();
340: if (_nextIndent < 0)
341: _nextIndent = 0;
342: // If there is no current line and we're de-identing then
343: // this indentation level is actually the next level.
344: if ((_line.length() + _spaces + _text.length()) == 0)
345: _this Indent = _nextIndent;
346: }
347:
348: public int getNextIndent() {
349: return _nextIndent;
350: }
351:
352: public void setNextIndent(int indent) {
353: _nextIndent = indent;
354: }
355:
356: public void setThisIndent(int indent) {
357: _thisIndent = indent;
358: }
359:
360: }
|