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