Clover coverage report - dom4j - 1.5
Coverage timestamp: vr sep 3 2004 20:47:03 GMT+01:00
file stats: LOC: 1.589   Methods: 93
NCLOC: 1.062   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
XMLWriter.java 58,4% 61,9% 55,9% 60,4%
coverage coverage
 1    /*
 2    * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
 3    *
 4    * This software is open source.
 5    * See the bottom of this file for the licence.
 6    *
 7    * $Id: XMLWriter.java,v 1.76 2004/09/01 19:37:56 maartenc Exp $
 8    */
 9   
 10    package org.dom4j.io;
 11   
 12    import java.io.BufferedWriter;
 13    import java.io.IOException;
 14    import java.io.OutputStream;
 15    import java.io.OutputStreamWriter;
 16    import java.io.UnsupportedEncodingException;
 17    import java.io.Writer;
 18    import java.util.HashMap;
 19    import java.util.Iterator;
 20    import java.util.List;
 21    import java.util.Map;
 22    import java.util.StringTokenizer;
 23   
 24    import org.dom4j.Attribute;
 25    import org.dom4j.CDATA;
 26    import org.dom4j.Comment;
 27    import org.dom4j.Document;
 28    import org.dom4j.DocumentType;
 29    import org.dom4j.Element;
 30    import org.dom4j.Entity;
 31    import org.dom4j.Namespace;
 32    import org.dom4j.Node;
 33    import org.dom4j.ProcessingInstruction;
 34    import org.dom4j.Text;
 35    import org.dom4j.tree.NamespaceStack;
 36    import org.xml.sax.Attributes;
 37    import org.xml.sax.InputSource;
 38    import org.xml.sax.Locator;
 39    import org.xml.sax.SAXException;
 40    import org.xml.sax.SAXNotRecognizedException;
 41    import org.xml.sax.SAXNotSupportedException;
 42    import org.xml.sax.XMLReader;
 43    import org.xml.sax.ext.LexicalHandler;
 44    import org.xml.sax.helpers.XMLFilterImpl;
 45   
 46    /**<p><code>XMLWriter</code> takes a DOM4J tree and formats it to a
 47    * stream as XML.
 48    * It can also take SAX events too so can be used by SAX clients as this object
 49    * implements the {@link org.xml.sax.ContentHandler} and {@link LexicalHandler} interfaces.
 50    * as well. This formatter performs typical document
 51    * formatting. The XML declaration and processing instructions are
 52    * always on their own lines. An {@link OutputFormat} object can be
 53    * used to define how whitespace is handled when printing and allows various
 54    * configuration options, such as to allow suppression of the XML declaration,
 55    * the encoding declaration or whether empty documents are collapsed.</p>
 56    *
 57    * <p> There are <code>write(...)</code> methods to print any of the
 58    * standard DOM4J classes, including <code>Document</code> and
 59    * <code>Element</code>, to either a <code>Writer</code> or an
 60    * <code>OutputStream</code>. Warning: using your own
 61    * <code>Writer</code> may cause the writer's preferred character
 62    * encoding to be ignored. If you use encodings other than UTF8, we
 63    * recommend using the method that takes an OutputStream instead.
 64    * </p>
 65    *
 66    * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
 67    * @author Joseph Bowbeer
 68    * @version $Revision: 1.76 $
 69    */
 70    public class XMLWriter extends XMLFilterImpl implements LexicalHandler {
 71   
 72    private static final String PAD_TEXT = " ";
 73   
 74    protected static final String[] LEXICAL_HANDLER_NAMES = {
 75    "http://xml.org/sax/properties/lexical-handler",
 76    "http://xml.org/sax/handlers/LexicalHandler"
 77    };
 78   
 79    protected static final OutputFormat DEFAULT_FORMAT = new OutputFormat();
 80   
 81    /** Should entityRefs by resolved when writing ? */
 82    private boolean resolveEntityRefs = true;
 83   
 84    /** Stores the last type of node written so algorithms can refer to the
 85    * previous node type */
 86    protected int lastOutputNodeType;
 87   
 88    /** Stores the xml:space attribute value of preserve for whitespace flag */
 89    protected boolean preserve=false;
 90   
 91    /** The Writer used to output to */
 92    protected Writer writer;
 93   
 94    /** The Stack of namespaceStack written so far */
 95    private NamespaceStack namespaceStack = new NamespaceStack();
 96   
 97    /** The format used by this writer */
 98    private OutputFormat format;
 99   
 100    /** whether we should escape text */
 101    private boolean escapeText = true;
 102    /** The initial number of indentations (so you can print a whole
 103    document indented, if you like) **/
 104    private int indentLevel = 0;
 105   
 106    /** buffer used when escaping strings */
 107    private StringBuffer buffer = new StringBuffer();
 108   
 109    /** whether we have added characters before from the same chunk of characters */
 110    private boolean charactersAdded = false;
 111    private char lastChar;
 112   
 113    /** Whether a flush should occur after writing a document */
 114    private boolean autoFlush;
 115   
 116    /** Lexical handler we should delegate to */
 117    private LexicalHandler lexicalHandler;
 118   
 119    /** Whether comments should appear inside DTD declarations - defaults to false */
 120    private boolean showCommentsInDTDs;
 121   
 122    /** Is the writer curerntly inside a DTD definition? */
 123    private boolean inDTD;
 124   
 125    /** The namespaces used for the current element when consuming SAX events */
 126    private Map namespacesMap;
 127   
 128    /**
 129    * what is the maximum allowed character code
 130    * such as 127 in US-ASCII (7 bit) or 255 in ISO-* (8 bit)
 131    * or -1 to not escape any characters (other than the special XML characters like < > &)
 132    */
 133    private int maximumAllowedCharacter;
 134   
 135   
 136  32 public XMLWriter(Writer writer) {
 137  32 this( writer, DEFAULT_FORMAT );
 138    }
 139   
 140  4206 public XMLWriter(Writer writer, OutputFormat format) {
 141  4206 this.writer = writer;
 142  4206 this.format = format;
 143  4206 namespaceStack.push(Namespace.NO_NAMESPACE);
 144    }
 145   
 146  4 public XMLWriter() {
 147  4 this.format = DEFAULT_FORMAT;
 148  4 this.writer = new BufferedWriter( new OutputStreamWriter( System.out ) );
 149  4 this.autoFlush = true;
 150  4 namespaceStack.push(Namespace.NO_NAMESPACE);
 151    }
 152   
 153  4 public XMLWriter(OutputStream out) throws UnsupportedEncodingException {
 154  4 this.format = DEFAULT_FORMAT;
 155  4 this.writer = createWriter(out, format.getEncoding());
 156  4 this.autoFlush = true;
 157  4 namespaceStack.push(Namespace.NO_NAMESPACE);
 158    }
 159   
 160  110 public XMLWriter(OutputStream out, OutputFormat format) throws UnsupportedEncodingException {
 161  110 this.format = format;
 162  110 this.writer = createWriter(out, format.getEncoding());
 163  110 this.autoFlush = true;
 164  110 namespaceStack.push(Namespace.NO_NAMESPACE);
 165    }
 166   
 167  14 public XMLWriter(OutputFormat format) throws UnsupportedEncodingException {
 168  14 this.format = format;
 169  14 this.writer = createWriter( System.out, format.getEncoding() );
 170  14 this.autoFlush = true;
 171  14 namespaceStack.push(Namespace.NO_NAMESPACE);
 172    }
 173   
 174  4 public void setWriter(Writer writer) {
 175  4 this.writer = writer;
 176  4 this.autoFlush = false;
 177    }
 178   
 179  14 public void setOutputStream(OutputStream out) throws UnsupportedEncodingException {
 180  14 this.writer = createWriter(out, format.getEncoding());
 181  14 this.autoFlush = true;
 182    }
 183   
 184    /**
 185    * @return true if text thats output should be escaped.
 186    * This is enabled by default. It could be disabled if
 187    * the output format is textual, like in XSLT where we can have
 188    * xml, html or text output.
 189    */
 190  0 public boolean isEscapeText() {
 191  0 return escapeText;
 192    }
 193   
 194    /**
 195    * Sets whether text output should be escaped or not.
 196    * This is enabled by default. It could be disabled if
 197    * the output format is textual, like in XSLT where we can have
 198    * xml, html or text output.
 199    */
 200  2 public void setEscapeText(boolean escapeText) {
 201  2 this.escapeText = escapeText;
 202    }
 203   
 204   
 205    /** Set the initial indentation level. This can be used to output
 206    * a document (or, more likely, an element) starting at a given
 207    * indent level, so it's not always flush against the left margin.
 208    * Default: 0
 209    *
 210    * @param indentLevel the number of indents to start with
 211    */
 212  0 public void setIndentLevel(int indentLevel) {
 213  0 this.indentLevel = indentLevel;
 214    }
 215   
 216    /**
 217    * Returns the maximum allowed character code that should be allowed
 218    * unescaped which defaults to 127 in US-ASCII (7 bit) or
 219    * 255 in ISO-* (8 bit).
 220    */
 221  748134 public int getMaximumAllowedCharacter() {
 222  748134 if (maximumAllowedCharacter == 0) {
 223  4322 maximumAllowedCharacter = defaultMaximumAllowedCharacter();
 224    }
 225  748134 return maximumAllowedCharacter;
 226    }
 227   
 228    /**
 229    * Sets the maximum allowed character code that should be allowed
 230    * unescaped
 231    * such as 127 in US-ASCII (7 bit) or 255 in ISO-* (8 bit)
 232    * or -1 to not escape any characters (other than the special XML characters like < > &)
 233    *
 234    * If this is not explicitly set then it is defaulted from the encoding.
 235    *
 236    * @param maximumAllowedCharacter The maximumAllowedCharacter to set
 237    */
 238  2 public void setMaximumAllowedCharacter(int maximumAllowedCharacter) {
 239  2 this.maximumAllowedCharacter = maximumAllowedCharacter;
 240    }
 241   
 242    /** Flushes the underlying Writer */
 243  132 public void flush() throws IOException {
 244  132 writer.flush();
 245    }
 246   
 247    /** Closes the underlying Writer */
 248  36 public void close() throws IOException {
 249  36 writer.close();
 250    }
 251   
 252    /** Writes the new line text to the underlying Writer */
 253  190 public void println() throws IOException {
 254  190 writer.write( format.getLineSeparator() );
 255    }
 256   
 257    /** Writes the given {@link Attribute}.
 258    *
 259    * @param attribute <code>Attribute</code> to output.
 260    */
 261  0 public void write(Attribute attribute) throws IOException {
 262  0 writeAttribute(attribute);
 263   
 264  0 if ( autoFlush ) {
 265  0 flush();
 266    }
 267    }
 268   
 269   
 270    /** <p>This will print the <code>Document</code> to the current Writer.</p>
 271    *
 272    * <p> Warning: using your own Writer may cause the writer's
 273    * preferred character encoding to be ignored. If you use
 274    * encodings other than UTF8, we recommend using the method that
 275    * takes an OutputStream instead. </p>
 276    *
 277    * <p>Note: as with all Writers, you may need to flush() yours
 278    * after this method returns.</p>
 279    *
 280    * @param doc <code>Document</code> to format.
 281    * @throws <code>IOException</code> - if there's any problem writing.
 282    **/
 283  190 public void write(Document doc) throws IOException {
 284  190 writeDeclaration();
 285   
 286  190 if (doc.getDocType() != null) {
 287  2 indent();
 288  2 writeDocType(doc.getDocType());
 289    }
 290   
 291  190 for ( int i = 0, size = doc.nodeCount(); i < size; i++ ) {
 292  190 Node node = doc.node(i);
 293  190 writeNode( node );
 294    }
 295  190 writePrintln();
 296   
 297  190 if ( autoFlush ) {
 298  126 flush();
 299    }
 300    }
 301   
 302    /** <p>Writes the <code>{@link Element}</code>, including
 303    * its <code>{@link Attribute}</code>s, and its value, and all
 304    * its content (child nodes) to the current Writer.</p>
 305    *
 306    * @param element <code>Element</code> to output.
 307    */
 308  4138 public void write(Element element) throws IOException {
 309  4138 writeElement(element);
 310   
 311  4138 if ( autoFlush ) {
 312  4 flush();
 313    }
 314    }
 315   
 316   
 317    /** Writes the given {@link CDATA}.
 318    *
 319    * @param cdata <code>CDATA</code> to output.
 320    */
 321  0 public void write(CDATA cdata) throws IOException {
 322  0 writeCDATA( cdata.getText() );
 323   
 324  0 if ( autoFlush ) {
 325  0 flush();
 326    }
 327    }
 328   
 329    /** Writes the given {@link Comment}.
 330    *
 331    * @param comment <code>Comment</code> to output.
 332    */
 333  0 public void write(Comment comment) throws IOException {
 334  0 writeComment( comment.getText() );
 335   
 336  0 if ( autoFlush ) {
 337  0 flush();
 338    }
 339    }
 340   
 341    /** Writes the given {@link DocumentType}.
 342    *
 343    * @param docType <code>DocumentType</code> to output.
 344    */
 345  0 public void write(DocumentType docType) throws IOException {
 346  0 writeDocType(docType);
 347   
 348  0 if ( autoFlush ) {
 349  0 flush();
 350    }
 351    }
 352   
 353   
 354    /** Writes the given {@link Entity}.
 355    *
 356    * @param entity <code>Entity</code> to output.
 357    */
 358  0 public void write(Entity entity) throws IOException {
 359  0 writeEntity( entity );
 360   
 361  0 if ( autoFlush ) {
 362  0 flush();
 363    }
 364    }
 365   
 366   
 367    /** Writes the given {@link Namespace}.
 368    *
 369    * @param namespace <code>Namespace</code> to output.
 370    */
 371  0 public void write(Namespace namespace) throws IOException {
 372  0 writeNamespace(namespace);
 373   
 374  0 if ( autoFlush ) {
 375  0 flush();
 376    }
 377    }
 378   
 379    /** Writes the given {@link ProcessingInstruction}.
 380    *
 381    * @param processingInstruction <code>ProcessingInstruction</code> to output.
 382    */
 383  0 public void write(ProcessingInstruction processingInstruction) throws IOException {
 384  0 writeProcessingInstruction(processingInstruction);
 385   
 386  0 if ( autoFlush ) {
 387  0 flush();
 388    }
 389    }
 390   
 391    /** <p>Print out a {@link String}, Perfoms
 392    * the necessary entity escaping and whitespace stripping.</p>
 393    *
 394    * @param text is the text to output
 395    */
 396  0 public void write(String text) throws IOException {
 397  0 writeString(text);
 398   
 399  0 if ( autoFlush ) {
 400  0 flush();
 401    }
 402    }
 403   
 404    /** Writes the given {@link Text}.
 405    *
 406    * @param text <code>Text</code> to output.
 407    */
 408  0 public void write(Text text) throws IOException {
 409  0 writeString(text.getText());
 410   
 411  0 if ( autoFlush ) {
 412  0 flush();
 413    }
 414    }
 415   
 416    /** Writes the given {@link Node}.
 417    *
 418    * @param node <code>Node</code> to output.
 419    */
 420  2 public void write(Node node) throws IOException {
 421  2 writeNode(node);
 422   
 423  2 if ( autoFlush ) {
 424  0 flush();
 425    }
 426    }
 427   
 428    /** Writes the given object which should be a String, a Node or a List
 429    * of Nodes.
 430    *
 431    * @param object is the object to output.
 432    */
 433  2 public void write(Object object) throws IOException {
 434  2 if (object instanceof Node) {
 435  2 write((Node) object);
 436    }
 437  0 else if (object instanceof String) {
 438  0 write((String) object);
 439    }
 440  0 else if (object instanceof List) {
 441  0 List list = (List) object;
 442  0 for ( int i = 0, size = list.size(); i < size; i++ ) {
 443  0 write( list.get(i) );
 444    }
 445    }
 446  0 else if (object != null) {
 447  0 throw new IOException( "Invalid object: " + object );
 448    }
 449    }
 450   
 451   
 452    /** <p>Writes the opening tag of an {@link Element},
 453    * including its {@link Attribute}s
 454    * but without its content.</p>
 455    *
 456    * @param element <code>Element</code> to output.
 457    */
 458  0 public void writeOpen(Element element) throws IOException {
 459  0 writer.write("<");
 460  0 writer.write( element.getQualifiedName() );
 461  0 writeAttributes(element);
 462  0 writer.write(">");
 463    }
 464   
 465    /** <p>Writes the closing tag of an {@link Element}</p>
 466    *
 467    * @param element <code>Element</code> to output.
 468    */
 469  0 public void writeClose(Element element) throws IOException {
 470  0 writeClose( element.getQualifiedName() );
 471    }
 472   
 473   
 474    // XMLFilterImpl methods
 475    //-------------------------------------------------------------------------
 476  0 public void parse(InputSource source) throws IOException, SAXException {
 477  0 installLexicalHandler();
 478  0 super.parse(source);
 479    }
 480   
 481   
 482  0 public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
 483  0 for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
 484  0 if (LEXICAL_HANDLER_NAMES[i].equals(name)) {
 485  0 setLexicalHandler((LexicalHandler) value);
 486  0 return;
 487    }
 488    }
 489  0 super.setProperty(name, value);
 490    }
 491   
 492  0 public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
 493  0 for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
 494  0 if (LEXICAL_HANDLER_NAMES[i].equals(name)) {
 495  0 return getLexicalHandler();
 496    }
 497    }
 498  0 return super.getProperty(name);
 499    }
 500   
 501  0 public void setLexicalHandler (LexicalHandler handler) {
 502  0 if (handler == null) {
 503  0 throw new NullPointerException("Null lexical handler");
 504    }
 505    else {
 506  0 this.lexicalHandler = handler;
 507    }
 508    }
 509   
 510  0 public LexicalHandler getLexicalHandler(){
 511  0 return lexicalHandler;
 512    }
 513   
 514   
 515    // ContentHandler interface
 516    //-------------------------------------------------------------------------
 517  2 public void setDocumentLocator(Locator locator) {
 518  2 super.setDocumentLocator(locator);
 519    }
 520   
 521  6 public void startDocument() throws SAXException {
 522  6 try {
 523  6 writeDeclaration();
 524  6 super.startDocument();
 525    }
 526    catch (IOException e) {
 527  0 handleException(e);
 528    }
 529    }
 530   
 531  6 public void endDocument() throws SAXException {
 532  6 super.endDocument();
 533   
 534  6 if ( autoFlush ) {
 535  0 try {
 536  0 flush();
 537    } catch ( IOException e) {}
 538    }
 539    }
 540   
 541  0 public void startPrefixMapping(String prefix, String uri) throws SAXException {
 542  0 if ( namespacesMap == null ) {
 543  0 namespacesMap = new HashMap();
 544    }
 545  0 namespacesMap.put(prefix, uri);
 546  0 super.startPrefixMapping(prefix, uri);
 547    }
 548   
 549  0 public void endPrefixMapping(String prefix) throws SAXException {
 550  0 super.endPrefixMapping(prefix);
 551    }
 552   
 553  12 public void startElement(String namespaceURI, String localName, String qName, Attributes attributes) throws SAXException {
 554  12 try {
 555  12 charactersAdded = false;
 556   
 557  12 writePrintln();
 558  12 indent();
 559  12 writer.write("<");
 560  12 writer.write(qName);
 561  12 writeNamespaces();
 562  12 writeAttributes( attributes );
 563  12 writer.write(">");
 564  12 ++indentLevel;
 565  12 lastOutputNodeType = Node.ELEMENT_NODE;
 566   
 567  12 super.startElement( namespaceURI, localName, qName, attributes );
 568    }
 569    catch (IOException e) {
 570  0 handleException(e);
 571    }
 572    }
 573   
 574  12 public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
 575  12 try {
 576  12 charactersAdded = false;
 577  12 --indentLevel;
 578  12 if ( lastOutputNodeType == Node.ELEMENT_NODE ) {
 579  6 writePrintln();
 580  6 indent();
 581    }
 582   
 583    // XXXX: need to determine this using a stack and checking for
 584    // content / children
 585  12 boolean hadContent = true;
 586  12 if ( hadContent ) {
 587  12 writeClose(qName);
 588    }
 589    else {
 590  0 writeEmptyElementClose(qName);
 591    }
 592  12 lastOutputNodeType = Node.ELEMENT_NODE;
 593   
 594  12 super.endElement( namespaceURI, localName, qName );
 595    }
 596    catch (IOException e) {
 597  0 handleException(e);
 598    }
 599    }
 600   
 601  28 public void characters(char[] ch, int start, int length) throws SAXException {
 602  28 if (ch == null || ch.length == 0 || length <= 0) {
 603  2 return;
 604    }
 605   
 606  26 try {
 607    /*
 608    * we can't use the writeString method here because it's possible
 609    * we don't receive all characters at once and calling writeString
 610    * would cause unwanted spaces to be added in between these chunks
 611    * of character arrays.
 612    */
 613  26 String string = new String(ch, start, length);
 614   
 615  26 if (escapeText) {
 616  24 string = escapeElementEntities(string);
 617    }
 618   
 619  26 if (format.isTrimText()) {
 620  24 if ((lastOutputNodeType == Node.TEXT_NODE) && !charactersAdded) {
 621  0 writer.write(" ");
 622  24 } else if (charactersAdded && Character.isWhitespace(lastChar)) {
 623  2 writer.write(lastChar);
 624    }
 625   
 626  24 String delim = "";
 627  24 StringTokenizer tokens = new StringTokenizer(string);
 628  24 while (tokens.hasMoreTokens()) {
 629  30 writer.write(delim);
 630  30 writer.write(tokens.nextToken());
 631  30 delim = " ";
 632    }
 633    } else {
 634  2 writer.write(string);
 635    }
 636   
 637  26 charactersAdded = true;
 638  26 lastChar = ch[start + length - 1];
 639  26 lastOutputNodeType = Node.TEXT_NODE;
 640   
 641  26 super.characters(ch, start, length);
 642    }
 643    catch (IOException e) {
 644  0 handleException(e);
 645    }
 646    }
 647   
 648  0 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
 649  0 super.ignorableWhitespace(ch, start, length);
 650    }
 651   
 652  0 public void processingInstruction(String target, String data) throws SAXException {
 653  0 try {
 654  0 indent();
 655  0 writer.write("<?");
 656  0 writer.write(target);
 657  0 writer.write(" ");
 658  0 writer.write(data);
 659  0 writer.write("?>");
 660  0 writePrintln();
 661  0 lastOutputNodeType = Node.PROCESSING_INSTRUCTION_NODE;
 662   
 663  0 super.processingInstruction(target, data);
 664    }
 665    catch (IOException e) {
 666  0 handleException(e);
 667    }
 668    }
 669   
 670   
 671   
 672    // DTDHandler interface
 673    //-------------------------------------------------------------------------
 674  0 public void notationDecl(String name, String publicID, String systemID) throws SAXException {
 675  0 super.notationDecl(name, publicID, systemID);
 676    }
 677   
 678  0 public void unparsedEntityDecl(String name, String publicID, String systemID, String notationName) throws SAXException {
 679  0 super.unparsedEntityDecl(name, publicID, systemID, notationName);
 680    }
 681   
 682   
 683    // LexicalHandler interface
 684    //-------------------------------------------------------------------------
 685  0 public void startDTD(String name, String publicID, String systemID) throws SAXException {
 686  0 inDTD = true;
 687  0 try {
 688  0 writeDocType(name, publicID, systemID);
 689    }
 690    catch (IOException e) {
 691  0 handleException(e);
 692    }
 693   
 694  0 if (lexicalHandler != null) {
 695  0 lexicalHandler.startDTD(name, publicID, systemID);
 696    }
 697    }
 698   
 699  0 public void endDTD() throws SAXException {
 700  0 inDTD = false;
 701  0 if (lexicalHandler != null) {
 702  0 lexicalHandler.endDTD();
 703    }
 704    }
 705   
 706  0 public void startCDATA() throws SAXException {
 707  0 try {
 708  0 writer.write( "<![CDATA[" );
 709    }
 710    catch (IOException e) {
 711  0 handleException(e);
 712    }
 713   
 714  0 if (lexicalHandler != null) {
 715  0 lexicalHandler.startCDATA();
 716    }
 717    }
 718   
 719  0 public void endCDATA() throws SAXException {
 720  0 try {
 721  0 writer.write( "]]>" );
 722    }
 723    catch (IOException e) {
 724  0 handleException(e);
 725    }
 726   
 727  0 if (lexicalHandler != null) {
 728  0 lexicalHandler.endCDATA();
 729    }
 730    }
 731   
 732  0 public void startEntity(String name) throws SAXException {
 733  0 try {
 734  0 writeEntityRef(name);
 735    }
 736    catch (IOException e) {
 737  0 handleException(e);
 738    }
 739   
 740  0 if (lexicalHandler != null) {
 741  0 lexicalHandler.startEntity(name);
 742    }
 743    }
 744   
 745  0 public void endEntity(String name) throws SAXException {
 746  0 if (lexicalHandler != null) {
 747  0 lexicalHandler.endEntity(name);
 748    }
 749    }
 750   
 751  0 public void comment(char[] ch, int start, int length) throws SAXException {
 752  0 if ( showCommentsInDTDs || ! inDTD ) {
 753  0 try {
 754  0 charactersAdded = false;
 755  0 writeComment( new String(ch, start, length) );
 756    }
 757    catch (IOException e) {
 758  0 handleException(e);
 759    }
 760    }
 761   
 762  0 if (lexicalHandler != null) {
 763  0 lexicalHandler.comment(ch, start, length);
 764    }
 765    }
 766   
 767   
 768   
 769    // Implementation methods
 770    //-------------------------------------------------------------------------
 771  35438 protected void writeElement(Element element) throws IOException {
 772  35438 int size = element.nodeCount();
 773  35438 String qualifiedName = element.getQualifiedName();
 774   
 775  35438 writePrintln();
 776  35438 indent();
 777   
 778  35438 writer.write("<");
 779  35438 writer.write(qualifiedName);
 780   
 781  35438 int previouslyDeclaredNamespaces = namespaceStack.size();
 782  35438 Namespace ns = element.getNamespace();
 783  35438 if (isNamespaceDeclaration( ns ) ) {
 784  1162 namespaceStack.push(ns);
 785  1162 writeNamespace(ns);
 786    }
 787   
 788    // Print out additional namespace declarations
 789  35438 boolean textOnly = true;
 790  35438 for ( int i = 0; i < size; i++ ) {
 791  95188 Node node = element.node(i);
 792  95188 if ( node instanceof Namespace ) {
 793  180 Namespace additional = (Namespace) node;
 794  180 if (isNamespaceDeclaration( additional ) ) {
 795  32 namespaceStack.push(additional);
 796  32 writeNamespace(additional);
 797    }
 798    }
 799  95008 else if ( node instanceof Element) {
 800  31110 textOnly = false;
 801    }
 802  63898 else if ( node instanceof Comment) {
 803  822 textOnly = false;
 804    }
 805    }
 806   
 807  35438 writeAttributes(element);
 808   
 809  35438 lastOutputNodeType = Node.ELEMENT_NODE;
 810   
 811  35438 if ( size <= 0 ) {
 812  428 writeEmptyElementClose(qualifiedName);
 813    }
 814    else {
 815  35010 writer.write(">");
 816  35010 if ( textOnly ) {
 817    // we have at least one text node so lets assume
 818    // that its non-empty
 819  27174 writeElementContent(element);
 820    }
 821    else {
 822    // we know it's not null or empty from above
 823  7836 ++indentLevel;
 824   
 825  7836 writeElementContent(element);
 826   
 827  7836 --indentLevel;
 828   
 829  7836 writePrintln();
 830  7836 indent();
 831    }
 832  35010 writer.write("</");
 833  35010 writer.write(qualifiedName);
 834  35010 writer.write(">");
 835    }
 836   
 837    // remove declared namespaceStack from stack
 838  35438 while (namespaceStack.size() > previouslyDeclaredNamespaces) {
 839  1198 namespaceStack.pop();
 840    }
 841   
 842  35438 lastOutputNodeType = Node.ELEMENT_NODE;
 843    }
 844   
 845    /**
 846    * Determines if element is a special case of XML elements
 847    * where it contains an xml:space attribute of "preserve".
 848    * If it does, then retain whitespace.
 849    */
 850  168 protected final boolean isElementSpacePreserved(Element element) {
 851  168 final Attribute attr = (Attribute)element.attribute("space");
 852  168 boolean preserveFound=preserve; //default to global state
 853  168 if (attr!=null) {
 854  10 if ("xml".equals(attr.getNamespacePrefix()) &&
 855    "preserve".equals(attr.getText())) {
 856  8 preserveFound = true;
 857    }
 858    else {
 859  2 preserveFound = false;
 860    }
 861    }
 862  168 return preserveFound;
 863    }
 864    /** Outputs the content of the given element. If whitespace trimming is
 865    * enabled then all adjacent text nodes are appended together before
 866    * the whitespace trimming occurs to avoid problems with multiple
 867    * text nodes being created due to text content that spans parser buffers
 868    * in a SAX parser.
 869    */
 870  35010 protected void writeElementContent(Element element) throws IOException {
 871  35010 boolean trim = format.isTrimText();
 872  35010 boolean oldPreserve=preserve;
 873  35010 if (trim) { //verify we have to before more expensive test
 874  168 preserve=isElementSpacePreserved(element);
 875  168 trim = !preserve;
 876    }
 877  35010 if (trim) {
 878    // concatenate adjacent text nodes together
 879    // so that whitespace trimming works properly
 880  146 Text lastTextNode = null;
 881  146 StringBuffer buffer = null;
 882  146 boolean textOnly = true;
 883  146 for ( int i = 0, size = element.nodeCount(); i < size; i++ ) {
 884  256 Node node = element.node(i);
 885  256 if ( node instanceof Text ) {
 886  132 if ( lastTextNode == null ) {
 887  90 lastTextNode = (Text) node;
 888    }
 889    else {
 890  42 if (buffer == null) {
 891  16 buffer = new StringBuffer( lastTextNode.getText() );
 892    }
 893  42 buffer.append( ((Text) node).getText() );
 894    }
 895    }
 896    else {
 897  124 if (!textOnly && format.isPadText()) {
 898  0 writer.write(PAD_TEXT);
 899    }
 900   
 901  124 textOnly = false;
 902   
 903  124 if ( lastTextNode != null ) {
 904  30 if ( buffer != null ) {
 905  4 writeString( buffer.toString() );
 906  4 buffer = null;
 907    }
 908    else {
 909  26 writeString( lastTextNode.getText() );
 910    }
 911  30 lastTextNode = null;
 912   
 913  30 if (format.isPadText()) {
 914  0 writer.write(PAD_TEXT);
 915    }
 916    }
 917  124 writeNode(node);
 918    }
 919    }
 920  146 if ( lastTextNode != null ) {
 921  60 if (!textOnly && format.isPadText()) {
 922  0 writer.write(PAD_TEXT);
 923    }
 924  60 if ( buffer != null ) {
 925  12 writeString( buffer.toString() );
 926  12 buffer = null;
 927    }
 928    else {
 929  48 writeString( lastTextNode.getText() );
 930    }
 931  60 lastTextNode = null;
 932    }
 933    }
 934    else {
 935  34864 Node lastTextNode = null;
 936  34864 for ( int i = 0, size = element.nodeCount(); i < size; i++ ) {
 937  94932 Node node = element.node(i);
 938  94932 if (node instanceof Text) {
 939  62938 writeNode(node);
 940  62938 lastTextNode = node;
 941    } else {
 942  31994 if ((lastTextNode != null) && format.isPadText()) {
 943  2 writer.write(PAD_TEXT);
 944    }
 945  31994 writeNode(node);
 946  31994 if ((lastTextNode != null) && format.isPadText()) {
 947  2 writer.write(PAD_TEXT);
 948    }
 949  31994 lastTextNode = null;
 950    }
 951    }
 952    }
 953  35010 preserve=oldPreserve;
 954    }
 955  4 protected void writeCDATA(String text) throws IOException {
 956  4 writer.write( "<![CDATA[" );
 957  4 writer.write( text );
 958  4 writer.write( "]]>" );
 959   
 960  4 lastOutputNodeType = Node.CDATA_SECTION_NODE;
 961    }
 962   
 963  2 protected void writeDocType(DocumentType docType) throws IOException {
 964  2 if (docType != null) {
 965  2 docType.write( writer );
 966    //writeDocType( docType.getElementName(), docType.getPublicID(), docType.getSystemID() );
 967  2 writePrintln();
 968    }
 969    }
 970   
 971   
 972  1194 protected void writeNamespace(Namespace namespace) throws IOException {
 973  1194 if ( namespace != null ) {
 974  1194 writeNamespace(namespace.getPrefix(), namespace.getURI());
 975    }
 976    }
 977   
 978    /**
 979    * Writes the SAX namepsaces
 980    */
 981  12 protected void writeNamespaces() throws IOException {
 982  12 if ( namespacesMap != null ) {
 983  0 for ( Iterator iter = namespacesMap.entrySet().iterator(); iter.hasNext(); ) {
 984  0 Map.Entry entry = (Map.Entry) iter.next();
 985  0 String prefix = (String) entry.getKey();
 986  0 String uri = (String) entry.getValue();
 987  0 writeNamespace(prefix, uri);
 988    }
 989  0 namespacesMap = null;
 990    }
 991    }
 992   
 993    /**
 994    * Writes the SAX namepsaces
 995    */
 996  1198 protected void writeNamespace(String prefix, String uri) throws IOException {
 997  1198 if ( prefix != null && prefix.length() > 0 ) {
 998  1106 writer.write(" xmlns:");
 999  1106 writer.write(prefix);
 1000  1106 writer.write("=\"");
 1001    }
 1002    else {
 1003  92 writer.write(" xmlns=\"");
 1004    }
 1005  1198 writer.write(uri);
 1006  1198 writer.write("\"");
 1007    }
 1008   
 1009  0 protected void writeProcessingInstruction(ProcessingInstruction processingInstruction) throws IOException {
 1010    //indent();
 1011  0 writer.write( "<?" );
 1012  0 writer.write( processingInstruction.getName() );
 1013  0 writer.write( " " );
 1014  0 writer.write( processingInstruction.getText() );
 1015  0 writer.write( "?>" );
 1016  0 writePrintln();
 1017   
 1018  0 lastOutputNodeType = Node.PROCESSING_INSTRUCTION_NODE;
 1019    }
 1020   
 1021  90 protected void writeString(String text) throws IOException {
 1022  90 if ( text != null && text.length() > 0 ) {
 1023  86 if ( escapeText ) {
 1024  86 text = escapeElementEntities(text);
 1025    }
 1026   
 1027    // if (format.isPadText()) {
 1028    // if (lastOutputNodeType == Node.ELEMENT_NODE) {
 1029    // writer.write(PAD_TEXT);
 1030    // }
 1031    // }
 1032   
 1033  86 if (format.isTrimText()) {
 1034  86 boolean first = true;
 1035  86 StringTokenizer tokenizer = new StringTokenizer(text);
 1036  86 while (tokenizer.hasMoreTokens()) {
 1037  252 String token = tokenizer.nextToken();
 1038  252 if ( first ) {
 1039  78 first = false;
 1040  78 if ( lastOutputNodeType == Node.TEXT_NODE ) {
 1041  0 writer.write(" ");
 1042    }
 1043    }
 1044    else {
 1045  174 writer.write(" ");
 1046    }
 1047  252 writer.write(token);
 1048  252 lastOutputNodeType = Node.TEXT_NODE;
 1049    }
 1050    }
 1051    else {
 1052  0 lastOutputNodeType = Node.TEXT_NODE;
 1053  0 writer.write(text);
 1054    }
 1055    }
 1056    }
 1057   
 1058    /**
 1059    * This method is used to write out Nodes that contain text
 1060    * and still allow for xml:space to be handled properly.
 1061    *
 1062    */
 1063  62938 protected void writeNodeText(Node node) throws IOException {
 1064  62938 String text = node.getText();
 1065  62938 if (text != null && text.length() > 0) {
 1066  62938 if (escapeText) {
 1067  62938 text = escapeElementEntities(text);
 1068    }
 1069   
 1070  62938 lastOutputNodeType = Node.TEXT_NODE;
 1071  62938 writer.write(text);
 1072    }
 1073    }
 1074   
 1075  95248 protected void writeNode(Node node) throws IOException {
 1076  95248 int nodeType = node.getNodeType();
 1077  95248 switch (nodeType) {
 1078  31300 case Node.ELEMENT_NODE:
 1079  31300 writeElement((Element) node);
 1080  31300 break;
 1081  0 case Node.ATTRIBUTE_NODE:
 1082  0 writeAttribute((Attribute) node);
 1083  0 break;
 1084  62938 case Node.TEXT_NODE:
 1085  62938 writeNodeText(node);
 1086    //write((Text) node);
 1087  62938 break;
 1088  6 case Node.CDATA_SECTION_NODE:
 1089  6 writeCDATA(node.getText());
 1090  6 break;
 1091  0 case Node.ENTITY_REFERENCE_NODE:
 1092  0 writeEntity((Entity) node);
 1093  0 break;
 1094  0 case Node.PROCESSING_INSTRUCTION_NODE:
 1095  0 writeProcessingInstruction((ProcessingInstruction) node);
 1096  0 break;
 1097  822 case Node.COMMENT_NODE:
 1098  822 writeComment(node.getText());
 1099  822 break;
 1100  2 case Node.DOCUMENT_NODE:
 1101  2 write((Document) node);
 1102  2 break;
 1103  0 case Node.DOCUMENT_TYPE_NODE:
 1104  0 writeDocType((DocumentType) node);
 1105  0 break;
 1106  180 case Node.NAMESPACE_NODE:
 1107    // Will be output with attributes
 1108    //write((Namespace) node);
 1109  180 break;
 1110  0 default:
 1111  0 throw new IOException( "Invalid node type: " + node );
 1112    }
 1113    }
 1114   
 1115   
 1116   
 1117   
 1118  0 protected void installLexicalHandler() {
 1119  0 XMLReader parent = getParent();
 1120  0 if (parent == null) {
 1121  0 throw new NullPointerException("No parent for filter");
 1122    }
 1123    // try to register for lexical events
 1124  0 for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
 1125  0 try {
 1126  0 parent.setProperty(LEXICAL_HANDLER_NAMES[i], this);
 1127  0 break;
 1128    }
 1129    catch (SAXNotRecognizedException ex) {
 1130    // ignore
 1131    }
 1132    catch (SAXNotSupportedException ex) {
 1133    // ignore
 1134    }
 1135    }
 1136    }
 1137   
 1138  0 protected void writeDocType(String name, String publicID, String systemID) throws IOException {
 1139  0 boolean hasPublic = false;
 1140   
 1141  0 writer.write("<!DOCTYPE ");
 1142  0 writer.write(name);
 1143  0 if ((publicID != null) && (!publicID.equals(""))) {
 1144  0 writer.write(" PUBLIC \"");
 1145  0 writer.write(publicID);
 1146  0 writer.write("\"");
 1147  0 hasPublic = true;
 1148    }
 1149  0 if ((systemID != null) && (!systemID.equals(""))) {
 1150  0 if (!hasPublic) {
 1151  0 writer.write(" SYSTEM");
 1152    }
 1153  0 writer.write(" \"");
 1154  0 writer.write(systemID);
 1155  0 writer.write("\"");
 1156    }
 1157  0 writer.write(">");
 1158  0 writePrintln();
 1159    }
 1160   
 1161  0 protected void writeEntity(Entity entity) throws IOException {
 1162  0 if (!resolveEntityRefs()) {
 1163  0 writeEntityRef( entity.getName() );
 1164    } else {
 1165  0 writer.write(entity.getText());
 1166    }
 1167    }
 1168   
 1169  0 protected void writeEntityRef(String name) throws IOException {
 1170  0 writer.write( "&" );
 1171  0 writer.write( name );
 1172  0 writer.write( ";" );
 1173   
 1174  0 lastOutputNodeType = Node.ENTITY_REFERENCE_NODE;
 1175    }
 1176   
 1177  822 protected void writeComment(String text) throws IOException {
 1178  822 if (format.isNewlines()) {
 1179  6 println();
 1180  6 indent();
 1181    }
 1182  822 writer.write( "<!--" );
 1183  822 writer.write( text );
 1184  822 writer.write( "-->" );
 1185   
 1186  822 lastOutputNodeType = Node.COMMENT_NODE;
 1187    }
 1188   
 1189    /** Writes the attributes of the given element
 1190    *
 1191    */
 1192  35438 protected void writeAttributes( Element element ) throws IOException {
 1193   
 1194    // I do not yet handle the case where the same prefix maps to
 1195    // two different URIs. For attributes on the same element
 1196    // this is illegal; but as yet we don't throw an exception
 1197    // if someone tries to do this
 1198  35438 for ( int i = 0, size = element.attributeCount(); i < size; i++ ) {
 1199  6230 Attribute attribute = element.attribute(i);
 1200  6230 Namespace ns = attribute.getNamespace();
 1201  6230 if (ns != null && ns != Namespace.NO_NAMESPACE && ns != Namespace.XML_NAMESPACE) {
 1202  24 String prefix = ns.getPrefix();
 1203  24 String uri = namespaceStack.getURI(prefix);
 1204  24 if (!ns.getURI().equals(uri)) { // output a new namespace declaration
 1205  0 writeNamespace(ns);
 1206  0 namespaceStack.push(ns);
 1207    }
 1208    }
 1209   
 1210    // If the attribute is a namespace declaration, check if we have already
 1211    // written that declaration elsewhere (if that's the case, it must be
 1212    // in the namespace stack
 1213  6230 String attName = attribute.getName();
 1214  6230 if (attName.startsWith("xmlns:")) {
 1215  6 String prefix = attName.substring(6);
 1216  6 if (namespaceStack.getNamespaceForPrefix(prefix) == null) {
 1217  4 String uri = attribute.getValue();
 1218  4 namespaceStack.push(prefix, uri);
 1219  4 writeNamespace(prefix, uri);
 1220    }
 1221  6224 } else if (attName.equals("xmlns")) {
 1222  2 if (namespaceStack.getDefaultNamespace() == null) {
 1223  0 String uri = attribute.getValue();
 1224  0 namespaceStack.push(null, uri);
 1225  0 writeNamespace(null, uri);
 1226    }
 1227    } else {
 1228  6222 char quote = format.getAttributeQuoteCharacter();
 1229  6222 writer.write(" ");
 1230  6222 writer.write(attribute.getQualifiedName());
 1231  6222 writer.write("=");
 1232  6222 writer.write(quote);
 1233  6222 writeEscapeAttributeEntities(attribute.getValue());
 1234  6222 writer.write(quote);
 1235    }
 1236    }
 1237    }
 1238   
 1239  0 protected void writeAttribute(Attribute attribute) throws IOException {
 1240  0 writer.write(" ");
 1241  0 writer.write(attribute.getQualifiedName());
 1242  0 writer.write("=");
 1243   
 1244  0 char quote = format.getAttributeQuoteCharacter();
 1245  0 writer.write(quote);
 1246   
 1247  0 writeEscapeAttributeEntities(attribute.getValue());
 1248   
 1249  0 writer.write(quote);
 1250  0 lastOutputNodeType = Node.ATTRIBUTE_NODE;
 1251    }
 1252   
 1253  12 protected void writeAttributes(Attributes attributes) throws IOException {
 1254  12 for (int i = 0, size = attributes.getLength(); i < size; i++) {
 1255  10 writeAttribute( attributes, i );
 1256    }
 1257    }
 1258   
 1259  10 protected void writeAttribute(Attributes attributes, int index) throws IOException {
 1260  10 char quote = format.getAttributeQuoteCharacter();
 1261  10 writer.write(" ");
 1262  10 writer.write(attributes.getQName(index));
 1263  10 writer.write("=");
 1264  10 writer.write(quote);
 1265  10 writeEscapeAttributeEntities(attributes.getValue(index));
 1266  10 writer.write(quote);
 1267    }
 1268   
 1269   
 1270   
 1271  43300 protected void indent() throws IOException {
 1272  43300 String indent = format.getIndent();
 1273  43300 if ( indent != null && indent.length() > 0 ) {
 1274  272 for ( int i = 0; i < indentLevel; i++ ) {
 1275  320 writer.write(indent);
 1276    }
 1277    }
 1278    }
 1279   
 1280    /**
 1281    * <p>
 1282    * This will print a new line only if the newlines flag was set to true
 1283    * </p>
 1284    */
 1285  43484 protected void writePrintln() throws IOException {
 1286  43484 if (format.isNewlines()) {
 1287  308 writer.write( format.getLineSeparator() );
 1288    }
 1289    }
 1290   
 1291    /**
 1292    * Get an OutputStreamWriter, use preferred encoding.
 1293    */
 1294  142 protected Writer createWriter(OutputStream outStream, String encoding) throws UnsupportedEncodingException {
 1295  142 return new BufferedWriter(
 1296    new OutputStreamWriter( outStream, encoding )
 1297    );
 1298    }
 1299   
 1300    /**
 1301    * <p>
 1302    * This will write the declaration to the given Writer.
 1303    * Assumes XML version 1.0 since we don't directly know.
 1304    * </p>
 1305    */
 1306  190 protected void writeDeclaration() throws IOException {
 1307  190 String encoding = format.getEncoding();
 1308   
 1309    // Only print of declaration is not suppressed
 1310  190 if (! format.isSuppressDeclaration()) {
 1311    // Assume 1.0 version
 1312  184 if (encoding.equals("UTF8")) {
 1313  0 writer.write("<?xml version=\"1.0\"");
 1314  0 if (!format.isOmitEncoding()) {
 1315  0 writer.write(" encoding=\"UTF-8\"");
 1316    }
 1317  0 writer.write("?>");
 1318    } else {
 1319  184 writer.write("<?xml version=\"1.0\"");
 1320  184 if (! format.isOmitEncoding()) {
 1321  184 writer.write(" encoding=\"" + encoding + "\"");
 1322    }
 1323  184 writer.write("?>");
 1324    }
 1325  184 if (format.isNewLineAfterDeclaration()) {
 1326  184 println();
 1327    }
 1328    }
 1329    }
 1330   
 1331  12 protected void writeClose(String qualifiedName) throws IOException {
 1332  12 writer.write("</");
 1333  12 writer.write(qualifiedName);
 1334  12 writer.write(">");
 1335    }
 1336   
 1337  426 protected void writeEmptyElementClose(String qualifiedName) throws IOException {
 1338    // Simply close up
 1339  426 if (! format.isExpandEmptyElements()) {
 1340  420 writer.write("/>");
 1341    } else {
 1342  6 writer.write("></");
 1343  6 writer.write(qualifiedName);
 1344  6 writer.write(">");
 1345    }
 1346    }
 1347   
 1348  0 protected boolean isExpandEmptyElements() {
 1349  0 return format.isExpandEmptyElements();
 1350    }
 1351   
 1352   
 1353    /** This will take the pre-defined entities in XML 1.0 and
 1354    * convert their character representation to the appropriate
 1355    * entity reference, suitable for XML attributes.
 1356    */
 1357  63048 protected String escapeElementEntities(String text) {
 1358  63048 char[] block = null;
 1359  63048 int i, last = 0, size = text.length();
 1360  63048 for ( i = 0; i < size; i++ ) {
 1361  728272 String entity = null;
 1362  728272 char c = text.charAt(i);
 1363  728272 switch( c ) {
 1364  24 case '<' :
 1365  24 entity = "&lt;";
 1366  24 break;
 1367  22 case '>' :
 1368  22 entity = "&gt;";
 1369  22 break;
 1370  36 case '&' :
 1371  36 entity = "&amp;";
 1372  36 break;
 1373  0 case '\t': case '\n': case '\r':
 1374    // don't encode standard whitespace characters
 1375  53806 if (preserve) {
 1376  4 entity=String.valueOf(c);
 1377    }
 1378  53806 break;
 1379  674384 default:
 1380  674384 if (c < 32 || shouldEncodeChar(c)) {
 1381  2 entity = "&#" + (int) c + ";";
 1382    }
 1383  674384 break;
 1384    }
 1385  728272 if (entity != null) {
 1386  88 if ( block == null ) {
 1387  68 block = text.toCharArray();
 1388    }
 1389  88 buffer.append(block, last, i - last);
 1390  88 buffer.append(entity);
 1391  88 last = i + 1;
 1392    }
 1393    }
 1394  63048 if ( last == 0 ) {
 1395  62980 return text;
 1396    }
 1397  68 if ( last < size ) {
 1398  20 if ( block == null ) {
 1399  0 block = text.toCharArray();
 1400    }
 1401  20 buffer.append(block, last, i - last);
 1402    }
 1403  68 String answer = buffer.toString();
 1404  68 buffer.setLength(0);
 1405  68 return answer;
 1406    }
 1407   
 1408   
 1409  6232 protected void writeEscapeAttributeEntities(String text) throws IOException {
 1410  6232 if ( text != null ) {
 1411  6232 String escapedText = escapeAttributeEntities( text );
 1412  6232 writer.write( escapedText );
 1413    }
 1414    }
 1415    /** This will take the pre-defined entities in XML 1.0 and
 1416    * convert their character representation to the appropriate
 1417    * entity reference, suitable for XML attributes.
 1418    */
 1419  6232 protected String escapeAttributeEntities(String text) {
 1420  6232 char quote = format.getAttributeQuoteCharacter();
 1421   
 1422  6232 char[] block = null;
 1423  6232 int i, last = 0, size = text.length();
 1424  6232 for ( i = 0; i < size; i++ ) {
 1425  73752 String entity = null;
 1426  73752 char c = text.charAt(i);
 1427  73752 switch( c ) {
 1428  0 case '<' :
 1429  0 entity = "&lt;";
 1430  0 break;
 1431  0 case '>' :
 1432  0 entity = "&gt;";
 1433  0 break;
 1434  2 case '\'' :
 1435  2 if (quote == '\'') {
 1436  0 entity = "&apos;";
 1437    }
 1438  2 break;
 1439  0 case '\"' :
 1440  0 if (quote == '\"') {
 1441  0 entity = "&quot;";
 1442    }
 1443  0 break;
 1444  0 case '&' :
 1445  0 entity = "&amp;";
 1446  0 break;
 1447  0 case '\t': case '\n': case '\r':
 1448    // don't encode standard whitespace characters
 1449  0 break;
 1450  73750 default:
 1451  73750 if (c < 32 || shouldEncodeChar(c)) {
 1452  0 entity = "&#" + (int) c + ";";
 1453    }
 1454  73750 break;
 1455    }
 1456  73752 if (entity != null) {
 1457  0 if ( block == null ) {
 1458  0 block = text.toCharArray();
 1459    }
 1460  0 buffer.append(block, last, i - last);
 1461  0 buffer.append(entity);
 1462  0 last = i + 1;
 1463    }
 1464    }
 1465  6232 if ( last == 0 ) {
 1466  6232 return text;
 1467    }
 1468  0 if ( last < size ) {
 1469  0 if ( block == null ) {
 1470  0 block = text.toCharArray();
 1471    }
 1472  0 buffer.append(block, last, i - last);
 1473    }
 1474  0 String answer = buffer.toString();
 1475  0 buffer.setLength(0);
 1476  0 return answer;
 1477    }
 1478   
 1479    /**
 1480    * Should the given character be escaped. This depends on the
 1481    * encoding of the document.
 1482    *
 1483    * @return boolean
 1484    */
 1485  748134 protected boolean shouldEncodeChar(char c) {
 1486  748134 int max = getMaximumAllowedCharacter();
 1487  748134 return max > 0 && c > max;
 1488    }
 1489   
 1490    /**
 1491    * Returns the maximum allowed character code that should be allowed
 1492    * unescaped which defaults to 127 in US-ASCII (7 bit) or
 1493    * 255 in ISO-* (8 bit).
 1494    */
 1495  4322 protected int defaultMaximumAllowedCharacter() {
 1496  4322 String encoding = format.getEncoding();
 1497  4322 if (encoding != null) {
 1498  4322 if (encoding.equals("US-ASCII")) {
 1499  0 return 127;
 1500    }
 1501    }
 1502    // no encoding for things like ISO-*, UTF-8 or UTF-16
 1503  4322 return -1;
 1504    }
 1505   
 1506  35618 protected boolean isNamespaceDeclaration( Namespace ns ) {
 1507  35618 if (ns != null && ns != Namespace.XML_NAMESPACE) {
 1508  35618 String uri = ns.getURI();
 1509  35618 if ( uri != null ) {
 1510  35618 if ( ! namespaceStack.contains( ns ) ) {
 1511  1194 return true;
 1512   
 1513    }
 1514    }
 1515    }
 1516  34424 return false;
 1517   
 1518    }
 1519   
 1520  0 protected void handleException(IOException e) throws SAXException {
 1521  0 throw new SAXException(e);
 1522    }
 1523   
 1524    //Laramie Crocker 4/8/2002 10:38AM
 1525    /** Lets subclasses get at the current format object, so they can call setTrimText, setNewLines, etc.
 1526    * Put in to support the HTMLWriter, in the way
 1527    * that it pushes the current newline/trim state onto a stack and overrides
 1528    * the state within preformatted tags.
 1529    */
 1530  14 protected OutputFormat getOutputFormat() {
 1531  14 return format;
 1532    }
 1533   
 1534  0 public boolean resolveEntityRefs() {
 1535  0 return resolveEntityRefs;
 1536    }
 1537   
 1538  0 public void setResolveEntityRefs(boolean resolve) {
 1539  0 this.resolveEntityRefs = resolve;
 1540    }
 1541    }
 1542   
 1543   
 1544   
 1545   
 1546    /*
 1547    * Redistribution and use of this software and associated documentation
 1548    * ("Software"), with or without modification, are permitted provided
 1549    * that the following conditions are met:
 1550    *
 1551    * 1. Redistributions of source code must retain copyright
 1552    * statements and notices. Redistributions must also contain a
 1553    * copy of this document.
 1554    *
 1555    * 2. Redistributions in binary form must reproduce the
 1556    * above copyright notice, this list of conditions and the
 1557    * following disclaimer in the documentation and/or other
 1558    * materials provided with the distribution.
 1559    *
 1560    * 3. The name "DOM4J" must not be used to endorse or promote
 1561    * products derived from this Software without prior written
 1562    * permission of MetaStuff, Ltd. For written permission,
 1563    * please contact dom4j-info@metastuff.com.
 1564    *
 1565    * 4. Products derived from this Software may not be called "DOM4J"
 1566    * nor may "DOM4J" appear in their names without prior written
 1567    * permission of MetaStuff, Ltd. DOM4J is a registered
 1568    * trademark of MetaStuff, Ltd.
 1569    *
 1570    * 5. Due credit should be given to the DOM4J Project -
 1571    * http://www.dom4j.org
 1572    *
 1573    * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
 1574    * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
 1575    * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 1576    * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 1577    * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 1578    * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 1579    * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 1580    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 1581    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 1582    * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 1583    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 1584    * OF THE POSSIBILITY OF SUCH DAMAGE.
 1585    *
 1586    * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
 1587    *
 1588    * $Id: XMLWriter.java,v 1.76 2004/09/01 19:37:56 maartenc Exp $
 1589    */