1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  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  * @deprecated As of JDK 9, Xerces 2.9.0, Xerces DOM L3 Serializer implementation
  37  * is replaced by that of Xalan. Main class
  38  * {@link com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl} is replaced
  39  * by {@link com.sun.org.apache.xml.internal.serializer.dom3.LSSerializerImpl}.
  40  */
  41 @Deprecated
  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 }