001: /*
002: * Copyright 1999-2002,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.jasig.portal.serialize;
018:
019: import java.io.Writer;
020: import java.io.StringWriter;
021: import java.io.IOException;
022:
023: /**
024: * Extends {@link Printer} and adds support for indentation and line
025: * wrapping.
026: *
027: * @version $Revision: 36559 $ $Date: 2006-04-28 11:38:13 -0700 (Fri, 28 Apr 2006) $
028: * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a>
029: */
030: public class IndentPrinter extends Printer {
031:
032: /**
033: * Holds the currently accumulating text line. This buffer will constantly
034: * be reused by deleting its contents instead of reallocating it.
035: */
036: private StringBuffer _line;
037:
038: /**
039: * Holds the currently accumulating text that follows {@link #_line}.
040: * When the end of the part is identified by a call to {@link #printSpace}
041: * or {@link #breakLine}, this part is added to the accumulated line.
042: */
043: private StringBuffer _text;
044:
045: /**
046: * Counts how many white spaces come between the accumulated line and the
047: * current accumulated text. Multiple spaces at the end of the a line
048: * will not be printed.
049: */
050: private int _spaces;
051:
052: /**
053: * Holds the indentation for the current line that is now accumulating in
054: * memory and will be sent for printing shortly.
055: */
056: private int _this Indent;
057:
058: /**
059: * Holds the indentation for the next line to be printed. After this line is
060: * printed, {@link #_nextIndent} is assigned to {@link #_thisIndent}.
061: */
062: private int _nextIndent;
063:
064: public IndentPrinter(Writer writer, OutputFormat format) {
065: super (writer, format);
066: // Initialize everything for a first/second run.
067: _line = new StringBuffer(80);
068: _text = new StringBuffer(20);
069: _spaces = 0;
070: _this Indent = _nextIndent = 0;
071: }
072:
073: /**
074: * Called by any of the DTD handlers to enter DTD mode.
075: * Once entered, all output will be accumulated in a string
076: * that can be printed as part of the document's DTD.
077: * This method may be called any number of time but will only
078: * have affect the first time it's called. To exist DTD state
079: * and get the accumulated DTD, call {@link #leaveDTD}.
080: */
081: public void enterDTD() {
082: // Can only enter DTD state once. Once we're out of DTD
083: // state, can no longer re-enter it.
084: if (_dtdWriter == null) {
085: _line.append(_text);
086: _text = new StringBuffer(20);
087: flushLine(false);
088: _dtdWriter = new StringWriter();
089: _docWriter = _writer;
090: _writer = _dtdWriter;
091: }
092: }
093:
094: /**
095: * Called by the root element to leave DTD mode and if any
096: * DTD parts were printer, will return a string with their
097: * textual content.
098: */
099: public String leaveDTD() {
100: // Only works if we're going out of DTD mode.
101: if (_writer == _dtdWriter) {
102: _line.append(_text);
103: _text = new StringBuffer(20);
104: flushLine(false);
105: _writer = _docWriter;
106: return _dtdWriter.toString();
107: } else
108: return null;
109: }
110:
111: /**
112: * Called to print additional text. Each time this method is called
113: * it accumulates more text. When a space is printed ({@link
114: * #printSpace}) all the accumulated text becomes one part and is
115: * added to the accumulate line. When a line is long enough, it can
116: * be broken at its text boundary.
117: *
118: * @param text The text to print
119: */
120: public void printText(String text) {
121: _text.append(text);
122: }
123:
124: public void printText(StringBuffer text) {
125: _text.append(text.toString());
126: }
127:
128: public void printText(char ch) {
129: _text.append(ch);
130: }
131:
132: public void printText(char[] chars, int start, int length) {
133: _text.append(chars, start, length);
134: }
135:
136: /**
137: * Called to print a single space between text parts that may be
138: * broken into separate lines. Must not be called to print a space
139: * when preserving spaces. The text accumulated so far with {@link
140: * #printText} will be added to the accumulated line, and a space
141: * separator will be counted. If the line accumulated so far is
142: * long enough, it will be printed.
143: */
144: public void printSpace() {
145: // The line consists of the text accumulated in _line,
146: // followed by one or more spaces as counted by _spaces,
147: // followed by more space accumulated in _text:
148: // - Text is printed and accumulated into _text.
149: // - A space is printed, so _text is added to _line and
150: // a space is counted.
151: // - More text is printed and accumulated into _text.
152: // - A space is printed, the previous spaces are added
153: // to _line, the _text is added to _line, and a new
154: // space is counted.
155:
156: // If text was accumulated with printText(), then the space
157: // means we have to move that text into the line and
158: // start accumulating new text with printText().
159: if (_text.length() > 0) {
160: // If the text breaks a line bounary, wrap to the next line.
161: // The printed line size consists of the indentation we're going
162: // to use next, the accumulated line so far, some spaces and the
163: // accumulated text so far.
164: if (_format.getLineWidth() > 0
165: && _this Indent + _line.length() + _spaces
166: + _text.length() > _format.getLineWidth()) {
167: flushLine(false);
168: try {
169: // Print line and new line, then zero the line contents.
170: _writer.write(_format.getLineSeparator());
171: } catch (IOException except) {
172: // We don't throw an exception, but hold it
173: // until the end of the document.
174: if (_exception == null)
175: _exception = except;
176: }
177: }
178:
179: // Add as many spaces as we accumulaed before.
180: // At the end of this loop, _spaces is zero.
181: while (_spaces > 0) {
182: _line.append(' ');
183: --_spaces;
184: }
185: _line.append(_text);
186: _text = new StringBuffer(20);
187: }
188: // Starting a new word: accumulate the text between the line
189: // and this new word; not a new word: just add another space.
190: ++_spaces;
191: }
192:
193: /**
194: * Called to print a line consisting of the text accumulated so
195: * far. This is equivalent to calling {@link #printSpace} but
196: * forcing the line to print and starting a new line ({@link
197: * #printSpace} will only start a new line if the current line
198: * is long enough).
199: */
200: public void breakLine() {
201: breakLine(false);
202: }
203:
204: public void breakLine(boolean preserveSpace) {
205: // Equivalent to calling printSpace and forcing a flushLine.
206: if (_text.length() > 0) {
207: while (_spaces > 0) {
208: _line.append(' ');
209: --_spaces;
210: }
211: _line.append(_text);
212: _text = new StringBuffer(20);
213: }
214: flushLine(preserveSpace);
215: try {
216: // Print line and new line, then zero the line contents.
217: _writer.write(_format.getLineSeparator());
218: } catch (IOException except) {
219: // We don't throw an exception, but hold it
220: // until the end of the document.
221: if (_exception == null)
222: _exception = except;
223: }
224: }
225:
226: /**
227: * Flushes the line accumulated so far to the writer and get ready
228: * to accumulate the next line. This method is called by {@link
229: * #printText} and {@link #printSpace} when the accumulated line plus
230: * accumulated text are two long to fit on a given line. At the end of
231: * this method _line is empty and _spaces is zero.
232: */
233: public void flushLine(boolean preserveSpace) {
234: int indent;
235:
236: if (_line.length() > 0) {
237: try {
238:
239: if (_format.getIndenting() && !preserveSpace) {
240: // Make sure the indentation does not blow us away.
241: indent = _this Indent;
242: if ((2 * indent) > _format.getLineWidth()
243: && _format.getLineWidth() > 0)
244: indent = _format.getLineWidth() / 2;
245: // Print the indentation as spaces and set the current
246: // indentation to the next expected indentation.
247: while (indent > 0) {
248: _writer.write(' ');
249: --indent;
250: }
251: }
252: _this Indent = _nextIndent;
253:
254: // There is no need to print the spaces at the end of the line,
255: // they are simply stripped and replaced with a single line
256: // separator.
257: _spaces = 0;
258: _writer.write(_line.toString());
259:
260: _line = new StringBuffer(40);
261: } catch (IOException except) {
262: // We don't throw an exception, but hold it
263: // until the end of the document.
264: if (_exception == null)
265: _exception = except;
266: }
267: }
268: }
269:
270: /**
271: * Flush the output stream. Must be called when done printing
272: * the document, otherwise some text might be buffered.
273: */
274: public void flush() {
275: if (_line.length() > 0 || _text.length() > 0)
276: breakLine();
277: try {
278: _writer.flush();
279: } catch (IOException except) {
280: // We don't throw an exception, but hold it
281: // until the end of the document.
282: if (_exception == null)
283: _exception = except;
284: }
285: }
286:
287: /**
288: * Increment the indentation for the next line.
289: */
290: public void indent() {
291: _nextIndent += _format.getIndent();
292: }
293:
294: /**
295: * Decrement the indentation for the next line.
296: */
297: public void unindent() {
298: _nextIndent -= _format.getIndent();
299: if (_nextIndent < 0)
300: _nextIndent = 0;
301: // If there is no current line and we're de-identing then
302: // this indentation level is actually the next level.
303: if ((_line.length() + _spaces + _text.length()) == 0)
304: _this Indent = _nextIndent;
305: }
306:
307: public int getNextIndent() {
308: return _nextIndent;
309: }
310:
311: public void setNextIndent(int indent) {
312: _nextIndent = indent;
313: }
314:
315: public void setThisIndent(int indent) {
316: _thisIndent = indent;
317: }
318:
319: }
|