1 package org.dom4j.io; 2 3 import org.xml.sax.helpers.XMLFilterImpl ; 4 import org.xml.sax.ext.LexicalHandler ; 5 import org.xml.sax.*; 6 import org.dom4j.tree.NamespaceStack; 7 import org.dom4j.*; 8 9 import java.io.*; 10 import java.util.*; 11 12 16 public class XMLWriter extends XMLFilterImpl implements LexicalHandler { 17 18 private static final String PAD_TEXT = " "; 19 20 protected static final String [] LEXICAL_HANDLER_NAMES = { 21 "http://xml.org/sax/properties/lexical-handler", 22 "http://xml.org/sax/handlers/LexicalHandler" 23 }; 24 25 protected static final OutputFormat DEFAULT_FORMAT = new OutputFormat(); 26 27 28 private boolean resolveEntityRefs = true; 29 30 32 protected int lastOutputNodeType; 33 34 35 protected boolean preserve=false; 36 37 38 protected Writer writer; 39 40 41 private NamespaceStack namespaceStack = new NamespaceStack(); 42 43 44 private OutputFormat format; 45 46 47 private boolean escapeText = true; 48 50 private int indentLevel = 0; 51 52 53 private StringBuilder buffer = new StringBuilder (); 54 55 56 private boolean charactersAdded = false; 57 private char lastChar; 58 59 60 private boolean autoFlush; 61 62 63 private LexicalHandler lexicalHandler; 64 65 66 private boolean showCommentsInDTDs; 67 68 69 private boolean inDTD; 70 71 72 private Map namespacesMap; 73 74 79 private int maximumAllowedCharacter; 80 81 82 public XMLWriter(Writer writer) { 83 this( writer, DEFAULT_FORMAT ); 84 } 85 86 public XMLWriter(Writer writer, OutputFormat format) { 87 this.writer = writer; 88 this.format = format; 89 namespaceStack.push(Namespace.NO_NAMESPACE); 90 } 91 92 public XMLWriter() { 93 this.format = DEFAULT_FORMAT; 94 this.writer = new BufferedWriter( new OutputStreamWriter( System.out ) ); 95 this.autoFlush = true; 96 namespaceStack.push(Namespace.NO_NAMESPACE); 97 } 98 99 public XMLWriter(OutputStream out) throws UnsupportedEncodingException { 100 this.format = DEFAULT_FORMAT; 101 this.writer = createWriter(out, format.getEncoding()); 102 this.autoFlush = true; 103 namespaceStack.push(Namespace.NO_NAMESPACE); 104 } 105 106 public XMLWriter(OutputStream out, OutputFormat format) throws UnsupportedEncodingException { 107 this.format = format; 108 this.writer = createWriter(out, format.getEncoding()); 109 this.autoFlush = true; 110 namespaceStack.push(Namespace.NO_NAMESPACE); 111 } 112 113 public XMLWriter(OutputFormat format) throws UnsupportedEncodingException { 114 this.format = format; 115 this.writer = createWriter( System.out, format.getEncoding() ); 116 this.autoFlush = true; 117 namespaceStack.push(Namespace.NO_NAMESPACE); 118 } 119 120 public void setWriter(Writer writer) { 121 this.writer = writer; 122 this.autoFlush = false; 123 } 124 125 public void setOutputStream(OutputStream out) throws UnsupportedEncodingException { 126 this.writer = createWriter(out, format.getEncoding()); 127 this.autoFlush = true; 128 } 129 130 136 public boolean isEscapeText() { 137 return escapeText; 138 } 139 140 146 public void setEscapeText(boolean escapeText) { 147 this.escapeText = escapeText; 148 } 149 150 151 158 public void setIndentLevel(int indentLevel) { 159 this.indentLevel = indentLevel; 160 } 161 162 167 public int getMaximumAllowedCharacter() { 168 if (maximumAllowedCharacter == 0) { 169 maximumAllowedCharacter = defaultMaximumAllowedCharacter(); 170 } 171 return maximumAllowedCharacter; 172 } 173 174 184 public void setMaximumAllowedCharacter(int maximumAllowedCharacter) { 185 this.maximumAllowedCharacter = maximumAllowedCharacter; 186 } 187 188 189 public void flush() throws IOException { 190 writer.flush(); 191 } 192 193 194 public void close() throws IOException { 195 writer.close(); 196 } 197 198 199 public void println() throws IOException { 200 writer.write( format.getLineSeparator() ); 201 } 202 203 207 public void write(Attribute attribute) throws IOException { 208 writeAttribute(attribute); 209 210 if ( autoFlush ) { 211 flush(); 212 } 213 } 214 215 216 229 public void write(Document doc) throws IOException { 230 writeDeclaration(); 231 232 if (doc.getDocType() != null) { 233 indent(); 234 writeDocType(doc.getDocType()); 235 } 236 237 for ( int i = 0, size = doc.nodeCount(); i < size; i++ ) { 238 Node node = doc.node(i); 239 writeNode( node ); 240 } 241 writePrintln(); 242 243 if ( autoFlush ) { 244 flush(); 245 } 246 } 247 248 254 public void write(Element element) throws IOException { 255 writeElement(element); 256 257 if ( autoFlush ) { 258 flush(); 259 } 260 } 261 262 263 267 public void write(CDATA cdata) throws IOException { 268 writeCDATA( cdata.getText() ); 269 270 if ( autoFlush ) { 271 flush(); 272 } 273 } 274 275 279 public void write(Comment comment) throws IOException { 280 writeComment( comment.getText() ); 281 282 if ( autoFlush ) { 283 flush(); 284 } 285 } 286 287 291 public void write(DocumentType docType) throws IOException { 292 writeDocType(docType); 293 294 if ( autoFlush ) { 295 flush(); 296 } 297 } 298 299 300 304 public void write(Entity entity) throws IOException { 305 writeEntity( entity ); 306 307 if ( autoFlush ) { 308 flush(); 309 } 310 } 311 312 313 317 public void write(Namespace namespace) throws IOException { 318 writeNamespace(namespace); 319 320 if ( autoFlush ) { 321 flush(); 322 } 323 } 324 325 329 public void write(ProcessingInstruction processingInstruction) throws IOException { 330 writeProcessingInstruction(processingInstruction); 331 332 if ( autoFlush ) { 333 flush(); 334 } 335 } 336 337 342 public void write(String text) throws IOException { 343 writeString(text); 344 345 if ( autoFlush ) { 346 flush(); 347 } 348 } 349 350 354 public void write(Text text) throws IOException { 355 writeString(text.getText()); 356 357 if ( autoFlush ) { 358 flush(); 359 } 360 } 361 362 366 public void write(Node node) throws IOException { 367 writeNode(node); 368 369 if ( autoFlush ) { 370 flush(); 371 } 372 } 373 374 379 public void write(Object object) throws IOException { 380 if (object instanceof Node) { 381 write((Node) object); 382 } 383 else if (object instanceof String ) { 384 write((String ) object); 385 } 386 else if (object instanceof List) { 387 List list = (List) object; 388 for ( int i = 0, size = list.size(); i < size; i++ ) { 389 write( list.get(i) ); 390 } 391 } 392 else if (object != null) { 393 throw new IOException( "Invalid object: " + object ); 394 } 395 } 396 397 398 404 public void writeOpen(Element element) throws IOException { 405 writer.write("<"); 406 writer.write( element.getQualifiedName() ); 407 writeAttributes(element); 408 writer.write(">"); 409 } 410 411 415 public void writeClose(Element element) throws IOException { 416 writeClose( element.getQualifiedName() ); 417 } 418 419 420 public void parse(InputSource source) throws IOException, SAXException { 423 installLexicalHandler(); 424 super.parse(source); 425 } 426 427 428 public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException { 429 for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) { 430 if (LEXICAL_HANDLER_NAMES[i].equals(name)) { 431 setLexicalHandler((LexicalHandler ) value); 432 return; 433 } 434 } 435 super.setProperty(name, value); 436 } 437 438 public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException { 439 for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) { 440 if (LEXICAL_HANDLER_NAMES[i].equals(name)) { 441 return getLexicalHandler(); 442 } 443 } 444 return super.getProperty(name); 445 } 446 447 public void setLexicalHandler (LexicalHandler handler) { 448 if (handler == null) { 449 throw new NullPointerException ("Null lexical handler"); 450 } 451 else { 452 this.lexicalHandler = handler; 453 } 454 } 455 456 public LexicalHandler getLexicalHandler(){ 457 return lexicalHandler; 458 } 459 460 461 public void setDocumentLocator(Locator locator) { 464 super.setDocumentLocator(locator); 465 } 466 467 public void startDocument() throws SAXException { 468 try { 469 writeDeclaration(); 470 super.startDocument(); 471 } 472 catch (IOException e) { 473 handleException(e); 474 } 475 } 476 477 public void endDocument() throws SAXException { 478 super.endDocument(); 479 480 if ( autoFlush ) { 481 try { 482 flush(); 483 } catch ( IOException e) {} 484 } 485 } 486 487 public void startPrefixMapping(String prefix, String uri) throws SAXException { 488 if ( namespacesMap == null ) { 489 namespacesMap = new HashMap(); 490 } 491 namespacesMap.put(prefix, uri); 492 super.startPrefixMapping(prefix, uri); 493 } 494 495 public void endPrefixMapping(String prefix) throws SAXException { 496 super.endPrefixMapping(prefix); 497 } 498 499 public void startElement(String namespaceURI, String localName, String qName, Attributes attributes) throws SAXException { 500 try { 501 charactersAdded = false; 502 503 writePrintln(); 504 indent(); 505 writer.write("<"); 506 writer.write(qName); 507 writeNamespaces(); 508 writeAttributes( attributes ); 509 writer.write(">"); 510 ++indentLevel; 511 lastOutputNodeType = Node.ELEMENT_NODE; 512 513 super.startElement( namespaceURI, localName, qName, attributes ); 514 } 515 catch (IOException e) { 516 handleException(e); 517 } 518 } 519 520 public void endElement(String namespaceURI, String localName, String qName) throws SAXException { 521 try { 522 charactersAdded = false; 523 --indentLevel; 524 if ( lastOutputNodeType == Node.ELEMENT_NODE ) { 525 writePrintln(); 526 indent(); 527 } 528 529 boolean hadContent = true; 532 if ( hadContent ) { 533 writeClose(qName); 534 } 535 else { 536 writeEmptyElementClose(qName); 537 } 538 lastOutputNodeType = Node.ELEMENT_NODE; 539 540 super.endElement( namespaceURI, localName, qName ); 541 } 542 catch (IOException e) { 543 handleException(e); 544 } 545 } 546 547 public void characters(char[] ch, int start, int length) throws SAXException { 548 if (ch == null || ch.length == 0 || length <= 0) { 549 return; 550 } 551 552 try { 553 559 String string = new String (ch, start, length); 560 561 if (escapeText) { 562 string = escapeElementEntities(string); 563 } 564 565 if (format.isTrimText()) { 566 if ((lastOutputNodeType == Node.TEXT_NODE) && !charactersAdded) { 567 writer.write(" "); 568 } else if (charactersAdded && Character.isWhitespace(lastChar)) { 569 writer.write(lastChar); 570 } 571 572 String delim = ""; 573 StringTokenizer tokens = new StringTokenizer(string); 574 while (tokens.hasMoreTokens()) { 575 writer.write(delim); 576 writer.write(tokens.nextToken()); 577 delim = " "; 578 } 579 } else { 580 writer.write(string); 581 } 582 583 charactersAdded = true; 584 lastChar = ch[start + length - 1]; 585 lastOutputNodeType = Node.TEXT_NODE; 586 587 super.characters(ch, start, length); 588 } 589 catch (IOException e) { 590 handleException(e); 591 } 592 } 593 594 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { 595 super.ignorableWhitespace(ch, start, length); 596 } 597 598 public void processingInstruction(String target, String data) throws SAXException { 599 try { 600 indent(); 601 writer.write("<?"); 602 writer.write(target); 603 writer.write(" "); 604 writer.write(data); 605 writer.write("?>"); 606 writePrintln(); 607 lastOutputNodeType = Node.PROCESSING_INSTRUCTION_NODE; 608 609 super.processingInstruction(target, data); 610 } 611 catch (IOException e) { 612 handleException(e); 613 } 614 } 615 616 617 618 public void notationDecl(String name, String publicID, String systemID) throws SAXException { 621 super.notationDecl(name, publicID, systemID); 622 } 623 624 public void unparsedEntityDecl(String name, String publicID, String systemID, String notationName) throws SAXException { 625 super.unparsedEntityDecl(name, publicID, systemID, notationName); 626 } 627 628 629 public void startDTD(String name, String publicID, String systemID) throws SAXException { 632 inDTD = true; 633 try { 634 writeDocType(name, publicID, systemID); 635 } 636 catch (IOException e) { 637 handleException(e); 638 } 639 640 if (lexicalHandler != null) { 641 lexicalHandler.startDTD(name, publicID, systemID); 642 } 643 } 644 645 public void endDTD() throws SAXException { 646 inDTD = false; 647 if (lexicalHandler != null) { 648 lexicalHandler.endDTD(); 649 } 650 } 651 652 public void startCDATA() throws SAXException { 653 try { 654 writer.write( "<![CDATA[" ); 655 } 656 catch (IOException e) { 657 handleException(e); 658 } 659 660 if (lexicalHandler != null) { 661 lexicalHandler.startCDATA(); 662 } 663 } 664 665 public void endCDATA() throws SAXException { 666 try { 667 writer.write( "]]>" ); 668 } 669 catch (IOException e) { 670 handleException(e); 671 } 672 673 if (lexicalHandler != null) { 674 lexicalHandler.endCDATA(); 675 } 676 } 677 678 public void startEntity(String name) throws SAXException { 679 try { 680 writeEntityRef(name); 681 } 682 catch (IOException e) { 683 handleException(e); 684 } 685 686 if (lexicalHandler != null) { 687 lexicalHandler.startEntity(name); 688 } 689 } 690 691 public void endEntity(String name) throws SAXException { 692 if (lexicalHandler != null) { 693 lexicalHandler.endEntity(name); 694 } 695 } 696 697 public void comment(char[] ch, int start, int length) throws SAXException { 698 if ( showCommentsInDTDs || ! inDTD ) { 699 try { 700 charactersAdded = false; 701 writeComment( new String (ch, start, length) ); 702 } 703 catch (IOException e) { 704 handleException(e); 705 } 706 } 707 708 if (lexicalHandler != null) { 709 lexicalHandler.comment(ch, start, length); 710 } 711 } 712 713 714 715 protected void writeElement(Element element) throws IOException { 718 int size = element.nodeCount(); 719 String qualifiedName = element.getQualifiedName(); 720 721 writePrintln(); 722 indent(); 723 724 writer.write("<"); 725 writer.write(qualifiedName); 726 727 int previouslyDeclaredNamespaces = namespaceStack.size(); 728 Namespace ns = element.getNamespace(); 729 if (isNamespaceDeclaration( ns ) ) { 730 namespaceStack.push(ns); 731 writeNamespace(ns); 732 } 733 734 boolean textOnly = true; 736 for ( int i = 0; i < size; i++ ) { 737 Node node = element.node(i); 738 if ( node instanceof Namespace ) { 739 Namespace additional = (Namespace) node; 740 if (isNamespaceDeclaration( additional ) ) { 741 namespaceStack.push(additional); 742 writeNamespace(additional); 743 } 744 } 745 else if ( node instanceof Element) { 746 textOnly = false; 747 } 748 else if ( node instanceof Comment) { 749 textOnly = false; 750 } 751 } 752 753 writeAttributes(element); 754 755 lastOutputNodeType = Node.ELEMENT_NODE; 756 757 if ( size <= 0 ) { 758 writeEmptyElementClose(qualifiedName); 759 } 760 else { 761 writer.write(">"); 762 if ( textOnly ) { 763 writeElementContent(element); 766 } 767 else { 768 ++indentLevel; 770 771 writeElementContent(element); 772 773 --indentLevel; 774 775 writePrintln(); 776 indent(); 777 } 778 writer.write("</"); 779 writer.write(qualifiedName); 780 writer.write(">"); 781 } 782 783 while (namespaceStack.size() > previouslyDeclaredNamespaces) { 785 namespaceStack.pop(); 786 } 787 788 lastOutputNodeType = Node.ELEMENT_NODE; 789 } 790 791 796 protected final boolean isElementSpacePreserved(Element element) { 797 final Attribute attr = (Attribute)element.attribute("space"); 798 boolean preserveFound=preserve; if (attr!=null) { 800 if ("xml".equals(attr.getNamespacePrefix()) && 801 "preserve".equals(attr.getText())) { 802 preserveFound = true; 803 } 804 else { 805 preserveFound = false; 806 } 807 } 808 return preserveFound; 809 } 810 816 protected void writeElementContent(Element element) throws IOException { 817 boolean trim = format.isTrimText(); 818 boolean oldPreserve=preserve; 819 if (trim) { preserve=isElementSpacePreserved(element); 821 trim = !preserve; 822 } 823 if (trim) { 824 Text lastTextNode = null; 827 StringBuilder buffer = null; 828 boolean textOnly = true; 829 for ( int i = 0, size = element.nodeCount(); i < size; i++ ) { 830 Node node = element.node(i); 831 if ( node instanceof Text ) { 832 if ( lastTextNode == null ) { 833 lastTextNode = (Text) node; 834 } 835 else { 836 if (buffer == null) { 837 buffer = new StringBuilder ( lastTextNode.getText() ); 838 } 839 buffer.append( ((Text) node).getText() ); 840 } 841 } 842 else { 843 if (!textOnly && format.isPadText()) { 844 writer.write(PAD_TEXT); 845 } 846 847 textOnly = false; 848 849 if ( lastTextNode != null ) { 850 if ( buffer != null ) { 851 writeString( buffer.toString() ); 852 buffer = null; 853 } 854 else { 855 writeString( lastTextNode.getText() ); 856 } 857 lastTextNode = null; 858 859 if (format.isPadText()) { 860 writer.write(PAD_TEXT); 861 } 862 } 863 writeNode(node); 864 } 865 } 866 if ( lastTextNode != null ) { 867 if (!textOnly && format.isPadText()) { 868 writer.write(PAD_TEXT); 869 } 870 if ( buffer != null ) { 871 writeString( buffer.toString() ); 872 buffer = null; 873 } 874 else { 875 writeString( lastTextNode.getText() ); 876 } 877 lastTextNode = null; 878 } 879 } 880 else { 881 Node lastTextNode = null; 882 for ( int i = 0, size = element.nodeCount(); i < size; i++ ) { 883 Node node = element.node(i); 884 if (node instanceof Text) { 885 writeNode(node); 886 lastTextNode = node; 887 } else { 888 if ((lastTextNode != null) && format.isPadText()) { 889 writer.write(PAD_TEXT); 890 } 891 writeNode(node); 892 if ((lastTextNode != null) && format.isPadText()) { 893 writer.write(PAD_TEXT); 894 } 895 lastTextNode = null; 896 } 897 } 898 } 899 preserve=oldPreserve; 900 } 901 protected void writeCDATA(String text) throws IOException { 902 writer.write( "<![CDATA[" ); 903 if (text != null) { 904 writer.write( text ); 905 } 906 writer.write( "]]>" ); 907 908 lastOutputNodeType = Node.CDATA_SECTION_NODE; 909 } 910 911 protected void writeDocType(DocumentType docType) throws IOException { 912 if (docType != null) { 913 docType.write( writer ); 914 writePrintln(); 916 } 917 } 918 919 920 protected void writeNamespace(Namespace namespace) throws IOException { 921 if ( namespace != null ) { 922 writeNamespace(namespace.getPrefix(), namespace.getURI()); 923 } 924 } 925 926 929 protected void writeNamespaces() throws IOException { 930 if ( namespacesMap != null ) { 931 for ( Iterator iter = namespacesMap.entrySet().iterator(); iter.hasNext(); ) { 932 Map.Entry entry = (Map.Entry) iter.next(); 933 String prefix = (String ) entry.getKey(); 934 String uri = (String ) entry.getValue(); 935 writeNamespace(prefix, uri); 936 } 937 namespacesMap = null; 938 } 939 } 940 941 944 protected void writeNamespace(String prefix, String uri) throws IOException { 945 if ( prefix != null && prefix.length() > 0 ) { 946 writer.write(" xmlns:"); 947 writer.write(prefix); 948 writer.write("=\""); 949 } 950 else { 951 writer.write(" xmlns=\""); 952 } 953 writer.write(uri); 954 writer.write("\""); 955 } 956 957 protected void writeProcessingInstruction(ProcessingInstruction processingInstruction) throws IOException { 958 writer.write( "<?" ); 960 writer.write( processingInstruction.getName() ); 961 writer.write( " " ); 962 writer.write( processingInstruction.getText() ); 963 writer.write( "?>" ); 964 writePrintln(); 965 966 lastOutputNodeType = Node.PROCESSING_INSTRUCTION_NODE; 967 } 968 969 protected void writeString(String text) throws IOException { 970 if ( text != null && text.length() > 0 ) { 971 if ( escapeText ) { 972 text = escapeElementEntities(text); 973 } 974 975 981 if (format.isTrimText()) { 982 boolean first = true; 983 StringTokenizer tokenizer = new StringTokenizer(text); 984 while (tokenizer.hasMoreTokens()) { 985 String token = tokenizer.nextToken(); 986 if ( first ) { 987 first = false; 988 if ( lastOutputNodeType == Node.TEXT_NODE ) { 989 writer.write(" "); 990 } 991 } 992 else { 993 writer.write(" "); 994 } 995 writer.write(token); 996 lastOutputNodeType = Node.TEXT_NODE; 997 } 998 } 999 else { 1000 lastOutputNodeType = Node.TEXT_NODE; 1001 writer.write(text); 1002 } 1003 } 1004 } 1005 1006 1011 protected void writeNodeText(Node node) throws IOException { 1012 String text = node.getText(); 1013 if (text != null && text.length() > 0) { 1014 if (escapeText) { 1015 text = escapeElementEntities(text); 1016 } 1017 1018 lastOutputNodeType = Node.TEXT_NODE; 1019 writer.write(text); 1020 } 1021 } 1022 1023 protected void writeNode(Node node) throws IOException { 1024 int nodeType = node.getNodeType(); 1025 switch (nodeType) { 1026 case Node.ELEMENT_NODE: 1027 writeElement((Element) node); 1028 break; 1029 case Node.ATTRIBUTE_NODE: 1030 writeAttribute((Attribute) node); 1031 break; 1032 case Node.TEXT_NODE: 1033 writeNodeText(node); 1034 break; 1036 case Node.CDATA_SECTION_NODE: 1037 writeCDATA(node.getText()); 1038 break; 1039 case Node.ENTITY_REFERENCE_NODE: 1040 writeEntity((Entity) node); 1041 break; 1042 case Node.PROCESSING_INSTRUCTION_NODE: 1043 writeProcessingInstruction((ProcessingInstruction) node); 1044 break; 1045 case Node.COMMENT_NODE: 1046 writeComment(node.getText()); 1047 break; 1048 case Node.DOCUMENT_NODE: 1049 write((Document) node); 1050 break; 1051 case Node.DOCUMENT_TYPE_NODE: 1052 writeDocType((DocumentType) node); 1053 break; 1054 case Node.NAMESPACE_NODE: 1055 break; 1058 default: 1059 throw new IOException( "Invalid node type: " + node ); 1060 } 1061 } 1062 1063 1064 1065 1066 protected void installLexicalHandler() { 1067 XMLReader parent = getParent(); 1068 if (parent == null) { 1069 throw new NullPointerException ("No parent for filter"); 1070 } 1071 for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) { 1073 try { 1074 parent.setProperty(LEXICAL_HANDLER_NAMES[i], this); 1075 break; 1076 } 1077 catch (SAXNotRecognizedException ex) { 1078 } 1080 catch (SAXNotSupportedException ex) { 1081 } 1083 } 1084 } 1085 1086 protected void writeDocType(String name, String publicID, String systemID) throws IOException { 1087 boolean hasPublic = false; 1088 1089 writer.write("<!DOCTYPE "); 1090 writer.write(name); 1091 if ((publicID != null) && (!publicID.equals(""))) { 1092 writer.write(" PUBLIC \""); 1093 writer.write(publicID); 1094 writer.write("\""); 1095 hasPublic = true; 1096 } 1097 if ((systemID != null) && (!systemID.equals(""))) { 1098 if (!hasPublic) { 1099 writer.write(" SYSTEM"); 1100 } 1101 writer.write(" \""); 1102 writer.write(systemID); 1103 writer.write("\""); 1104 } 1105 writer.write(">"); 1106 writePrintln(); 1107 } 1108 1109 protected void writeEntity(Entity entity) throws IOException { 1110 if (!resolveEntityRefs()) { 1111 writeEntityRef( entity.getName() ); 1112 } else { 1113 writer.write(entity.getText()); 1114 } 1115 } 1116 1117 protected void writeEntityRef(String name) throws IOException { 1118 writer.write( "&" ); 1119 writer.write( name ); 1120 writer.write( ";" ); 1121 1122 lastOutputNodeType = Node.ENTITY_REFERENCE_NODE; 1123 } 1124 1125 protected void writeComment(String text) throws IOException { 1126 if (format.isNewlines()) { 1127 println(); 1128 indent(); 1129 } 1130 writer.write( "<!--" ); 1131 writer.write( text ); 1132 writer.write( "-->" ); 1133 1134 lastOutputNodeType = Node.COMMENT_NODE; 1135 } 1136 1137 1140 protected void writeAttributes( Element element ) throws IOException { 1141 1142 for ( int i = 0, size = element.attributeCount(); i < size; i++ ) { 1147 Attribute attribute = element.attribute(i); 1148 Namespace ns = attribute.getNamespace(); 1149 if (ns != null && ns != Namespace.NO_NAMESPACE && ns != Namespace.XML_NAMESPACE) { 1150 String prefix = ns.getPrefix(); 1151 String uri = namespaceStack.getURI(prefix); 1152 if (!ns.getURI().equals(uri)) { writeNamespace(ns); 1154 namespaceStack.push(ns); 1155 } 1156 } 1157 1158 String attName = attribute.getName(); 1162 if (attName.startsWith("xmlns:")) { 1163 String prefix = attName.substring(6); 1164 if (namespaceStack.getNamespaceForPrefix(prefix) == null) { 1165 String uri = attribute.getValue(); 1166 namespaceStack.push(prefix, uri); 1167 writeNamespace(prefix, uri); 1168 } 1169 } else if (attName.equals("xmlns")) { 1170 if (namespaceStack.getDefaultNamespace() == null) { 1171 String uri = attribute.getValue(); 1172 namespaceStack.push(null, uri); 1173 writeNamespace(null, uri); 1174 } 1175 } else { 1176 char quote = format.getAttributeQuoteCharacter(); 1177 writer.write(" "); 1178 writer.write(attribute.getQualifiedName()); 1179 writer.write("="); 1180 writer.write(quote); 1181 writeEscapeAttributeEntities(attribute.getValue()); 1182 writer.write(quote); 1183 } 1184 } 1185 } 1186 1187 protected void writeAttribute(Attribute attribute) throws IOException { 1188 writer.write(" "); 1189 writer.write(attribute.getQualifiedName()); 1190 writer.write("="); 1191 1192 char quote = format.getAttributeQuoteCharacter(); 1193 writer.write(quote); 1194 1195 writeEscapeAttributeEntities(attribute.getValue()); 1196 1197 writer.write(quote); 1198 lastOutputNodeType = Node.ATTRIBUTE_NODE; 1199 } 1200 1201 protected void writeAttributes(Attributes attributes) throws IOException { 1202 for (int i = 0, size = attributes.getLength(); i < size; i++) { 1203 writeAttribute( attributes, i ); 1204 } 1205 } 1206 1207 protected void writeAttribute(Attributes attributes, int index) throws IOException { 1208 char quote = format.getAttributeQuoteCharacter(); 1209 writer.write(" "); 1210 writer.write(attributes.getQName(index)); 1211 writer.write("="); 1212 writer.write(quote); 1213 writeEscapeAttributeEntities(attributes.getValue(index)); 1214 writer.write(quote); 1215 } 1216 1217 1218 1219 protected void indent() throws IOException { 1220 String indent = format.getIndent(); 1221 if ( indent != null && indent.length() > 0 ) { 1222 for ( int i = 0; i < indentLevel; i++ ) { 1223 writer.write(indent); 1224 } 1225 } 1226 } 1227 1228 1233 protected void writePrintln() throws IOException { 1234 if (format.isNewlines()) { 1235 writer.write( format.getLineSeparator() ); 1236 } 1237 } 1238 1239 1242 protected Writer createWriter(OutputStream outStream, String encoding) throws UnsupportedEncodingException { 1243 return new BufferedWriter( 1244 new OutputStreamWriter( outStream, encoding ) 1245 ); 1246 } 1247 1248 1254 protected void writeDeclaration() throws IOException { 1255 String encoding = format.getEncoding(); 1256 1257 if (! format.isSuppressDeclaration()) { 1259 if (encoding.equals("UTF8")) { 1261 writer.write("<?xml version=\"1.0\""); 1262 if (!format.isOmitEncoding()) { 1263 writer.write(" encoding=\"UTF-8\""); 1264 } 1265 writer.write("?>"); 1266 } else { 1267 writer.write("<?xml version=\"1.0\""); 1268 if (! format.isOmitEncoding()) { 1269 writer.write(" encoding=\"" + encoding + "\""); 1270 } 1271 writer.write("?>"); 1272 } 1273 if (format.isNewLineAfterDeclaration()) { 1274 println(); 1275 } 1276 } 1277 } 1278 1279 protected void writeClose(String qualifiedName) throws IOException { 1280 writer.write("</"); 1281 writer.write(qualifiedName); 1282 writer.write(">"); 1283 } 1284 1285 protected void writeEmptyElementClose(String qualifiedName) throws IOException { 1286 if (! format.isExpandEmptyElements()) { 1288 writer.write("/>"); 1289 } else { 1290 writer.write("></"); 1291 writer.write(qualifiedName); 1292 writer.write(">"); 1293 } 1294 } 1295 1296 protected boolean isExpandEmptyElements() { 1297 return format.isExpandEmptyElements(); 1298 } 1299 1300 1301 1305 protected String escapeElementEntities(String text) { 1306 char[] block = null; 1307 int i, last = 0, size = text.length(); 1308 for ( i = 0; i < size; i++ ) { 1309 String entity = null; 1310 char c = text.charAt(i); 1311 switch( c ) { 1312 case '<' : 1313 entity = "<"; 1314 break; 1315 case '>' : 1316 entity = ">"; 1317 break; 1318 case '&' : 1319 entity = "&"; 1320 break; 1321 case '\t': case '\n': case '\r': 1322 if (preserve) { 1324 entity=String.valueOf(c); 1325 } 1326 break; 1327 default: 1328 if (c < 32 || shouldEncodeChar(c)) { 1329 entity = "&#" + (int) c + ";"; 1330 } 1331 break; 1332 } 1333 if (entity != null) { 1334 if ( block == null ) { 1335 block = text.toCharArray(); 1336 } 1337 buffer.append(block, last, i - last); 1338 buffer.append(entity); 1339 last = i + 1; 1340 } 1341 } 1342 if ( last == 0 ) { 1343 return text; 1344 } 1345 if ( last < size ) { 1346 if ( block == null ) { 1347 block = text.toCharArray(); 1348 } 1349 buffer.append(block, last, i - last); 1350 } 1351 String answer = buffer.toString(); 1352 buffer.setLength(0); 1353 return answer; 1354 } 1355 1356 1357 protected void writeEscapeAttributeEntities(String text) throws IOException { 1358 if ( text != null ) { 1359 String escapedText = escapeAttributeEntities( text ); 1360 writer.write( escapedText ); 1361 } 1362 } 1363 1367 protected String escapeAttributeEntities(String text) { 1368 char quote = format.getAttributeQuoteCharacter(); 1369 1370 char[] block = null; 1371 int i, last = 0, size = text.length(); 1372 for ( i = 0; i < size; i++ ) { 1373 String entity = null; 1374 char c = text.charAt(i); 1375 switch( c ) { 1376 case '<' : 1377 entity = "<"; 1378 break; 1379 case '>' : 1380 entity = ">"; 1381 break; 1382 case '\'' : 1383 if (quote == '\'') { 1384 entity = "'"; 1385 } 1386 break; 1387 case '\"' : 1388 if (quote == '\"') { 1389 entity = """; 1390 } 1391 break; 1392 case '&' : 1393 entity = "&"; 1394 break; 1395 case '\t': case '\n': case '\r': 1396 break; 1398 default: 1399 if (c < 32 || shouldEncodeChar(c)) { 1400 entity = "&#" + (int) c + ";"; 1401 } 1402 break; 1403 } 1404 if (entity != null) { 1405 if ( block == null ) { 1406 block = text.toCharArray(); 1407 } 1408 buffer.append(block, last, i - last); 1409 buffer.append(entity); 1410 last = i + 1; 1411 } 1412 } 1413 if ( last == 0 ) { 1414 return text; 1415 } 1416 if ( last < size ) { 1417 if ( block == null ) { 1418 block = text.toCharArray(); 1419 } 1420 buffer.append(block, last, i - last); 1421 } 1422 String answer = buffer.toString(); 1423 buffer.setLength(0); 1424 return answer; 1425 } 1426 1427 1433 protected boolean shouldEncodeChar(char c) { 1434 int max = getMaximumAllowedCharacter(); 1435 return max > 0 && c > max; 1436 } 1437 1438 1443 protected int defaultMaximumAllowedCharacter() { 1444 String encoding = format.getEncoding(); 1445 if (encoding != null) { 1446 if (encoding.equals("US-ASCII")) { 1447 return 127; 1448 } 1449 } 1450 return -1; 1452 } 1453 1454 protected boolean isNamespaceDeclaration( Namespace ns ) { 1455 if (ns != null && ns != Namespace.XML_NAMESPACE) { 1456 String uri = ns.getURI(); 1457 if ( uri != null ) { 1458 if ( ! namespaceStack.contains( ns ) ) { 1459 return true; 1460 1461 } 1462 } 1463 } 1464 return false; 1465 1466 } 1467 1468 protected void handleException(IOException e) throws SAXException { 1469 throw new SAXException(e); 1470 } 1471 1472 1478 protected OutputFormat getOutputFormat() { 1479 return format; 1480 } 1481 1482 public boolean resolveEntityRefs() { 1483 return resolveEntityRefs; 1484 } 1485 1486 public void setResolveEntityRefs(boolean resolve) { 1487 this.resolveEntityRefs = resolve; 1488 } 1489} | Popular Tags |