1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xerces" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, International
  53. * Business Machines, Inc., http://www.apache.org. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package com.sun.org.apache.xml.internal.serialize;
  58. import java.io.Writer;
  59. import java.io.StringWriter;
  60. import java.io.IOException;
  61. /**
  62. * Extends {@link Printer} and adds support for indentation and line
  63. * wrapping.
  64. *
  65. * @version $Revision: 1.8 $ $Date: 2003/01/13 15:59:09 $
  66. * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a>
  67. */
  68. public class IndentPrinter
  69. extends Printer
  70. {
  71. /**
  72. * Holds the currently accumulating text line. This buffer will constantly
  73. * be reused by deleting its contents instead of reallocating it.
  74. */
  75. private StringBuffer _line;
  76. /**
  77. * Holds the currently accumulating text that follows {@link #_line}.
  78. * When the end of the part is identified by a call to {@link #printSpace}
  79. * or {@link #breakLine}, this part is added to the accumulated line.
  80. */
  81. private StringBuffer _text;
  82. /**
  83. * Counts how many white spaces come between the accumulated line and the
  84. * current accumulated text. Multiple spaces at the end of the a line
  85. * will not be printed.
  86. */
  87. private int _spaces;
  88. /**
  89. * Holds the indentation for the current line that is now accumulating in
  90. * memory and will be sent for printing shortly.
  91. */
  92. private int _thisIndent;
  93. /**
  94. * Holds the indentation for the next line to be printed. After this line is
  95. * printed, {@link #_nextIndent} is assigned to {@link #_thisIndent}.
  96. */
  97. private int _nextIndent;
  98. public IndentPrinter( Writer writer, OutputFormat format)
  99. {
  100. super( writer, format );
  101. // Initialize everything for a first/second run.
  102. _line = new StringBuffer( 80 );
  103. _text = new StringBuffer( 20 );
  104. _spaces = 0;
  105. _thisIndent = _nextIndent = 0;
  106. }
  107. /**
  108. * Called by any of the DTD handlers to enter DTD mode.
  109. * Once entered, all output will be accumulated in a string
  110. * that can be printed as part of the document's DTD.
  111. * This method may be called any number of time but will only
  112. * have affect the first time it's called. To exist DTD state
  113. * and get the accumulated DTD, call {@link #leaveDTD}.
  114. */
  115. public void enterDTD()
  116. {
  117. // Can only enter DTD state once. Once we're out of DTD
  118. // state, can no longer re-enter it.
  119. if ( _dtdWriter == null ) {
  120. _line.append( _text );
  121. _text = new StringBuffer( 20 );
  122. flushLine( false );
  123. _dtdWriter = new StringWriter();
  124. _docWriter = _writer;
  125. _writer = _dtdWriter;
  126. }
  127. }
  128. /**
  129. * Called by the root element to leave DTD mode and if any
  130. * DTD parts were printer, will return a string with their
  131. * textual content.
  132. */
  133. public String leaveDTD()
  134. {
  135. // Only works if we're going out of DTD mode.
  136. if ( _writer == _dtdWriter ) {
  137. _line.append( _text );
  138. _text = new StringBuffer( 20 );
  139. flushLine( false );
  140. _writer = _docWriter;
  141. return _dtdWriter.toString();
  142. } else
  143. return null;
  144. }
  145. /**
  146. * Called to print additional text. Each time this method is called
  147. * it accumulates more text. When a space is printed ({@link
  148. * #printSpace}) all the accumulated text becomes one part and is
  149. * added to the accumulate line. When a line is long enough, it can
  150. * be broken at its text boundary.
  151. *
  152. * @param text The text to print
  153. */
  154. public void printText( String text )
  155. {
  156. _text.append( text );
  157. }
  158. public void printText( StringBuffer text )
  159. {
  160. _text.append( text.toString() );
  161. }
  162. public void printText( char ch )
  163. {
  164. _text.append( ch );
  165. }
  166. public void printText( char[] chars, int start, int length )
  167. {
  168. _text.append( chars, start, length );
  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. // If text was accumulated with printText(), then the space
  191. // means we have to move that text into the line and
  192. // start accumulating new text with printText().
  193. if ( _text.length() > 0 ) {
  194. // If the text breaks a line bounary, wrap to the next line.
  195. // The printed line size consists of the indentation we're going
  196. // to use next, the accumulated line so far, some spaces and the
  197. // accumulated text so far.
  198. if ( _format.getLineWidth() > 0 &&
  199. _thisIndent + _line.length() + _spaces + _text.length() > _format.getLineWidth() ) {
  200. flushLine( false );
  201. try {
  202. // Print line and new line, then zero the line contents.
  203. _writer.write( _format.getLineSeparator() );
  204. } catch ( IOException except ) {
  205. // We don't throw an exception, but hold it
  206. // until the end of the document.
  207. if ( _exception == null )
  208. _exception = except;
  209. }
  210. }
  211. // Add as many spaces as we accumulaed before.
  212. // At the end of this loop, _spaces is zero.
  213. while ( _spaces > 0 ) {
  214. _line.append( ' ' );
  215. --_spaces;
  216. }
  217. _line.append( _text );
  218. _text = new StringBuffer( 20 );
  219. }
  220. // Starting a new word: accumulate the text between the line
  221. // and this new word; not a new word: just add another space.
  222. ++_spaces;
  223. }
  224. /**
  225. * Called to print a line consisting of the text accumulated so
  226. * far. This is equivalent to calling {@link #printSpace} but
  227. * forcing the line to print and starting a new line ({@link
  228. * #printSpace} will only start a new line if the current line
  229. * is long enough).
  230. */
  231. public void breakLine()
  232. {
  233. breakLine( false );
  234. }
  235. public void breakLine( boolean preserveSpace )
  236. {
  237. // Equivalent to calling printSpace and forcing a flushLine.
  238. if ( _text.length() > 0 ) {
  239. while ( _spaces > 0 ) {
  240. _line.append( ' ' );
  241. --_spaces;
  242. }
  243. _line.append( _text );
  244. _text = new StringBuffer( 20 );
  245. }
  246. flushLine( preserveSpace );
  247. try {
  248. // Print line and new line, then zero the line contents.
  249. _writer.write( _format.getLineSeparator() );
  250. } catch ( IOException except ) {
  251. // We don't throw an exception, but hold it
  252. // until the end of the document.
  253. if ( _exception == null )
  254. _exception = except;
  255. }
  256. }
  257. /**
  258. * Flushes the line accumulated so far to the writer and get ready
  259. * to accumulate the next line. This method is called by {@link
  260. * #printText} and {@link #printSpace} when the accumulated line plus
  261. * accumulated text are two long to fit on a given line. At the end of
  262. * this method _line is empty and _spaces is zero.
  263. */
  264. public void flushLine( boolean preserveSpace )
  265. {
  266. int indent;
  267. if ( _line.length() > 0 ) {
  268. try {
  269. if ( _format.getIndenting() && ! preserveSpace ) {
  270. // Make sure the indentation does not blow us away.
  271. indent = _thisIndent;
  272. if ( ( 2 * indent ) > _format.getLineWidth() && _format.getLineWidth() > 0 )
  273. indent = _format.getLineWidth() / 2;
  274. // Print the indentation as spaces and set the current
  275. // indentation to the next expected indentation.
  276. while ( indent > 0 ) {
  277. _writer.write( ' ' );
  278. --indent;
  279. }
  280. }
  281. _thisIndent = _nextIndent;
  282. // There is no need to print the spaces at the end of the line,
  283. // they are simply stripped and replaced with a single line
  284. // separator.
  285. _spaces = 0;
  286. _writer.write( _line.toString() );
  287. _line = new StringBuffer( 40 );
  288. } catch ( IOException except ) {
  289. // We don't throw an exception, but hold it
  290. // until the end of the document.
  291. if ( _exception == null )
  292. _exception = except;
  293. }
  294. }
  295. }
  296. /**
  297. * Flush the output stream. Must be called when done printing
  298. * the document, otherwise some text might be buffered.
  299. */
  300. public void flush()
  301. {
  302. if ( _line.length() > 0 || _text.length() > 0 )
  303. breakLine();
  304. try {
  305. _writer.flush();
  306. } catch ( IOException except ) {
  307. // We don't throw an exception, but hold it
  308. // until the end of the document.
  309. if ( _exception == null )
  310. _exception = except;
  311. }
  312. }
  313. /**
  314. * Increment the indentation for the next line.
  315. */
  316. public void indent()
  317. {
  318. _nextIndent += _format.getIndent();
  319. }
  320. /**
  321. * Decrement the indentation for the next line.
  322. */
  323. public void unindent()
  324. {
  325. _nextIndent -= _format.getIndent();
  326. if ( _nextIndent < 0 )
  327. _nextIndent = 0;
  328. // If there is no current line and we're de-identing then
  329. // this indentation level is actually the next level.
  330. if ( ( _line.length() + _spaces + _text.length() ) == 0 )
  331. _thisIndent = _nextIndent;
  332. }
  333. public int getNextIndent()
  334. {
  335. return _nextIndent;
  336. }
  337. public void setNextIndent( int indent )
  338. {
  339. _nextIndent = indent;
  340. }
  341. public void setThisIndent( int indent )
  342. {
  343. _thisIndent = indent;
  344. }
  345. }