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
22package com.sun.org.apache.xml.internal.serialize;
23
24
25import java.io.Writer;
26import java.io.StringWriter;
27import 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
42public 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}
372