|
1 /* |
|
2 * reserved comment block |
|
3 * DO NOT REMOVE OR ALTER! |
|
4 */ |
|
5 /* |
|
6 * Copyright 1999-2002,2004 The Apache Software Foundation. |
|
7 * |
|
8 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
9 * you may not use this file except in compliance with the License. |
|
10 * You may obtain a copy of the License at |
|
11 * |
|
12 * http://www.apache.org/licenses/LICENSE-2.0 |
|
13 * |
|
14 * Unless required by applicable law or agreed to in writing, software |
|
15 * distributed under the License is distributed on an "AS IS" BASIS, |
|
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
17 * See the License for the specific language governing permissions and |
|
18 * limitations under the License. |
|
19 */ |
|
20 |
|
21 |
|
22 package com.sun.org.apache.xml.internal.serialize; |
|
23 |
|
24 |
|
25 import java.io.Writer; |
|
26 import java.io.StringWriter; |
|
27 import java.io.IOException; |
|
28 |
|
29 |
|
30 /** |
|
31 * Extends {@link Printer} and adds support for indentation and line |
|
32 * wrapping. |
|
33 * |
|
34 * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a> |
|
35 */ |
|
36 public class IndentPrinter |
|
37 extends Printer |
|
38 { |
|
39 |
|
40 |
|
41 /** |
|
42 * Holds the currently accumulating text line. This buffer will constantly |
|
43 * be reused by deleting its contents instead of reallocating it. |
|
44 */ |
|
45 private StringBuffer _line; |
|
46 |
|
47 |
|
48 /** |
|
49 * Holds the currently accumulating text that follows {@link #_line}. |
|
50 * When the end of the part is identified by a call to {@link #printSpace} |
|
51 * or {@link #breakLine}, this part is added to the accumulated line. |
|
52 */ |
|
53 private StringBuffer _text; |
|
54 |
|
55 |
|
56 /** |
|
57 * Counts how many white spaces come between the accumulated line and the |
|
58 * current accumulated text. Multiple spaces at the end of the a line |
|
59 * will not be printed. |
|
60 */ |
|
61 private int _spaces; |
|
62 |
|
63 |
|
64 /** |
|
65 * Holds the indentation for the current line that is now accumulating in |
|
66 * memory and will be sent for printing shortly. |
|
67 */ |
|
68 private int _thisIndent; |
|
69 |
|
70 |
|
71 /** |
|
72 * Holds the indentation for the next line to be printed. After this line is |
|
73 * printed, {@link #_nextIndent} is assigned to {@link #_thisIndent}. |
|
74 */ |
|
75 private int _nextIndent; |
|
76 |
|
77 |
|
78 public IndentPrinter( Writer writer, OutputFormat format) |
|
79 { |
|
80 super( writer, format ); |
|
81 // Initialize everything for a first/second run. |
|
82 _line = new StringBuffer( 80 ); |
|
83 _text = new StringBuffer( 20 ); |
|
84 _spaces = 0; |
|
85 _thisIndent = _nextIndent = 0; |
|
86 } |
|
87 |
|
88 |
|
89 /** |
|
90 * Called by any of the DTD handlers to enter DTD mode. |
|
91 * Once entered, all output will be accumulated in a string |
|
92 * that can be printed as part of the document's DTD. |
|
93 * This method may be called any number of time but will only |
|
94 * have affect the first time it's called. To exist DTD state |
|
95 * and get the accumulated DTD, call {@link #leaveDTD}. |
|
96 */ |
|
97 public void enterDTD() |
|
98 { |
|
99 // Can only enter DTD state once. Once we're out of DTD |
|
100 // state, can no longer re-enter it. |
|
101 if ( _dtdWriter == null ) { |
|
102 _line.append( _text ); |
|
103 _text = new StringBuffer( 20 ); |
|
104 flushLine( false ); |
|
105 _dtdWriter = new StringWriter(); |
|
106 _docWriter = _writer; |
|
107 _writer = _dtdWriter; |
|
108 } |
|
109 } |
|
110 |
|
111 |
|
112 /** |
|
113 * Called by the root element to leave DTD mode and if any |
|
114 * DTD parts were printer, will return a string with their |
|
115 * textual content. |
|
116 */ |
|
117 public String leaveDTD() |
|
118 { |
|
119 // Only works if we're going out of DTD mode. |
|
120 if ( _writer == _dtdWriter ) { |
|
121 _line.append( _text ); |
|
122 _text = new StringBuffer( 20 ); |
|
123 flushLine( false ); |
|
124 _writer = _docWriter; |
|
125 return _dtdWriter.toString(); |
|
126 } else |
|
127 return null; |
|
128 } |
|
129 |
|
130 |
|
131 /** |
|
132 * Called to print additional text. Each time this method is called |
|
133 * it accumulates more text. When a space is printed ({@link |
|
134 * #printSpace}) all the accumulated text becomes one part and is |
|
135 * added to the accumulate line. When a line is long enough, it can |
|
136 * be broken at its text boundary. |
|
137 * |
|
138 * @param text The text to print |
|
139 */ |
|
140 public void printText( String text ) |
|
141 { |
|
142 _text.append( text ); |
|
143 } |
|
144 |
|
145 |
|
146 public void printText( StringBuffer text ) |
|
147 { |
|
148 _text.append( text.toString() ); |
|
149 } |
|
150 |
|
151 |
|
152 public void printText( char ch ) |
|
153 { |
|
154 _text.append( ch ); |
|
155 } |
|
156 |
|
157 |
|
158 public void printText( char[] chars, int start, int length ) |
|
159 { |
|
160 _text.append( chars, start, length ); |
|
161 } |
|
162 |
|
163 |
|
164 /** |
|
165 * Called to print a single space between text parts that may be |
|
166 * broken into separate lines. Must not be called to print a space |
|
167 * when preserving spaces. The text accumulated so far with {@link |
|
168 * #printText} will be added to the accumulated line, and a space |
|
169 * separator will be counted. If the line accumulated so far is |
|
170 * long enough, it will be printed. |
|
171 */ |
|
172 public void printSpace() |
|
173 { |
|
174 // The line consists of the text accumulated in _line, |
|
175 // followed by one or more spaces as counted by _spaces, |
|
176 // followed by more space accumulated in _text: |
|
177 // - Text is printed and accumulated into _text. |
|
178 // - A space is printed, so _text is added to _line and |
|
179 // a space is counted. |
|
180 // - More text is printed and accumulated into _text. |
|
181 // - A space is printed, the previous spaces are added |
|
182 // to _line, the _text is added to _line, and a new |
|
183 // space is counted. |
|
184 |
|
185 // If text was accumulated with printText(), then the space |
|
186 // means we have to move that text into the line and |
|
187 // start accumulating new text with printText(). |
|
188 if ( _text.length() > 0 ) { |
|
189 // If the text breaks a line bounary, wrap to the next line. |
|
190 // The printed line size consists of the indentation we're going |
|
191 // to use next, the accumulated line so far, some spaces and the |
|
192 // accumulated text so far. |
|
193 if ( _format.getLineWidth() > 0 && |
|
194 _thisIndent + _line.length() + _spaces + _text.length() > _format.getLineWidth() ) { |
|
195 flushLine( false ); |
|
196 try { |
|
197 // Print line and new line, then zero the line contents. |
|
198 _writer.write( _format.getLineSeparator() ); |
|
199 } catch ( IOException except ) { |
|
200 // We don't throw an exception, but hold it |
|
201 // until the end of the document. |
|
202 if ( _exception == null ) |
|
203 _exception = except; |
|
204 } |
|
205 } |
|
206 |
|
207 // Add as many spaces as we accumulaed before. |
|
208 // At the end of this loop, _spaces is zero. |
|
209 while ( _spaces > 0 ) { |
|
210 _line.append( ' ' ); |
|
211 --_spaces; |
|
212 } |
|
213 _line.append( _text ); |
|
214 _text = new StringBuffer( 20 ); |
|
215 } |
|
216 // Starting a new word: accumulate the text between the line |
|
217 // and this new word; not a new word: just add another space. |
|
218 ++_spaces; |
|
219 } |
|
220 |
|
221 |
|
222 /** |
|
223 * Called to print a line consisting of the text accumulated so |
|
224 * far. This is equivalent to calling {@link #printSpace} but |
|
225 * forcing the line to print and starting a new line ({@link |
|
226 * #printSpace} will only start a new line if the current line |
|
227 * is long enough). |
|
228 */ |
|
229 public void breakLine() |
|
230 { |
|
231 breakLine( false ); |
|
232 } |
|
233 |
|
234 |
|
235 public void breakLine( boolean preserveSpace ) |
|
236 { |
|
237 // Equivalent to calling printSpace and forcing a flushLine. |
|
238 if ( _text.length() > 0 ) { |
|
239 while ( _spaces > 0 ) { |
|
240 _line.append( ' ' ); |
|
241 --_spaces; |
|
242 } |
|
243 _line.append( _text ); |
|
244 _text = new StringBuffer( 20 ); |
|
245 } |
|
246 flushLine( preserveSpace ); |
|
247 try { |
|
248 // Print line and new line, then zero the line contents. |
|
249 _writer.write( _format.getLineSeparator() ); |
|
250 } catch ( IOException except ) { |
|
251 // We don't throw an exception, but hold it |
|
252 // until the end of the document. |
|
253 if ( _exception == null ) |
|
254 _exception = except; |
|
255 } |
|
256 } |
|
257 |
|
258 |
|
259 /** |
|
260 * Flushes the line accumulated so far to the writer and get ready |
|
261 * to accumulate the next line. This method is called by {@link |
|
262 * #printText} and {@link #printSpace} when the accumulated line plus |
|
263 * accumulated text are two long to fit on a given line. At the end of |
|
264 * this method _line is empty and _spaces is zero. |
|
265 */ |
|
266 public void flushLine( boolean preserveSpace ) |
|
267 { |
|
268 int indent; |
|
269 |
|
270 if ( _line.length() > 0 ) { |
|
271 try { |
|
272 |
|
273 if ( _format.getIndenting() && ! preserveSpace ) { |
|
274 // Make sure the indentation does not blow us away. |
|
275 indent = _thisIndent; |
|
276 if ( ( 2 * indent ) > _format.getLineWidth() && _format.getLineWidth() > 0 ) |
|
277 indent = _format.getLineWidth() / 2; |
|
278 // Print the indentation as spaces and set the current |
|
279 // indentation to the next expected indentation. |
|
280 while ( indent > 0 ) { |
|
281 _writer.write( ' ' ); |
|
282 --indent; |
|
283 } |
|
284 } |
|
285 _thisIndent = _nextIndent; |
|
286 |
|
287 // There is no need to print the spaces at the end of the line, |
|
288 // they are simply stripped and replaced with a single line |
|
289 // separator. |
|
290 _spaces = 0; |
|
291 _writer.write( _line.toString() ); |
|
292 |
|
293 _line = new StringBuffer( 40 ); |
|
294 } catch ( IOException except ) { |
|
295 // We don't throw an exception, but hold it |
|
296 // until the end of the document. |
|
297 if ( _exception == null ) |
|
298 _exception = except; |
|
299 } |
|
300 } |
|
301 } |
|
302 |
|
303 |
|
304 /** |
|
305 * Flush the output stream. Must be called when done printing |
|
306 * the document, otherwise some text might be buffered. |
|
307 */ |
|
308 public void flush() |
|
309 { |
|
310 if ( _line.length() > 0 || _text.length() > 0 ) |
|
311 breakLine(); |
|
312 try { |
|
313 _writer.flush(); |
|
314 } catch ( IOException except ) { |
|
315 // We don't throw an exception, but hold it |
|
316 // until the end of the document. |
|
317 if ( _exception == null ) |
|
318 _exception = except; |
|
319 } |
|
320 } |
|
321 |
|
322 |
|
323 /** |
|
324 * Increment the indentation for the next line. |
|
325 */ |
|
326 public void indent() |
|
327 { |
|
328 _nextIndent += _format.getIndent(); |
|
329 } |
|
330 |
|
331 |
|
332 /** |
|
333 * Decrement the indentation for the next line. |
|
334 */ |
|
335 public void unindent() |
|
336 { |
|
337 _nextIndent -= _format.getIndent(); |
|
338 if ( _nextIndent < 0 ) |
|
339 _nextIndent = 0; |
|
340 // If there is no current line and we're de-identing then |
|
341 // this indentation level is actually the next level. |
|
342 if ( ( _line.length() + _spaces + _text.length() ) == 0 ) |
|
343 _thisIndent = _nextIndent; |
|
344 } |
|
345 |
|
346 |
|
347 public int getNextIndent() |
|
348 { |
|
349 return _nextIndent; |
|
350 } |
|
351 |
|
352 |
|
353 public void setNextIndent( int indent ) |
|
354 { |
|
355 _nextIndent = indent; |
|
356 } |
|
357 |
|
358 |
|
359 public void setThisIndent( int indent ) |
|
360 { |
|
361 _thisIndent = indent; |
|
362 } |
|
363 |
|
364 |
|
365 } |