Coverage Report - org.apache.maven.archetype.common.util.XMLOutputter
 
Classes in this File Line Coverage Branch Coverage Complexity
XMLOutputter
43%
215/491
41%
105/256
3.269
XMLOutputter$NamespaceStack
100%
1/1
N/A
3.269
 
 1  
 package org.apache.maven.archetype.common.util;
 2  
 
 3  
 /*
 4  
  * Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
 5  
  * All rights reserved.
 6  
  *
 7  
  * Redistribution and use in source and binary forms, with or without
 8  
  * modification, are permitted provided that the following conditions
 9  
  * are met:
 10  
  *
 11  
  * 1. Redistributions of source code must retain the above copyright
 12  
  *    notice, this list of conditions, and the following disclaimer.
 13  
  *
 14  
  * 2. Redistributions in binary form must reproduce the above copyright
 15  
  *    notice, this list of conditions, and the disclaimer that follows 
 16  
  *    these conditions in the documentation and/or other materials 
 17  
  *    provided with the distribution.
 18  
  *
 19  
  * 3. The name "JDOM" must not be used to endorse or promote products
 20  
  *    derived from this software without prior written permission.  For
 21  
  *    written permission, please contact <request_AT_jdom_DOT_org>.
 22  
  *
 23  
  * 4. Products derived from this software may not be called "JDOM", nor
 24  
  *    may "JDOM" appear in their name, without prior written permission
 25  
  *    from the JDOM Project Management <request_AT_jdom_DOT_org>.
 26  
  *
 27  
  * In addition, we request (but do not require) that you include in the 
 28  
  * end-user documentation provided with the redistribution and/or in the 
 29  
  * software itself an acknowledgement equivalent to the following:
 30  
  *     "This product includes software developed by the
 31  
  *      JDOM Project (http://www.jdom.org/)."
 32  
  * Alternatively, the acknowledgment may be graphical using the logos 
 33  
  * available at http://www.jdom.org/images/logos.
 34  
  *
 35  
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36  
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37  
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38  
  * DISCLAIMED.  IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
 39  
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40  
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41  
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42  
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43  
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44  
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45  
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46  
  * SUCH DAMAGE.
 47  
  *
 48  
  * This software consists of voluntary contributions made by many 
 49  
  * individuals on behalf of the JDOM Project and was originally 
 50  
  * created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
 51  
  * Brett McLaughlin <brett_AT_jdom_DOT_org>.  For more information
 52  
  * on the JDOM Project, please see <http://www.jdom.org/>.
 53  
  */
 54  
 
 55  
 import org.jdom.Attribute;
 56  
 import org.jdom.CDATA;
 57  
 import org.jdom.Comment;
 58  
 import org.jdom.DocType;
 59  
 import org.jdom.Document;
 60  
 import org.jdom.Element;
 61  
 import org.jdom.EntityRef;
 62  
 import org.jdom.Namespace;
 63  
 import org.jdom.ProcessingInstruction;
 64  
 import org.jdom.Text;
 65  
 import org.jdom.output.EscapeStrategy;
 66  
 
 67  
 import javax.xml.transform.Result;
 68  
 import java.io.BufferedOutputStream;
 69  
 import java.io.BufferedWriter;
 70  
 import java.io.IOException;
 71  
 import java.io.OutputStream;
 72  
 import java.io.OutputStreamWriter;
 73  
 import java.io.StringWriter;
 74  
 import java.io.Writer;
 75  
 import java.util.List;
 76  
 
 77  
 /**
 78  
  * This class is a fork from jdom 1.0 modified to preserve CData and comments parts.
 79  
  * <p/>
 80  
  * Outputs a JDOM document as a stream of bytes. The outputter can manage many
 81  
  * styles of document formatting, from untouched to pretty printed. The default
 82  
  * is to output the document content exactly as created, but this can be changed
 83  
  * by setting a new Format object. For pretty-print output, use
 84  
  * <code>{@link Format#getPrettyFormat()}</code>. For whitespace-normalized
 85  
  * output, use <code>{@link Format#getCompactFormat()}</code>.
 86  
  * <p/>
 87  
  * There are <code>{@link #output output(...)}</code> methods to print any of
 88  
  * the standard JDOM classes, including Document and Element, to either a Writer
 89  
  * or an OutputStream. <b>Warning</b>: When outputting to a Writer, make sure
 90  
  * the writer's encoding matches the encoding setting in the Format object. This
 91  
  * ensures the encoding in which the content is written (controlled by the
 92  
  * Writer configuration) matches the encoding placed in the document's XML
 93  
  * declaration (controlled by the XMLOutputter). Because a Writer cannot be
 94  
  * queried for its encoding, the information must be passed to the Format
 95  
  * manually in its constructor or via the
 96  
  * <code>{@link Format#setEncoding}</code> method. The default encoding is
 97  
  * UTF-8.
 98  
  * <p/>
 99  
  * The methods <code>{@link #outputString outputString(...)}</code> are for
 100  
  * convenience only; for top performance you should call one of the <code>{@link
 101  
  * #output output(...)}</code> methods and pass in your own Writer or
 102  
  * OutputStream if possible.
 103  
  * <p/>
 104  
  * XML declarations are always printed on their own line followed by a line
 105  
  * seperator (this doesn't change the semantics of the document). To omit
 106  
  * printing of the declaration use
 107  
  * <code>{@link Format#setOmitDeclaration}</code>. To omit printing of the
 108  
  * encoding in the declaration use <code>{@link Format#setOmitEncoding}</code>.
 109  
  * Unfortunatly there is currently no way to know the original encoding of the
 110  
  * document.
 111  
  * <p/>
 112  
  * Empty elements are by default printed as &lt;empty/&gt;, but this can be
 113  
  * configured with <code>{@link Format#setExpandEmptyElements}</code> to cause
 114  
  * them to be expanded to &lt;empty&gt;&lt;/empty&gt;.
 115  
  *
 116  
  * @author Brett McLaughlin
 117  
  * @author Jason Hunter
 118  
  * @author Jason Reid
 119  
  * @author Wolfgang Werner
 120  
  * @author Elliotte Rusty Harold
 121  
  * @author David &amp; Will (from Post Tool Design)
 122  
  * @author Dan Schaffer
 123  
  * @author Alex Chaffee
 124  
  * @author Bradley S. Huffman
 125  
  * @version $Revision: 1.112 $, $Date: 2004/09/01 06:08:18 $
 126  
  */
 127  
 
 128  
 public class XMLOutputter
 129  
     implements Cloneable
 130  
 {
 131  
 
 132  
     @SuppressWarnings( "unused" )
 133  
     private static final String CVS_ID =
 134  
         "@(#) $RCSfile: XMLOutputter.java,v $ $Revision: 1.112 $ $Date: 2004/09/01 06:08:18 $ $Name: jdom_1_0 $";
 135  
 
 136  
     // For normal output
 137  58
     private Format userFormat = Format.getRawFormat();
 138  
 
 139  
     // For xml:space="preserve"
 140  2
     protected static final Format preserveFormat = Format.getRawFormat();
 141  
 
 142  
     // What's currently in use
 143  58
     protected Format currentFormat = userFormat;
 144  
 
 145  
     /**
 146  
      * Whether output escaping is enabled for the being processed
 147  
      * Element - default is <code>true</code>
 148  
      */
 149  58
     private boolean escapeOutput = true;
 150  
 
 151  
     // * * * * * * * * * * Constructors * * * * * * * * * *
 152  
     // * * * * * * * * * * Constructors * * * * * * * * * *
 153  
 
 154  
     /**
 155  
      * This will create an <code>XMLOutputter</code> with the default
 156  
      * {@link Format} matching {@link Format#getRawFormat}.
 157  
      */
 158  
     public XMLOutputter()
 159  58
     {
 160  58
     }
 161  
 
 162  
     /**
 163  
      * This will create an <code>XMLOutputter</code> with the specified
 164  
      * format characteristics.  Note the format object is cloned internally
 165  
      * before use.
 166  
      */
 167  
     public XMLOutputter( Format format )
 168  0
     {
 169  0
         userFormat = (Format) format.clone();
 170  0
         currentFormat = userFormat;
 171  0
     }
 172  
 
 173  
     /**
 174  
      * This will create an <code>XMLOutputter</code> with all the
 175  
      * options as set in the given <code>XMLOutputter</code>.  Note
 176  
      * that <code>XMLOutputter two = (XMLOutputter)one.clone();</code>
 177  
      * would work equally well.
 178  
      *
 179  
      * @param that the XMLOutputter to clone
 180  
      */
 181  
     public XMLOutputter( XMLOutputter that )
 182  0
     {
 183  0
         this.userFormat = (Format) that.userFormat.clone();
 184  0
         currentFormat = userFormat;
 185  0
     }
 186  
 
 187  
     // * * * * * * * * * * Set parameters methods * * * * * * * * * *
 188  
     // * * * * * * * * * * Set parameters methods * * * * * * * * * *
 189  
 
 190  
     /**
 191  
      * Sets the new format logic for the outputter.  Note the Format
 192  
      * object is cloned internally before use.
 193  
      *
 194  
      * @param newFormat the format to use for output
 195  
      */
 196  
     public void setFormat( Format newFormat )
 197  
     {
 198  56
         this.userFormat = (Format) newFormat.clone();
 199  56
         this.currentFormat = userFormat;
 200  56
     }
 201  
 
 202  
     /**
 203  
      * Returns the current format in use by the outputter.  Note the
 204  
      * Format object returned is a clone of the one used internally.
 205  
      */
 206  
     public Format getFormat()
 207  
     {
 208  0
         return (Format) userFormat.clone();
 209  
     }
 210  
 
 211  
     // * * * * * * * * * * Output to a OutputStream * * * * * * * * * *
 212  
     // * * * * * * * * * * Output to a OutputStream * * * * * * * * * *
 213  
 
 214  
     /**
 215  
      * This will print the <code>Document</code> to the given output stream.
 216  
      * The characters are printed using the encoding specified in the
 217  
      * constructor, or a default of UTF-8.
 218  
      *
 219  
      * @param doc <code>Document</code> to format.
 220  
      * @param out <code>OutputStream</code> to use.
 221  
      * @throws IOException - if there's any problem writing.
 222  
      */
 223  
     public void output( Document doc, OutputStream out )
 224  
         throws IOException
 225  
     {
 226  0
         Writer writer = makeWriter( out );
 227  0
         output( doc, writer );  // output() flushes
 228  0
     }
 229  
 
 230  
     /**
 231  
      * Print out the <code>{@link DocType}</code>.
 232  
      *
 233  
      * @param doctype <code>DocType</code> to output.
 234  
      * @param out     <code>OutputStream</code> to use.
 235  
      */
 236  
     public void output( DocType doctype, OutputStream out )
 237  
         throws IOException
 238  
     {
 239  0
         Writer writer = makeWriter( out );
 240  0
         output( doctype, writer );  // output() flushes
 241  0
     }
 242  
 
 243  
     /**
 244  
      * Print out an <code>{@link Element}</code>, including
 245  
      * its <code>{@link Attribute}</code>s, and all
 246  
      * contained (child) elements, etc.
 247  
      *
 248  
      * @param element <code>Element</code> to output.
 249  
      * @param out     <code>Writer</code> to use.
 250  
      */
 251  
     public void output( Element element, OutputStream out )
 252  
         throws IOException
 253  
     {
 254  0
         Writer writer = makeWriter( out );
 255  0
         output( element, writer );  // output() flushes
 256  0
     }
 257  
 
 258  
     /**
 259  
      * This will handle printing out an <code>{@link
 260  
      * Element}</code>'s content only, not including its tag, and
 261  
      * attributes.  This can be useful for printing the content of an
 262  
      * element that contains HTML, like "&lt;description&gt;JDOM is
 263  
      * &lt;b&gt;fun&gt;!&lt;/description&gt;".
 264  
      *
 265  
      * @param element <code>Element</code> to output.
 266  
      * @param out     <code>OutputStream</code> to use.
 267  
      */
 268  
     public void outputElementContent( Element element, OutputStream out )
 269  
         throws IOException
 270  
     {
 271  0
         Writer writer = makeWriter( out );
 272  0
         outputElementContent( element, writer );  // output() flushes
 273  0
     }
 274  
 
 275  
     /**
 276  
      * This will handle printing out a list of nodes.
 277  
      * This can be useful for printing the content of an element that
 278  
      * contains HTML, like "&lt;description&gt;JDOM is
 279  
      * &lt;b&gt;fun&gt;!&lt;/description&gt;".
 280  
      *
 281  
      * @param list <code>List</code> of nodes.
 282  
      * @param out  <code>OutputStream</code> to use.
 283  
      */
 284  
     public void output( List<?> list, OutputStream out )
 285  
         throws IOException
 286  
     {
 287  0
         Writer writer = makeWriter( out );
 288  0
         output( list, writer );  // output() flushes
 289  0
     }
 290  
 
 291  
     /**
 292  
      * Print out a <code>{@link CDATA}</code> node.
 293  
      *
 294  
      * @param cdata <code>CDATA</code> to output.
 295  
      * @param out   <code>OutputStream</code> to use.
 296  
      */
 297  
     public void output( CDATA cdata, OutputStream out )
 298  
         throws IOException
 299  
     {
 300  0
         Writer writer = makeWriter( out );
 301  0
         output( cdata, writer );  // output() flushes
 302  0
     }
 303  
 
 304  
     /**
 305  
      * Print out a <code>{@link Text}</code> node.  Perfoms
 306  
      * the necessary entity escaping and whitespace stripping.
 307  
      *
 308  
      * @param text <code>Text</code> to output.
 309  
      * @param out  <code>OutputStream</code> to use.
 310  
      */
 311  
     public void output( Text text, OutputStream out )
 312  
         throws IOException
 313  
     {
 314  0
         Writer writer = makeWriter( out );
 315  0
         output( text, writer );  // output() flushes
 316  0
     }
 317  
 
 318  
     /**
 319  
      * Print out a <code>{@link Comment}</code>.
 320  
      *
 321  
      * @param comment <code>Comment</code> to output.
 322  
      * @param out     <code>OutputStream</code> to use.
 323  
      */
 324  
     public void output( Comment comment, OutputStream out )
 325  
         throws IOException
 326  
     {
 327  0
         Writer writer = makeWriter( out );
 328  0
         output( comment, writer );  // output() flushes
 329  0
     }
 330  
 
 331  
     /**
 332  
      * Print out a <code>{@link ProcessingInstruction}</code>.
 333  
      *
 334  
      * @param pi  <code>ProcessingInstruction</code> to output.
 335  
      * @param out <code>OutputStream</code> to use.
 336  
      */
 337  
     public void output( ProcessingInstruction pi, OutputStream out )
 338  
         throws IOException
 339  
     {
 340  0
         Writer writer = makeWriter( out );
 341  0
         output( pi, writer );  // output() flushes
 342  0
     }
 343  
 
 344  
     /**
 345  
      * Print out a <code>{@link EntityRef}</code>.
 346  
      *
 347  
      * @param entity <code>EntityRef</code> to output.
 348  
      * @param out    <code>OutputStream</code> to use.
 349  
      */
 350  
     public void output( EntityRef entity, OutputStream out )
 351  
         throws IOException
 352  
     {
 353  0
         Writer writer = makeWriter( out );
 354  0
         output( entity, writer );  // output() flushes
 355  0
     }
 356  
 
 357  
     /**
 358  
      * Get an OutputStreamWriter, using prefered encoding
 359  
      * (see {@link Format#setEncoding}).
 360  
      */
 361  
     private Writer makeWriter( OutputStream out )
 362  
         throws java.io.UnsupportedEncodingException
 363  
     {
 364  0
         return makeWriter( out, userFormat.encoding );
 365  
     }
 366  
 
 367  
     /** Get an OutputStreamWriter, use specified encoding. */
 368  
     private static Writer makeWriter( OutputStream out, String enc )
 369  
         throws java.io.UnsupportedEncodingException
 370  
     {
 371  
         // "UTF-8" is not recognized before JDK 1.1.6, so we'll translate
 372  
         // into "UTF8" which works with all JDKs.
 373  0
         if ( "UTF-8".equals( enc ) )
 374  
         {
 375  0
             enc = "UTF8";
 376  
         }
 377  
 
 378  0
         Writer writer = new BufferedWriter( ( new OutputStreamWriter( new BufferedOutputStream( out ), enc ) ) );
 379  0
         return writer;
 380  
     }
 381  
 
 382  
     // * * * * * * * * * * Output to a Writer * * * * * * * * * *
 383  
     // * * * * * * * * * * Output to a Writer * * * * * * * * * *
 384  
 
 385  
     /**
 386  
      * This will print the <code>Document</code> to the given Writer.
 387  
      * <p/>
 388  
      * <p/>
 389  
      * Warning: using your own Writer may cause the outputter's
 390  
      * preferred character encoding to be ignored.  If you use
 391  
      * encodings other than UTF-8, we recommend using the method that
 392  
      * takes an OutputStream instead.
 393  
      * </p>
 394  
      *
 395  
      * @param doc <code>Document</code> to format.
 396  
      * @param out <code>Writer</code> to use.
 397  
      * @throws IOException - if there's any problem writing.
 398  
      */
 399  
     public void output( Document doc, Writer out )
 400  
         throws IOException
 401  
     {
 402  
 
 403  58
         printDeclaration( out, doc, userFormat.encoding );
 404  
 
 405  
         // Print out root element, as well as any root level
 406  
         // comments and processing instructions,
 407  
         // starting with no indentation
 408  58
         List<?> content = doc.getContent();
 409  58
         int size = content.size();
 410  144
         for ( int i = 0; i < size; i++ )
 411  
         {
 412  86
             Object obj = content.get( i );
 413  
 
 414  86
             if ( obj instanceof Element )
 415  
             {
 416  58
                 printElement( out, doc.getRootElement(), 0, createNamespaceStack() );
 417  
             }
 418  28
             else if ( obj instanceof Comment )
 419  
             {
 420  28
                 printComment( out, (Comment) obj );
 421  
             }
 422  0
             else if ( obj instanceof ProcessingInstruction )
 423  
             {
 424  0
                 printProcessingInstruction( out, (ProcessingInstruction) obj );
 425  
             }
 426  0
             else if ( obj instanceof DocType )
 427  
             {
 428  0
                 printDocType( out, doc.getDocType() );
 429  
                 // Always print line separator after declaration, helps the
 430  
                 // output look better and is semantically inconsequential
 431  0
                 out.write( currentFormat.lineSeparator );
 432  
             }
 433  
             else
 434  
             {
 435  
                 // XXX if we get here then we have a illegal content, for
 436  
                 //     now we'll just ignore it
 437  
             }
 438  
 
 439  86
             newline( out );
 440  86
             indent( out, 0 );
 441  
         }
 442  
 
 443  
         // Output final line separator
 444  
         // We output this no matter what the newline flags say
 445  58
         out.write( currentFormat.lineSeparator );
 446  
 
 447  58
         out.flush();
 448  58
     }
 449  
 
 450  
     /**
 451  
      * Print out the <code>{@link DocType}</code>.
 452  
      *
 453  
      * @param doctype <code>DocType</code> to output.
 454  
      * @param out     <code>Writer</code> to use.
 455  
      */
 456  
     public void output( DocType doctype, Writer out )
 457  
         throws IOException
 458  
     {
 459  0
         printDocType( out, doctype );
 460  0
         out.flush();
 461  0
     }
 462  
 
 463  
     /**
 464  
      * Print out an <code>{@link Element}</code>, including
 465  
      * its <code>{@link Attribute}</code>s, and all
 466  
      * contained (child) elements, etc.
 467  
      *
 468  
      * @param element <code>Element</code> to output.
 469  
      * @param out     <code>Writer</code> to use.
 470  
      */
 471  
     public void output( Element element, Writer out )
 472  
         throws IOException
 473  
     {
 474  
         // If this is the root element we could pre-initialize the
 475  
         // namespace stack with the namespaces
 476  0
         printElement( out, element, 0, createNamespaceStack() );
 477  0
         out.flush();
 478  0
     }
 479  
 
 480  
     /**
 481  
      * This will handle printing out an <code>{@link
 482  
      * Element}</code>'s content only, not including its tag, and
 483  
      * attributes.  This can be useful for printing the content of an
 484  
      * element that contains HTML, like "&lt;description&gt;JDOM is
 485  
      * &lt;b&gt;fun&gt;!&lt;/description&gt;".
 486  
      *
 487  
      * @param element <code>Element</code> to output.
 488  
      * @param out     <code>Writer</code> to use.
 489  
      */
 490  
     public void outputElementContent( Element element, Writer out )
 491  
         throws IOException
 492  
     {
 493  0
         List<?> content = element.getContent();
 494  0
         printContentRange( out, content, 0, content.size(),
 495  
             0, createNamespaceStack() );
 496  0
         out.flush();
 497  0
     }
 498  
 
 499  
     /**
 500  
      * This will handle printing out a list of nodes.
 501  
      * This can be useful for printing the content of an element that
 502  
      * contains HTML, like "&lt;description&gt;JDOM is
 503  
      * &lt;b&gt;fun&gt;!&lt;/description&gt;".
 504  
      *
 505  
      * @param list <code>List</code> of nodes.
 506  
      * @param out  <code>Writer</code> to use.
 507  
      */
 508  
     public void output( List<?> list, Writer out )
 509  
         throws IOException
 510  
     {
 511  0
         printContentRange( out, list, 0, list.size(), 0, createNamespaceStack() );
 512  0
         out.flush();
 513  0
     }
 514  
 
 515  
     /**
 516  
      * Print out a <code>{@link CDATA}</code> node.
 517  
      *
 518  
      * @param cdata <code>CDATA</code> to output.
 519  
      * @param out   <code>Writer</code> to use.
 520  
      */
 521  
     public void output( CDATA cdata, Writer out )
 522  
         throws IOException
 523  
     {
 524  0
         printCDATA( out, cdata );
 525  0
         out.flush();
 526  0
     }
 527  
 
 528  
     /**
 529  
      * Print out a <code>{@link Text}</code> node.  Perfoms
 530  
      * the necessary entity escaping and whitespace stripping.
 531  
      *
 532  
      * @param text <code>Text</code> to output.
 533  
      * @param out  <code>Writer</code> to use.
 534  
      */
 535  
     public void output( Text text, Writer out )
 536  
         throws IOException
 537  
     {
 538  0
         printText( out, text );
 539  0
         out.flush();
 540  0
     }
 541  
 
 542  
     /**
 543  
      * Print out a <code>{@link Comment}</code>.
 544  
      *
 545  
      * @param comment <code>Comment</code> to output.
 546  
      * @param out     <code>Writer</code> to use.
 547  
      */
 548  
     public void output( Comment comment, Writer out )
 549  
         throws IOException
 550  
     {
 551  0
         printComment( out, comment );
 552  0
         out.flush();
 553  0
     }
 554  
 
 555  
     /**
 556  
      * Print out a <code>{@link ProcessingInstruction}</code>.
 557  
      *
 558  
      * @param pi  <code>ProcessingInstruction</code> to output.
 559  
      * @param out <code>Writer</code> to use.
 560  
      */
 561  
     public void output( ProcessingInstruction pi, Writer out )
 562  
         throws IOException
 563  
     {
 564  0
         boolean currentEscapingPolicy = currentFormat.ignoreTrAXEscapingPIs;
 565  
 
 566  
         // Output PI verbatim, disregarding TrAX escaping PIs.
 567  0
         currentFormat.setIgnoreTrAXEscapingPIs( true );
 568  0
         printProcessingInstruction( out, pi );
 569  0
         currentFormat.setIgnoreTrAXEscapingPIs( currentEscapingPolicy );
 570  
 
 571  0
         out.flush();
 572  0
     }
 573  
 
 574  
     /**
 575  
      * Print out a <code>{@link EntityRef}</code>.
 576  
      *
 577  
      * @param entity <code>EntityRef</code> to output.
 578  
      * @param out    <code>Writer</code> to use.
 579  
      */
 580  
     public void output( EntityRef entity, Writer out )
 581  
         throws IOException
 582  
     {
 583  0
         printEntityRef( out, entity );
 584  0
         out.flush();
 585  0
     }
 586  
 
 587  
     // * * * * * * * * * * Output to a String * * * * * * * * * *
 588  
     // * * * * * * * * * * Output to a String * * * * * * * * * *
 589  
 
 590  
     /**
 591  
      * Return a string representing a document.  Uses an internal
 592  
      * StringWriter. Warning: a String is Unicode, which may not match
 593  
      * the outputter's specified encoding.
 594  
      *
 595  
      * @param doc <code>Document</code> to format.
 596  
      */
 597  
     public String outputString( Document doc )
 598  
     {
 599  0
         StringWriter out = new StringWriter();
 600  
         try
 601  
         {
 602  0
             output( doc, out );  // output() flushes
 603  
         }
 604  0
         catch ( IOException e )
 605  
         {
 606  0
         }
 607  0
         return out.toString();
 608  
     }
 609  
 
 610  
     /**
 611  
      * Return a string representing a DocType. Warning: a String is
 612  
      * Unicode, which may not match the outputter's specified
 613  
      * encoding.
 614  
      *
 615  
      * @param doctype <code>DocType</code> to format.
 616  
      */
 617  
     public String outputString( DocType doctype )
 618  
     {
 619  0
         StringWriter out = new StringWriter();
 620  
         try
 621  
         {
 622  0
             output( doctype, out );  // output() flushes
 623  
         }
 624  0
         catch ( IOException e )
 625  
         {
 626  0
         }
 627  0
         return out.toString();
 628  
     }
 629  
 
 630  
     /**
 631  
      * Return a string representing an element. Warning: a String is
 632  
      * Unicode, which may not match the outputter's specified
 633  
      * encoding.
 634  
      *
 635  
      * @param element <code>Element</code> to format.
 636  
      */
 637  
     public String outputString( Element element )
 638  
     {
 639  0
         StringWriter out = new StringWriter();
 640  
         try
 641  
         {
 642  0
             output( element, out );  // output() flushes
 643  
         }
 644  0
         catch ( IOException e )
 645  
         {
 646  0
         }
 647  0
         return out.toString();
 648  
     }
 649  
 
 650  
     /**
 651  
      * Return a string representing a list of nodes.  The list is
 652  
      * assumed to contain legal JDOM nodes.
 653  
      *
 654  
      * @param list <code>List</code> to format.
 655  
      */
 656  
     public String outputString( List<?> list )
 657  
     {
 658  0
         StringWriter out = new StringWriter();
 659  
         try
 660  
         {
 661  0
             output( list, out );  // output() flushes
 662  
         }
 663  0
         catch ( IOException e )
 664  
         {
 665  0
         }
 666  0
         return out.toString();
 667  
     }
 668  
 
 669  
     /**
 670  
      * Return a string representing a CDATA node. Warning: a String is
 671  
      * Unicode, which may not match the outputter's specified
 672  
      * encoding.
 673  
      *
 674  
      * @param cdata <code>CDATA</code> to format.
 675  
      */
 676  
     public String outputString( CDATA cdata )
 677  
     {
 678  0
         StringWriter out = new StringWriter();
 679  
         try
 680  
         {
 681  0
             output( cdata, out );  // output() flushes
 682  
         }
 683  0
         catch ( IOException e )
 684  
         {
 685  0
         }
 686  0
         return out.toString();
 687  
     }
 688  
 
 689  
     /**
 690  
      * Return a string representing a Text node. Warning: a String is
 691  
      * Unicode, which may not match the outputter's specified
 692  
      * encoding.
 693  
      *
 694  
      * @param text <code>Text</code> to format.
 695  
      */
 696  
     public String outputString( Text text )
 697  
     {
 698  0
         StringWriter out = new StringWriter();
 699  
         try
 700  
         {
 701  0
             output( text, out );  // output() flushes
 702  
         }
 703  0
         catch ( IOException e )
 704  
         {
 705  0
         }
 706  0
         return out.toString();
 707  
     }
 708  
 
 709  
 
 710  
     /**
 711  
      * Return a string representing a comment. Warning: a String is
 712  
      * Unicode, which may not match the outputter's specified
 713  
      * encoding.
 714  
      *
 715  
      * @param comment <code>Comment</code> to format.
 716  
      */
 717  
     public String outputString( Comment comment )
 718  
     {
 719  0
         StringWriter out = new StringWriter();
 720  
         try
 721  
         {
 722  0
             output( comment, out );  // output() flushes
 723  
         }
 724  0
         catch ( IOException e )
 725  
         {
 726  0
         }
 727  0
         return out.toString();
 728  
     }
 729  
 
 730  
     /**
 731  
      * Return a string representing a PI. Warning: a String is
 732  
      * Unicode, which may not match the outputter's specified
 733  
      * encoding.
 734  
      *
 735  
      * @param pi <code>ProcessingInstruction</code> to format.
 736  
      */
 737  
     public String outputString( ProcessingInstruction pi )
 738  
     {
 739  0
         StringWriter out = new StringWriter();
 740  
         try
 741  
         {
 742  0
             output( pi, out );  // output() flushes
 743  
         }
 744  0
         catch ( IOException e )
 745  
         {
 746  0
         }
 747  0
         return out.toString();
 748  
     }
 749  
 
 750  
     /**
 751  
      * Return a string representing an entity. Warning: a String is
 752  
      * Unicode, which may not match the outputter's specified
 753  
      * encoding.
 754  
      *
 755  
      * @param entity <code>EntityRef</code> to format.
 756  
      */
 757  
     public String outputString( EntityRef entity )
 758  
     {
 759  0
         StringWriter out = new StringWriter();
 760  
         try
 761  
         {
 762  0
             output( entity, out );  // output() flushes
 763  
         }
 764  0
         catch ( IOException e )
 765  
         {
 766  0
         }
 767  0
         return out.toString();
 768  
     }
 769  
 
 770  
     // * * * * * * * * * * Internal printing methods * * * * * * * * * *
 771  
     // * * * * * * * * * * Internal printing methods * * * * * * * * * *
 772  
 
 773  
     /**
 774  
      * This will handle printing of the declaration.
 775  
      * Assumes XML version 1.0 since we don't directly know.
 776  
      *
 777  
      * @param doc      <code>Document</code> whose declaration to write.
 778  
      * @param out      <code>Writer</code> to use.
 779  
      * @param encoding The encoding to add to the declaration
 780  
      */
 781  
     protected void printDeclaration( Writer out, Document doc, String encoding )
 782  
         throws IOException
 783  
     {
 784  
 
 785  
         // Only print the declaration if it's not being omitted
 786  58
         if ( !userFormat.omitDeclaration )
 787  
         {
 788  
             // Assume 1.0 version
 789  58
             out.write( "<?xml version=\"1.0\"" );
 790  58
             if ( !userFormat.omitEncoding )
 791  
             {
 792  58
                 out.write( " encoding=\"" + encoding + "\"" );
 793  
             }
 794  58
             out.write( "?>" );
 795  
 
 796  
             // Print new line after decl always, even if no other new lines
 797  
             // Helps the output look better and is semantically
 798  
             // inconsequential
 799  58
             out.write( currentFormat.lineSeparator );
 800  
         }
 801  58
     }
 802  
 
 803  
     /**
 804  
      * This handle printing the DOCTYPE declaration if one exists.
 805  
      *
 806  
      * @param docType <code>Document</code> whose declaration to write.
 807  
      * @param out     <code>Writer</code> to use.
 808  
      */
 809  
     protected void printDocType( Writer out, DocType docType )
 810  
         throws IOException
 811  
     {
 812  
 
 813  0
         String publicID = docType.getPublicID();
 814  0
         String systemID = docType.getSystemID();
 815  0
         String internalSubset = docType.getInternalSubset();
 816  0
         boolean hasPublic = false;
 817  
 
 818  0
         out.write( "<!DOCTYPE " );
 819  0
         out.write( docType.getElementName() );
 820  0
         if ( publicID != null )
 821  
         {
 822  0
             out.write( " PUBLIC \"" );
 823  0
             out.write( publicID );
 824  0
             out.write( "\"" );
 825  0
             hasPublic = true;
 826  
         }
 827  0
         if ( systemID != null )
 828  
         {
 829  0
             if ( !hasPublic )
 830  
             {
 831  0
                 out.write( " SYSTEM" );
 832  
             }
 833  0
             out.write( " \"" );
 834  0
             out.write( systemID );
 835  0
             out.write( "\"" );
 836  
         }
 837  0
         if ( ( internalSubset != null ) && ( !internalSubset.equals( "" ) ) )
 838  
         {
 839  0
             out.write( " [" );
 840  0
             out.write( currentFormat.lineSeparator );
 841  0
             out.write( docType.getInternalSubset() );
 842  0
             out.write( "]" );
 843  
         }
 844  0
         out.write( ">" );
 845  0
     }
 846  
 
 847  
     /**
 848  
      * This will handle printing of comments.
 849  
      *
 850  
      * @param comment <code>Comment</code> to write.
 851  
      * @param out     <code>Writer</code> to use.
 852  
      */
 853  
     protected void printComment( Writer out, Comment comment )
 854  
         throws IOException
 855  
     {
 856  32
         out.write( "<!--" );
 857  32
         out.write( comment.getText() );
 858  32
         out.write( "-->" );
 859  32
     }
 860  
 
 861  
     /**
 862  
      * This will handle printing of processing instructions.
 863  
      *
 864  
      * @param pi  <code>ProcessingInstruction</code> to write.
 865  
      * @param out <code>Writer</code> to use.
 866  
      */
 867  
     protected void printProcessingInstruction( Writer out, ProcessingInstruction pi )
 868  
         throws IOException
 869  
     {
 870  0
         String target = pi.getTarget();
 871  0
         boolean piProcessed = false;
 872  
 
 873  0
         if ( currentFormat.ignoreTrAXEscapingPIs == false )
 874  
         {
 875  0
             if ( target.equals( Result.PI_DISABLE_OUTPUT_ESCAPING ) )
 876  
             {
 877  0
                 escapeOutput = false;
 878  0
                 piProcessed = true;
 879  
             }
 880  0
             else if ( target.equals( Result.PI_ENABLE_OUTPUT_ESCAPING ) )
 881  
             {
 882  0
                 escapeOutput = true;
 883  0
                 piProcessed = true;
 884  
             }
 885  
         }
 886  0
         if ( piProcessed == false )
 887  
         {
 888  0
             String rawData = pi.getData();
 889  
 
 890  
             // Write <?target data?> or if no data then just <?target?>
 891  0
             if ( !"".equals( rawData ) )
 892  
             {
 893  0
                 out.write( "<?" );
 894  0
                 out.write( target );
 895  0
                 out.write( " " );
 896  0
                 out.write( rawData );
 897  0
                 out.write( "?>" );
 898  
             }
 899  
             else
 900  
             {
 901  0
                 out.write( "<?" );
 902  0
                 out.write( target );
 903  0
                 out.write( "?>" );
 904  
             }
 905  
         }
 906  0
     }
 907  
 
 908  
     /**
 909  
      * This will handle printing a <code>{@link EntityRef}</code>.
 910  
      * Only the entity reference such as <code>&amp;entity;</code>
 911  
      * will be printed. However, subclasses are free to override
 912  
      * this method to print the contents of the entity instead.
 913  
      *
 914  
      * @param entity <code>EntityRef</code> to output.
 915  
      * @param out    <code>Writer</code> to use.
 916  
      */
 917  
     protected void printEntityRef( Writer out, EntityRef entity )
 918  
         throws IOException
 919  
     {
 920  0
         out.write( "&" );
 921  0
         out.write( entity.getName() );
 922  0
         out.write( ";" );
 923  0
     }
 924  
 
 925  
     /**
 926  
      * This will handle printing of <code>{@link CDATA}</code> text.
 927  
      *
 928  
      * @param cdata <code>CDATA</code> to output.
 929  
      * @param out   <code>Writer</code> to use.
 930  
      */
 931  
     protected void printCDATA( Writer out, CDATA cdata )
 932  
         throws IOException
 933  
     {
 934  2
         String str =
 935  
             ( currentFormat.mode == Format.TextMode.NORMALIZE ) ? cdata.getTextNormalize()
 936  
                             : ( ( currentFormat.mode == Format.TextMode.TRIM ) ? cdata.getText().trim()
 937  
                                             : cdata.getText() );
 938  2
         out.write( "<![CDATA[" );
 939  2
         out.write( str );
 940  2
         out.write( "]]>" );
 941  2
     }
 942  
 
 943  
     /**
 944  
      * This will handle printing of <code>{@link Text}</code> strings.
 945  
      *
 946  
      * @param text <code>Text</code> to write.
 947  
      * @param out  <code>Writer</code> to use.
 948  
      */
 949  
     protected void printText( Writer out, Text text )
 950  
         throws IOException
 951  
     {
 952  0
         String str =
 953  
             ( currentFormat.mode == Format.TextMode.NORMALIZE ) ? text.getTextNormalize()
 954  
                             : ( ( currentFormat.mode == Format.TextMode.TRIM ) ? text.getText().trim() : text.getText() );
 955  0
         out.write( escapeElementEntities( str ) );
 956  0
     }
 957  
 
 958  
     /**
 959  
      * This will handle printing a string.  Escapes the element entities,
 960  
      * trims interior whitespace, etc. if necessary.
 961  
      */
 962  
     private void printString( Writer out, String str )
 963  
         throws IOException
 964  
     {
 965  1506
         if ( currentFormat.mode == Format.TextMode.NORMALIZE )
 966  
         {
 967  0
             str = Text.normalizeString( str );
 968  
         }
 969  1506
         else if ( currentFormat.mode == Format.TextMode.TRIM )
 970  
         {
 971  0
             str = str.trim();
 972  
         }
 973  1506
         out.write( escapeElementEntities( str ) );
 974  1506
     }
 975  
 
 976  
     /**
 977  
      * This will handle printing of a <code>{@link Element}</code>,
 978  
      * its <code>{@link Attribute}</code>s, and all contained (child)
 979  
      * elements, etc.
 980  
      *
 981  
      * @param element    <code>Element</code> to output.
 982  
      * @param out        <code>Writer</code> to use.
 983  
      * @param level      <code>int</code> level of indention.
 984  
      * @param namespaces <code>List</code> stack of Namespaces in scope.
 985  
      */
 986  
     protected void printElement( Writer out, Element element, int level, NamespaceStack namespaces )
 987  
         throws IOException
 988  
     {
 989  
 
 990  784
         List<?> attributes = element.getAttributes();
 991  784
         List<?> content = element.getContent();
 992  
 
 993  
         // Check for xml:space and adjust format settings
 994  784
         String space = null;
 995  784
         if ( attributes != null )
 996  
         {
 997  784
             space = element.getAttributeValue( "space", Namespace.XML_NAMESPACE );
 998  
         }
 999  
 
 1000  784
         Format previousFormat = currentFormat;
 1001  
 
 1002  784
         if ( "default".equals( space ) )
 1003  
         {
 1004  0
             currentFormat = userFormat;
 1005  
         }
 1006  784
         else if ( "preserve".equals( space ) )
 1007  
         {
 1008  0
             currentFormat = preserveFormat;
 1009  
         }
 1010  
 
 1011  
         // Print the beginning of the tag plus attributes and any
 1012  
         // necessary namespace declarations
 1013  784
         out.write( "<" );
 1014  784
         printQualifiedName( out, element );
 1015  
 
 1016  
         // Mark our namespace starting point
 1017  784
         int previouslyDeclaredNamespaces = namespaces.size();
 1018  
 
 1019  
         // Print the element's namespace, if appropriate
 1020  784
         printElementNamespace( out, element, namespaces );
 1021  
 
 1022  
         // Print out additional namespace declarations
 1023  784
         printAdditionalNamespaces( out, element, namespaces );
 1024  
 
 1025  
         // Print out attributes
 1026  784
         if ( attributes != null )
 1027  
         {
 1028  784
             printAttributes( out, attributes, element, namespaces );
 1029  
         }
 1030  
 
 1031  
         // Depending on the settings (newlines, textNormalize, etc), we may
 1032  
         // or may not want to print all of the content, so determine the
 1033  
         // index of the start of the content we're interested
 1034  
         // in based on the current settings.
 1035  
 
 1036  784
         int start = skipLeadingWhite( content, 0 );
 1037  784
         int size = content.size();
 1038  784
         if ( start >= size )
 1039  
         {
 1040  
             // Case content is empty or all insignificant whitespace
 1041  0
             if ( currentFormat.expandEmptyElements )
 1042  
             {
 1043  0
                 out.write( "></" );
 1044  0
                 printQualifiedName( out, element );
 1045  0
                 out.write( ">" );
 1046  
             }
 1047  
             else
 1048  
             {
 1049  0
                 out.write( " />" );
 1050  
             }
 1051  
         }
 1052  
         else
 1053  
         {
 1054  784
             out.write( ">" );
 1055  
 
 1056  
             // For a special case where the content is only CDATA
 1057  
             // or Text we don't want to indent after the start or
 1058  
             // before the end tag.
 1059  
 
 1060  784
             if ( nextNonText( content, start ) < size )
 1061  
             {
 1062  
                 // Case Mixed Content - normal indentation
 1063  240
                 newline( out );
 1064  240
                 printContentRange( out, content, start, size,
 1065  
                     level + 1, namespaces );
 1066  240
                 newline( out );
 1067  240
                 indent( out, level );
 1068  
             }
 1069  
             else
 1070  
             {
 1071  
                 // Case all CDATA or Text - no indentation
 1072  544
                 printTextRange( out, content, start, size );
 1073  
             }
 1074  784
             out.write( "</" );
 1075  784
             printQualifiedName( out, element );
 1076  784
             out.write( ">" );
 1077  
         }
 1078  
 
 1079  
         // remove declared namespaces from stack
 1080  896
         while ( namespaces.size() > previouslyDeclaredNamespaces )
 1081  
         {
 1082  112
             namespaces.pop();
 1083  
         }
 1084  
 
 1085  
         // Restore our format settings
 1086  784
         currentFormat = previousFormat;
 1087  784
     }
 1088  
 
 1089  
     /**
 1090  
      * This will handle printing of content within a given range.
 1091  
      * The range to print is specified in typical Java fashion; the
 1092  
      * starting index is inclusive, while the ending index is
 1093  
      * exclusive.
 1094  
      *
 1095  
      * @param content    <code>List</code> of content to output
 1096  
      * @param start      index of first content node (inclusive.
 1097  
      * @param end        index of last content node (exclusive).
 1098  
      * @param out        <code>Writer</code> to use.
 1099  
      * @param level      <code>int</code> level of indentation.
 1100  
      * @param namespaces <code>List</code> stack of Namespaces in scope.
 1101  
      */
 1102  
     private void printContentRange( Writer out, List<?> content, int start, int end, int level, NamespaceStack namespaces )
 1103  
         throws IOException
 1104  
     {
 1105  
         boolean firstNode; // Flag for 1st node in content
 1106  
         Object next;       // Node we're about to print
 1107  
         int first, index;  // Indexes into the list of content
 1108  
 
 1109  240
         index = start;
 1110  1932
         while ( index < end )
 1111  
         {
 1112  1692
             firstNode = ( index == start ) ? true : false;
 1113  1692
             next = content.get( index );
 1114  
 
 1115  
             //
 1116  
             // Handle consecutive CDATA, Text, and EntityRef nodes all at once
 1117  
             //
 1118  1692
             if ( ( next instanceof Text ) || ( next instanceof EntityRef ) )
 1119  
             {
 1120  962
                 first = skipLeadingWhite( content, index );
 1121  
                 // Set index to next node for loop
 1122  962
                 index = nextNonText( content, first );
 1123  
 
 1124  
                 // If it's not all whitespace - print it!
 1125  962
                 if ( first < index )
 1126  
                 {
 1127  962
                     if ( !firstNode )
 1128  
                     {
 1129  724
                         newline( out );
 1130  
                     }
 1131  962
                     indent( out, level );
 1132  962
                     printTextRange( out, content, first, index );
 1133  
                 }
 1134  
                 continue;
 1135  
             }
 1136  
 
 1137  
             //
 1138  
             // Handle other nodes
 1139  
             //
 1140  730
             if ( !firstNode )
 1141  
             {
 1142  728
                 newline( out );
 1143  
             }
 1144  
 
 1145  730
             indent( out, level );
 1146  
 
 1147  730
             if ( next instanceof Comment )
 1148  
             {
 1149  4
                 printComment( out, (Comment) next );
 1150  
             }
 1151  726
             else if ( next instanceof Element )
 1152  
             {
 1153  726
                 printElement( out, (Element) next, level, namespaces );
 1154  
             }
 1155  0
             else if ( next instanceof ProcessingInstruction )
 1156  
             {
 1157  0
                 printProcessingInstruction( out, (ProcessingInstruction) next );
 1158  
             }
 1159  
             else
 1160  
             {
 1161  
                 // XXX if we get here then we have a illegal content, for
 1162  
                 //     now we'll just ignore it (probably should throw
 1163  
                 //     a exception)
 1164  
             }
 1165  
 
 1166  730
             index++;
 1167  
         } /* while */
 1168  240
     }
 1169  
 
 1170  
     /**
 1171  
      * This will handle printing of a sequence of <code>{@link CDATA}</code>
 1172  
      * or <code>{@link Text}</code> nodes.  It is an error to have any other
 1173  
      * pass this method any other type of node.
 1174  
      *
 1175  
      * @param content <code>List</code> of content to output
 1176  
      * @param start   index of first content node (inclusive).
 1177  
      * @param end     index of last content node (exclusive).
 1178  
      * @param out     <code>Writer</code> to use.
 1179  
      */
 1180  
     private void printTextRange( Writer out, List<?> content, int start, int end )
 1181  
         throws IOException
 1182  
     {
 1183  
         String previous; // Previous text printed
 1184  
         Object node;     // Next node to print
 1185  
         String next;     // Next text to print
 1186  
 
 1187  1506
         previous = null;
 1188  
 
 1189  
         // Remove leading whitespace-only nodes
 1190  1506
         start = skipLeadingWhite( content, start );
 1191  
 
 1192  1506
         int size = content.size();
 1193  1506
         if ( start < size )
 1194  
         {
 1195  
             // And remove trialing whitespace-only nodes
 1196  1506
             end = skipTrailingWhite( content, end );
 1197  
 
 1198  3014
             for ( int i = start; i < end; i++ )
 1199  
             {
 1200  1508
                 node = content.get( i );
 1201  
 
 1202  
                 // Get the unmangled version of the text
 1203  
                 // we are about to print
 1204  1508
                 if ( node instanceof CDATA )
 1205  
                 {
 1206  2
                     next = "<![CDATA[" + ( (CDATA) node ).getValue() + "]]>";
 1207  
                 }
 1208  1506
                 else if ( node instanceof Text )
 1209  
                 {
 1210  1506
                     next = ( (Text) node ).getText();
 1211  
                 }
 1212  0
                 else if ( node instanceof EntityRef )
 1213  
                 {
 1214  0
                     next = "&" + ( (EntityRef) node ).getValue() + ";";
 1215  
                 }
 1216  
                 else
 1217  
                 {
 1218  0
                     throw new IllegalStateException( "Should see only CDATA, Text, or EntityRef" );
 1219  
                 }
 1220  
 
 1221  
                 // This may save a little time
 1222  1508
                 if ( next == null || "".equals( next ) )
 1223  
                 {
 1224  0
                     continue;
 1225  
                 }
 1226  
 
 1227  
                 // Determine if we need to pad the output (padding is
 1228  
                 // only need in trim or normalizing mode)
 1229  1508
                 if ( previous != null )
 1230  
                 { // Not 1st node
 1231  2
                     if ( currentFormat.mode == Format.TextMode.NORMALIZE
 1232  
                         || currentFormat.mode == Format.TextMode.TRIM )
 1233  
                     {
 1234  0
                         if ( ( endsWithWhite( previous ) )
 1235  
                             || ( startsWithWhite( next ) ) )
 1236  
                         {
 1237  0
                             out.write( " " );
 1238  
                         }
 1239  
                     }
 1240  
                 }
 1241  
 
 1242  
                 // Print the node
 1243  1508
                 if ( node instanceof CDATA )
 1244  
                 {
 1245  2
                     printCDATA( out, (CDATA) node );
 1246  
                 }
 1247  1506
                 else if ( node instanceof EntityRef )
 1248  
                 {
 1249  0
                     printEntityRef( out, (EntityRef) node );
 1250  
                 }
 1251  
                 else
 1252  
                 {
 1253  1506
                     printString( out, next );
 1254  
                 }
 1255  
 
 1256  1508
                 previous = next;
 1257  
             }
 1258  
         }
 1259  1506
     }
 1260  
 
 1261  
     /**
 1262  
      * This will handle printing of any needed <code>{@link Namespace}</code>
 1263  
      * declarations.
 1264  
      *
 1265  
      * @param ns  <code>Namespace</code> to print definition of
 1266  
      * @param out <code>Writer</code> to use.
 1267  
      */
 1268  
     private void printNamespace( Writer out, Namespace ns, NamespaceStack namespaces )
 1269  
         throws IOException
 1270  
     {
 1271  890
         String prefix = ns.getPrefix();
 1272  890
         String uri = ns.getURI();
 1273  
 
 1274  
         // Already printed namespace decl?
 1275  890
         if ( uri.equals( namespaces.getURI( prefix ) ) )
 1276  
         {
 1277  778
             return;
 1278  
         }
 1279  
 
 1280  112
         out.write( " xmlns" );
 1281  112
         if ( !prefix.equals( "" ) )
 1282  
         {
 1283  56
             out.write( ":" );
 1284  56
             out.write( prefix );
 1285  
         }
 1286  112
         out.write( "=\"" );
 1287  112
         out.write( uri );
 1288  112
         out.write( "\"" );
 1289  112
         namespaces.push( ns );
 1290  112
     }
 1291  
 
 1292  
     /**
 1293  
      * This will handle printing of a <code>{@link Attribute}</code> list.
 1294  
      *
 1295  
      * @param attributes <code>List</code> of Attribute objcts
 1296  
      * @param out        <code>Writer</code> to use
 1297  
      */
 1298  
     protected void printAttributes( Writer out, List<?> attributes, Element parent, NamespaceStack namespaces )
 1299  
         throws IOException
 1300  
     {
 1301  
 
 1302  
         // I do not yet handle the case where the same prefix maps to
 1303  
         // two different URIs. For attributes on the same element
 1304  
         // this is illegal; but as yet we don't throw an exception
 1305  
         // if someone tries to do this
 1306  
         // Set prefixes = new HashSet();
 1307  840
         for ( int i = 0; i < attributes.size(); i++ )
 1308  
         {
 1309  56
             Attribute attribute = (Attribute) attributes.get( i );
 1310  56
             Namespace ns = attribute.getNamespace();
 1311  56
             if ( ( ns != Namespace.NO_NAMESPACE ) && ( ns != Namespace.XML_NAMESPACE ) )
 1312  
             {
 1313  56
                 printNamespace( out, ns, namespaces );
 1314  
             }
 1315  
 
 1316  56
             out.write( " " );
 1317  56
             printQualifiedName( out, attribute );
 1318  56
             out.write( "=" );
 1319  
 
 1320  56
             out.write( "\"" );
 1321  56
             out.write( escapeAttributeEntities( attribute.getValue() ) );
 1322  56
             out.write( "\"" );
 1323  
         }
 1324  784
     }
 1325  
 
 1326  
     private void printElementNamespace( Writer out, Element element, NamespaceStack namespaces )
 1327  
         throws IOException
 1328  
     {
 1329  
         // Add namespace decl only if it's not the XML namespace and it's
 1330  
         // not the NO_NAMESPACE with the prefix "" not yet mapped
 1331  
         // (we do output xmlns="" if the "" prefix was already used and we
 1332  
         // need to reclaim it for the NO_NAMESPACE)
 1333  784
         Namespace ns = element.getNamespace();
 1334  784
         if ( ns == Namespace.XML_NAMESPACE )
 1335  
         {
 1336  0
             return;
 1337  
         }
 1338  784
         if ( !( ( ns == Namespace.NO_NAMESPACE ) && ( namespaces.getURI( "" ) == null ) ) )
 1339  
         {
 1340  778
             printNamespace( out, ns, namespaces );
 1341  
         }
 1342  784
     }
 1343  
 
 1344  
     private void printAdditionalNamespaces( Writer out, Element element, NamespaceStack namespaces )
 1345  
         throws IOException
 1346  
     {
 1347  784
         List<?> list = element.getAdditionalNamespaces();
 1348  784
         if ( list != null )
 1349  
         {
 1350  840
             for ( int i = 0; i < list.size(); i++ )
 1351  
             {
 1352  56
                 Namespace additional = (Namespace) list.get( i );
 1353  56
                 printNamespace( out, additional, namespaces );
 1354  
             }
 1355  
         }
 1356  784
     }
 1357  
 
 1358  
     // * * * * * * * * * * Support methods * * * * * * * * * *
 1359  
     // * * * * * * * * * * Support methods * * * * * * * * * *
 1360  
 
 1361  
     /**
 1362  
      * This will print a new line only if the newlines flag was set to
 1363  
      * true.
 1364  
      *
 1365  
      * @param out <code>Writer</code> to use
 1366  
      */
 1367  
     private void newline( Writer out )
 1368  
         throws IOException
 1369  
     {
 1370  2018
         if ( currentFormat.indent != null )
 1371  
         {
 1372  0
             out.write( currentFormat.lineSeparator );
 1373  
         }
 1374  2018
     }
 1375  
 
 1376  
     /**
 1377  
      * This will print indents (only if the newlines flag was
 1378  
      * set to <code>true</code>, and indent is non-null).
 1379  
      *
 1380  
      * @param out   <code>Writer</code> to use
 1381  
      * @param level current indent level (number of tabs)
 1382  
      */
 1383  
     private void indent( Writer out, int level )
 1384  
         throws IOException
 1385  
     {
 1386  2018
         if ( currentFormat.indent == null || currentFormat.indent.equals( "" ) )
 1387  
         {
 1388  2018
             return;
 1389  
         }
 1390  
 
 1391  0
         for ( int i = 0; i < level; i++ )
 1392  
         {
 1393  0
             out.write( currentFormat.indent );
 1394  
         }
 1395  0
     }
 1396  
 
 1397  
     // Returns the index of the first non-all-whitespace CDATA or Text,
 1398  
     // index = content.size() is returned if content contains
 1399  
     // all whitespace.
 1400  
     // @param start index to begin search (inclusive)
 1401  
     private int skipLeadingWhite( List<?> content, int start )
 1402  
     {
 1403  3252
         if ( start < 0 )
 1404  
         {
 1405  0
             start = 0;
 1406  
         }
 1407  
 
 1408  3252
         int index = start;
 1409  3252
         int size = content.size();
 1410  3252
         if ( currentFormat.mode == Format.TextMode.TRIM_FULL_WHITE
 1411  
             || currentFormat.mode == Format.TextMode.NORMALIZE
 1412  
             || currentFormat.mode == Format.TextMode.TRIM )
 1413  
         {
 1414  0
             while ( index < size )
 1415  
             {
 1416  0
                 if ( !isAllWhitespace( content.get( index ) ) )
 1417  
                 {
 1418  0
                     return index;
 1419  
                 }
 1420  0
                 index++;
 1421  
             }
 1422  
         }
 1423  3252
         return index;
 1424  
     }
 1425  
 
 1426  
     // Return the index + 1 of the last non-all-whitespace CDATA or
 1427  
     // Text node,  index < 0 is returned
 1428  
     // if content contains all whitespace.
 1429  
     // @param start index to begin search (exclusive)
 1430  
     private int skipTrailingWhite( List<?> content, int start )
 1431  
     {
 1432  1506
         int size = content.size();
 1433  1506
         if ( start > size )
 1434  
         {
 1435  0
             start = size;
 1436  
         }
 1437  
 
 1438  1506
         int index = start;
 1439  1506
         if ( currentFormat.mode == Format.TextMode.TRIM_FULL_WHITE
 1440  
             || currentFormat.mode == Format.TextMode.NORMALIZE
 1441  
             || currentFormat.mode == Format.TextMode.TRIM )
 1442  
         {
 1443  0
             while ( index >= 0 )
 1444  
             {
 1445  0
                 if ( !isAllWhitespace( content.get( index - 1 ) ) )
 1446  
                 {
 1447  0
                     break;
 1448  
                 }
 1449  0
                 --index;
 1450  
             }
 1451  
         }
 1452  1506
         return index;
 1453  
     }
 1454  
 
 1455  
     // Return the next non-CDATA, non-Text, or non-EntityRef node,
 1456  
     // index = content.size() is returned if there is no more non-CDATA,
 1457  
     // non-Text, or non-EntiryRef nodes
 1458  
     // @param start index to begin search (inclusive)
 1459  
     private static int nextNonText( List<?> content, int start )
 1460  
     {
 1461  1746
         if ( start < 0 )
 1462  
         {
 1463  0
             start = 0;
 1464  
         }
 1465  
 
 1466  1746
         int index = start;
 1467  1746
         int size = content.size();
 1468  3492
         while ( index < size )
 1469  
         {
 1470  2710
             Object node = content.get( index );
 1471  2710
             if ( !( ( node instanceof Text ) || ( node instanceof EntityRef ) ) )
 1472  
             {
 1473  964
                 return index;
 1474  
             }
 1475  1746
             index++;
 1476  1746
         }
 1477  782
         return size;
 1478  
     }
 1479  
 
 1480  
     // Determine if a Object is all whitespace
 1481  
     private boolean isAllWhitespace( Object obj )
 1482  
     {
 1483  0
         String str = null;
 1484  
 
 1485  0
         if ( obj instanceof String )
 1486  
         {
 1487  0
             str = (String) obj;
 1488  
         }
 1489  0
         else if ( obj instanceof Text )
 1490  
         {
 1491  0
             str = ( (Text) obj ).getText();
 1492  
         }
 1493  0
         else if ( obj instanceof EntityRef )
 1494  
         {
 1495  0
             return false;
 1496  
         }
 1497  
         else
 1498  
         {
 1499  0
             return false;
 1500  
         }
 1501  
 
 1502  0
         for ( int i = 0; i < str.length(); i++ )
 1503  
         {
 1504  0
             if ( !isWhitespace( str.charAt( i ) ) )
 1505  
             {
 1506  0
                 return false;
 1507  
             }
 1508  
         }
 1509  0
         return true;
 1510  
     }
 1511  
 
 1512  
     // Determine if a string starts with a XML whitespace.
 1513  
     private boolean startsWithWhite( String str )
 1514  
     {
 1515  0
         return ( ( str != null ) && ( str.length() > 0 ) && isWhitespace( str.charAt( 0 ) ) );
 1516  
     }
 1517  
 
 1518  
     // Determine if a string ends with a XML whitespace.
 1519  
     private boolean endsWithWhite( String str )
 1520  
     {
 1521  0
         return ( ( str != null ) && ( str.length() > 0 ) && isWhitespace( str.charAt( str.length() - 1 ) ) );
 1522  
     }
 1523  
 
 1524  
     // Determine if a character is a XML whitespace.
 1525  
     // XXX should this method be in Verifier
 1526  
     private static boolean isWhitespace( char c )
 1527  
     {
 1528  0
         return ( c == ' ' || c == '\n' || c == '\t' || c == '\r' );
 1529  
     }
 1530  
 
 1531  
     /**
 1532  
      * This will take the pre-defined entities in XML 1.0 and
 1533  
      * convert their character representation to the appropriate
 1534  
      * entity reference, suitable for XML attributes.  It does not convert
 1535  
      * the single quote (') because it's not necessary as the outputter
 1536  
      * writes attributes surrounded by double-quotes.
 1537  
      *
 1538  
      * @param str <code>String</code> input to escape.
 1539  
      * @return <code>String</code> with escaped content.
 1540  
      */
 1541  
     public String escapeAttributeEntities( String str )
 1542  
     {
 1543  
         StringBuffer buffer;
 1544  
         char ch;
 1545  
         String entity;
 1546  56
         EscapeStrategy strategy = currentFormat.escapeStrategy;
 1547  
 
 1548  56
         buffer = null;
 1549  4278
         for ( int i = 0; i < str.length(); i++ )
 1550  
         {
 1551  4222
             ch = str.charAt( i );
 1552  4222
             switch ( ch )
 1553  
             {
 1554  
                 case '<':
 1555  0
                     entity = "&lt;";
 1556  0
                     break;
 1557  
                 case '>':
 1558  0
                     entity = "&gt;";
 1559  0
                     break;
 1560  
 /*
 1561  
                 case '\'' :
 1562  
                     entity = "&apos;";
 1563  
                     break;
 1564  
 */
 1565  
                 case '\"':
 1566  0
                     entity = "&quot;";
 1567  0
                     break;
 1568  
                 case '&':
 1569  0
                     entity = "&amp;";
 1570  0
                     break;
 1571  
                 case '\r':
 1572  0
                     entity = "&#xD;";
 1573  0
                     break;
 1574  
                 case '\t':
 1575  0
                     entity = "&#x9;";
 1576  0
                     break;
 1577  
                 case '\n':
 1578  0
                     entity = "&#xA;";
 1579  0
                     break;
 1580  
                 default:
 1581  4222
                     if ( strategy.shouldEscape( ch ) )
 1582  
                     {
 1583  0
                         entity = "&#x" + Integer.toHexString( ch ) + ";";
 1584  
                     }
 1585  
                     else
 1586  
                     {
 1587  4222
                         entity = null;
 1588  
                     }
 1589  
                     break;
 1590  
             }
 1591  4222
             if ( buffer == null )
 1592  
             {
 1593  4222
                 if ( entity != null )
 1594  
                 {
 1595  
                     // An entity occurred, so we'll have to use StringBuffer
 1596  
                     // (allocate room for it plus a few more entities).
 1597  0
                     buffer = new StringBuffer( str.length() + 20 );
 1598  
                     // Copy previous skipped characters and fall through
 1599  
                     // to pickup current character
 1600  0
                     buffer.append( str.substring( 0, i ) );
 1601  0
                     buffer.append( entity );
 1602  
                 }
 1603  
             }
 1604  
             else
 1605  
             {
 1606  0
                 if ( entity == null )
 1607  
                 {
 1608  0
                     buffer.append( ch );
 1609  
                 }
 1610  
                 else
 1611  
                 {
 1612  0
                     buffer.append( entity );
 1613  
                 }
 1614  
             }
 1615  
         }
 1616  
 
 1617  
         // If there were any entities, return the escaped characters
 1618  
         // that we put in the StringBuffer. Otherwise, just return
 1619  
         // the unmodified input string.
 1620  56
         return ( buffer == null ) ? str : buffer.toString();
 1621  
     }
 1622  
 
 1623  
 
 1624  
     /**
 1625  
      * This will take the three pre-defined entities in XML 1.0
 1626  
      * (used specifically in XML elements) and convert their character
 1627  
      * representation to the appropriate entity reference, suitable for
 1628  
      * XML element content.
 1629  
      *
 1630  
      * @param str <code>String</code> input to escape.
 1631  
      * @return <code>String</code> with escaped content.
 1632  
      */
 1633  
     public String escapeElementEntities( String str )
 1634  
     {
 1635  1506
         if ( escapeOutput == false )
 1636  
         {
 1637  0
             return str;
 1638  
         }
 1639  
 
 1640  
         StringBuffer buffer;
 1641  
         char ch;
 1642  
         String entity;
 1643  1506
         EscapeStrategy strategy = currentFormat.escapeStrategy;
 1644  
 
 1645  1506
         buffer = null;
 1646  14302
         for ( int i = 0; i < str.length(); i++ )
 1647  
         {
 1648  12796
             ch = str.charAt( i );
 1649  12796
             switch ( ch )
 1650  
             {
 1651  
                 case '<':
 1652  0
                     entity = "&lt;";
 1653  0
                     break;
 1654  
                 case '>':
 1655  0
                     entity = "&gt;";
 1656  0
                     break;
 1657  
                 case '&':
 1658  0
                     entity = "&amp;";
 1659  0
                     break;
 1660  
                 case '\r':
 1661  0
                     entity = "&#xD;";
 1662  0
                     break;
 1663  
                 case '\n':
 1664  1142
                     entity = currentFormat.lineSeparator;
 1665  1142
                     break;
 1666  
                 default:
 1667  11654
                     if ( strategy.shouldEscape( ch ) )
 1668  
                     {
 1669  0
                         entity = "&#x" + Integer.toHexString( ch ) + ";";
 1670  
                     }
 1671  
                     else
 1672  
                     {
 1673  11654
                         entity = null;
 1674  
                     }
 1675  
                     break;
 1676  
             }
 1677  12796
             if ( buffer == null )
 1678  
             {
 1679  8150
                 if ( entity != null )
 1680  
                 {
 1681  
                     // An entity occurred, so we'll have to use StringBuffer
 1682  
                     // (allocate room for it plus a few more entities).
 1683  964
                     buffer = new StringBuffer( str.length() + 20 );
 1684  
                     // Copy previous skipped characters and fall through
 1685  
                     // to pickup current character
 1686  964
                     buffer.append( str.substring( 0, i ) );
 1687  964
                     buffer.append( entity );
 1688  
                 }
 1689  
             }
 1690  
             else
 1691  
             {
 1692  4646
                 if ( entity == null )
 1693  
                 {
 1694  4468
                     buffer.append( ch );
 1695  
                 }
 1696  
                 else
 1697  
                 {
 1698  178
                     buffer.append( entity );
 1699  
                 }
 1700  
             }
 1701  
         }
 1702  
 
 1703  
         // If there were any entities, return the escaped characters
 1704  
         // that we put in the StringBuffer. Otherwise, just return
 1705  
         // the unmodified input string.
 1706  1506
         return ( buffer == null ) ? str : buffer.toString();
 1707  
     }
 1708  
 
 1709  
     /** Returns a copy of this XMLOutputter. */
 1710  
     public Object clone()
 1711  
     {
 1712  
         // Implementation notes: Since all state of an XMLOutputter is
 1713  
         // embodied in simple private instance variables, Object.clone
 1714  
         // can be used.  Note that since Object.clone is totally
 1715  
         // broken, we must catch an exception that will never be
 1716  
         // thrown.
 1717  
         try
 1718  
         {
 1719  0
             return super.clone();
 1720  
         }
 1721  0
         catch ( java.lang.CloneNotSupportedException e )
 1722  
         {
 1723  
             // even though this should never ever happen, it's still
 1724  
             // possible to fool Java into throwing a
 1725  
             // CloneNotSupportedException.  If that happens, we
 1726  
             // shouldn't swallow it.
 1727  0
             throw new RuntimeException( e.toString() );
 1728  
         }
 1729  
     }
 1730  
 
 1731  
     /**
 1732  
      * Return a string listing of the settings for this
 1733  
      * XMLOutputter instance.
 1734  
      *
 1735  
      * @return a string listing the settings for this XMLOutputter instance
 1736  
      */
 1737  
     public String toString()
 1738  
     {
 1739  0
         StringBuffer buffer = new StringBuffer();
 1740  0
         for ( int i = 0; i < userFormat.lineSeparator.length(); i++ )
 1741  
         {
 1742  0
             char ch = userFormat.lineSeparator.charAt( i );
 1743  0
             switch ( ch )
 1744  
             {
 1745  
                 case '\r':
 1746  0
                     buffer.append( "\\r" );
 1747  0
                     break;
 1748  
                 case '\n':
 1749  0
                     buffer.append( "\\n" );
 1750  0
                     break;
 1751  
                 case '\t':
 1752  0
                     buffer.append( "\\t" );
 1753  0
                     break;
 1754  
                 default:
 1755  0
                     buffer.append( "[" + ( (int) ch ) + "]" );
 1756  
                     break;
 1757  
             }
 1758  
         }
 1759  
 
 1760  0
         return (
 1761  
             "XMLOutputter[omitDeclaration = " + userFormat.omitDeclaration + ", "
 1762  
                 + "encoding = " + userFormat.encoding + ", "
 1763  
                 + "omitEncoding = " + userFormat.omitEncoding + ", "
 1764  
                 + "indent = '" + userFormat.indent + "'" + ", "
 1765  
                 + "expandEmptyElements = " + userFormat.expandEmptyElements + ", "
 1766  
                 + "lineSeparator = '" + buffer.toString() + "', "
 1767  
                 + "textMode = " + userFormat.mode + "]"
 1768  
         );
 1769  
     }
 1770  
 
 1771  
     /**
 1772  
      * Factory for making new NamespaceStack objects.  The NamespaceStack
 1773  
      * created is actually an inner class extending the package protected
 1774  
      * NamespaceStack, as a way to make NamespaceStack "friendly" toward
 1775  
      * subclassers.
 1776  
      */
 1777  
     private NamespaceStack createNamespaceStack()
 1778  
     {
 1779  
         // actually returns a XMLOutputter.NamespaceStack (see below)
 1780  58
         return new NamespaceStack();
 1781  
     }
 1782  
 
 1783  
     /**
 1784  
      * Our own null subclass of NamespaceStack.  This plays a little
 1785  
      * trick with Java access protection.  We want subclasses of
 1786  
      * XMLOutputter to be able to override protected methods that
 1787  
      * declare a NamespaceStack parameter, but we don't want to
 1788  
      * declare the parent NamespaceStack class as public.
 1789  
      */
 1790  58
     protected class NamespaceStack
 1791  
         extends org.apache.maven.archetype.common.util.NamespaceStack
 1792  
     {
 1793  
     }
 1794  
 
 1795  
     // Support method to print a name without using elt.getQualifiedName()
 1796  
     // and thus avoiding a StringBuffer creation and memory churn
 1797  
     private void printQualifiedName( Writer out, Element e )
 1798  
         throws IOException
 1799  
     {
 1800  1568
         if ( e.getNamespace().getPrefix().length() == 0 )
 1801  
         {
 1802  1568
             out.write( e.getName() );
 1803  
         }
 1804  
         else
 1805  
         {
 1806  0
             out.write( e.getNamespace().getPrefix() );
 1807  0
             out.write( ':' );
 1808  0
             out.write( e.getName() );
 1809  
         }
 1810  1568
     }
 1811  
 
 1812  
     // Support method to print a name without using att.getQualifiedName()
 1813  
     // and thus avoiding a StringBuffer creation and memory churn
 1814  
     private void printQualifiedName( Writer out, Attribute a )
 1815  
         throws IOException
 1816  
     {
 1817  56
         String prefix = a.getNamespace().getPrefix();
 1818  56
         if ( ( prefix != null ) && ( !prefix.equals( "" ) ) )
 1819  
         {
 1820  56
             out.write( prefix );
 1821  56
             out.write( ':' );
 1822  56
             out.write( a.getName() );
 1823  
         }
 1824  
         else
 1825  
         {
 1826  0
             out.write( a.getName() );
 1827  
         }
 1828  56
     }
 1829  
 
 1830  
     // * * * * * * * * * * Deprecated methods * * * * * * * * * *
 1831  
 
 1832  
     /* The methods below here are deprecations of protected methods.  We
 1833  
      * don't usually deprecate protected methods, so they're commented out.
 1834  
      * They're left here in case this mass deprecation causes people trouble.
 1835  
      * Since we're getting close to 1.0 it's actually better for people to
 1836  
      * raise issues early though.
 1837  
      */
 1838  
 
 1839  
 }