001 /** 002 * 003 * Copyright 2006 The Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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: 410741 $ $Date: 2006-05-31 21:35:48 -0700 (Wed, 31 May 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 }