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