diff -r 1d7e6da6adc8 -r c348e06f0e82 jaxp/src/com/sun/org/apache/xml/internal/serialize/IndentPrinter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/src/com/sun/org/apache/xml/internal/serialize/IndentPrinter.java Thu Apr 12 08:38:26 2012 -0700 @@ -0,0 +1,365 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* + * Copyright 1999-2002,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.sun.org.apache.xml.internal.serialize; + + +import java.io.Writer; +import java.io.StringWriter; +import java.io.IOException; + + +/** + * Extends {@link Printer} and adds support for indentation and line + * wrapping. + * + * @author Assaf Arkin + */ +public class IndentPrinter + extends Printer +{ + + + /** + * Holds the currently accumulating text line. This buffer will constantly + * be reused by deleting its contents instead of reallocating it. + */ + private StringBuffer _line; + + + /** + * Holds the currently accumulating text that follows {@link #_line}. + * When the end of the part is identified by a call to {@link #printSpace} + * or {@link #breakLine}, this part is added to the accumulated line. + */ + private StringBuffer _text; + + + /** + * Counts how many white spaces come between the accumulated line and the + * current accumulated text. Multiple spaces at the end of the a line + * will not be printed. + */ + private int _spaces; + + + /** + * Holds the indentation for the current line that is now accumulating in + * memory and will be sent for printing shortly. + */ + private int _thisIndent; + + + /** + * Holds the indentation for the next line to be printed. After this line is + * printed, {@link #_nextIndent} is assigned to {@link #_thisIndent}. + */ + private int _nextIndent; + + + public IndentPrinter( Writer writer, OutputFormat format) + { + super( writer, format ); + // Initialize everything for a first/second run. + _line = new StringBuffer( 80 ); + _text = new StringBuffer( 20 ); + _spaces = 0; + _thisIndent = _nextIndent = 0; + } + + + /** + * Called by any of the DTD handlers to enter DTD mode. + * Once entered, all output will be accumulated in a string + * that can be printed as part of the document's DTD. + * This method may be called any number of time but will only + * have affect the first time it's called. To exist DTD state + * and get the accumulated DTD, call {@link #leaveDTD}. + */ + public void enterDTD() + { + // Can only enter DTD state once. Once we're out of DTD + // state, can no longer re-enter it. + if ( _dtdWriter == null ) { + _line.append( _text ); + _text = new StringBuffer( 20 ); + flushLine( false ); + _dtdWriter = new StringWriter(); + _docWriter = _writer; + _writer = _dtdWriter; + } + } + + + /** + * Called by the root element to leave DTD mode and if any + * DTD parts were printer, will return a string with their + * textual content. + */ + public String leaveDTD() + { + // Only works if we're going out of DTD mode. + if ( _writer == _dtdWriter ) { + _line.append( _text ); + _text = new StringBuffer( 20 ); + flushLine( false ); + _writer = _docWriter; + return _dtdWriter.toString(); + } else + return null; + } + + + /** + * Called to print additional text. Each time this method is called + * it accumulates more text. When a space is printed ({@link + * #printSpace}) all the accumulated text becomes one part and is + * added to the accumulate line. When a line is long enough, it can + * be broken at its text boundary. + * + * @param text The text to print + */ + public void printText( String text ) + { + _text.append( text ); + } + + + public void printText( StringBuffer text ) + { + _text.append( text.toString() ); + } + + + public void printText( char ch ) + { + _text.append( ch ); + } + + + public void printText( char[] chars, int start, int length ) + { + _text.append( chars, start, length ); + } + + + /** + * Called to print a single space between text parts that may be + * broken into separate lines. Must not be called to print a space + * when preserving spaces. The text accumulated so far with {@link + * #printText} will be added to the accumulated line, and a space + * separator will be counted. If the line accumulated so far is + * long enough, it will be printed. + */ + public void printSpace() + { + // The line consists of the text accumulated in _line, + // followed by one or more spaces as counted by _spaces, + // followed by more space accumulated in _text: + // - Text is printed and accumulated into _text. + // - A space is printed, so _text is added to _line and + // a space is counted. + // - More text is printed and accumulated into _text. + // - A space is printed, the previous spaces are added + // to _line, the _text is added to _line, and a new + // space is counted. + + // If text was accumulated with printText(), then the space + // means we have to move that text into the line and + // start accumulating new text with printText(). + if ( _text.length() > 0 ) { + // If the text breaks a line bounary, wrap to the next line. + // The printed line size consists of the indentation we're going + // to use next, the accumulated line so far, some spaces and the + // accumulated text so far. + if ( _format.getLineWidth() > 0 && + _thisIndent + _line.length() + _spaces + _text.length() > _format.getLineWidth() ) { + flushLine( false ); + try { + // Print line and new line, then zero the line contents. + _writer.write( _format.getLineSeparator() ); + } catch ( IOException except ) { + // We don't throw an exception, but hold it + // until the end of the document. + if ( _exception == null ) + _exception = except; + } + } + + // Add as many spaces as we accumulaed before. + // At the end of this loop, _spaces is zero. + while ( _spaces > 0 ) { + _line.append( ' ' ); + --_spaces; + } + _line.append( _text ); + _text = new StringBuffer( 20 ); + } + // Starting a new word: accumulate the text between the line + // and this new word; not a new word: just add another space. + ++_spaces; + } + + + /** + * Called to print a line consisting of the text accumulated so + * far. This is equivalent to calling {@link #printSpace} but + * forcing the line to print and starting a new line ({@link + * #printSpace} will only start a new line if the current line + * is long enough). + */ + public void breakLine() + { + breakLine( false ); + } + + + public void breakLine( boolean preserveSpace ) + { + // Equivalent to calling printSpace and forcing a flushLine. + if ( _text.length() > 0 ) { + while ( _spaces > 0 ) { + _line.append( ' ' ); + --_spaces; + } + _line.append( _text ); + _text = new StringBuffer( 20 ); + } + flushLine( preserveSpace ); + try { + // Print line and new line, then zero the line contents. + _writer.write( _format.getLineSeparator() ); + } catch ( IOException except ) { + // We don't throw an exception, but hold it + // until the end of the document. + if ( _exception == null ) + _exception = except; + } + } + + + /** + * Flushes the line accumulated so far to the writer and get ready + * to accumulate the next line. This method is called by {@link + * #printText} and {@link #printSpace} when the accumulated line plus + * accumulated text are two long to fit on a given line. At the end of + * this method _line is empty and _spaces is zero. + */ + public void flushLine( boolean preserveSpace ) + { + int indent; + + if ( _line.length() > 0 ) { + try { + + if ( _format.getIndenting() && ! preserveSpace ) { + // Make sure the indentation does not blow us away. + indent = _thisIndent; + if ( ( 2 * indent ) > _format.getLineWidth() && _format.getLineWidth() > 0 ) + indent = _format.getLineWidth() / 2; + // Print the indentation as spaces and set the current + // indentation to the next expected indentation. + while ( indent > 0 ) { + _writer.write( ' ' ); + --indent; + } + } + _thisIndent = _nextIndent; + + // There is no need to print the spaces at the end of the line, + // they are simply stripped and replaced with a single line + // separator. + _spaces = 0; + _writer.write( _line.toString() ); + + _line = new StringBuffer( 40 ); + } catch ( IOException except ) { + // We don't throw an exception, but hold it + // until the end of the document. + if ( _exception == null ) + _exception = except; + } + } + } + + + /** + * Flush the output stream. Must be called when done printing + * the document, otherwise some text might be buffered. + */ + public void flush() + { + if ( _line.length() > 0 || _text.length() > 0 ) + breakLine(); + try { + _writer.flush(); + } catch ( IOException except ) { + // We don't throw an exception, but hold it + // until the end of the document. + if ( _exception == null ) + _exception = except; + } + } + + + /** + * Increment the indentation for the next line. + */ + public void indent() + { + _nextIndent += _format.getIndent(); + } + + + /** + * Decrement the indentation for the next line. + */ + public void unindent() + { + _nextIndent -= _format.getIndent(); + if ( _nextIndent < 0 ) + _nextIndent = 0; + // If there is no current line and we're de-identing then + // this indentation level is actually the next level. + if ( ( _line.length() + _spaces + _text.length() ) == 0 ) + _thisIndent = _nextIndent; + } + + + public int getNextIndent() + { + return _nextIndent; + } + + + public void setNextIndent( int indent ) + { + _nextIndent = indent; + } + + + public void setThisIndent( int indent ) + { + _thisIndent = indent; + } + + +}