1 21 22 package nu.xom; 23 24 import java.io.BufferedWriter ; 25 import java.io.IOException ; 26 import java.io.OutputStream ; 27 import java.io.OutputStreamWriter ; 28 import java.io.UnsupportedEncodingException ; 29 import java.io.Writer ; 30 import java.util.Locale ; 31 32 52 public class Serializer { 53 54 private TextWriter escaper; 55 private boolean preserveBaseURI = false; 56 57 58 67 public Serializer(OutputStream out) { 68 69 if (out == null) { 70 throw new NullPointerException ("Null OutputStream"); 71 } 72 try { 73 Writer writer = new OutputStreamWriter (out, "UTF8"); 74 writer = new BufferedWriter (writer); 75 escaper = TextWriterFactory.getTextWriter(writer, "UTF-8"); 76 } 77 catch (UnsupportedEncodingException ex) { 78 throw new RuntimeException ( 79 "The VM is broken. It does not understand UTF-8."); 80 } 81 82 } 83 84 85 145 public Serializer(OutputStream out, String encoding) 146 throws UnsupportedEncodingException { 147 148 if (out == null) { 149 throw new NullPointerException ("Null OutputStream"); 150 } 151 if (encoding == null) { 152 throw new NullPointerException ("Null encoding"); 153 } 154 155 this.setOutputStream(out, encoding); 156 157 } 158 159 160 174 public void setOutputStream(OutputStream out) 175 throws IOException { 176 177 this.flush(); 179 int maxLength = getMaxLength(); 180 int indent = this.getIndent(); 181 String lineSeparator = getLineSeparator(); 182 boolean nfc = getUnicodeNormalizationFormC(); 183 String encoding = escaper.getEncoding(); 184 setOutputStream(out, encoding); 185 setIndent(indent); 186 setMaxLength(maxLength); 187 setUnicodeNormalizationFormC(nfc); 188 setLineSeparator(lineSeparator); 189 190 } 191 192 193 private void setOutputStream(OutputStream out, String encoding) 194 throws UnsupportedEncodingException { 195 196 Writer writer; 197 String encodingUpperCase = encoding.toUpperCase(Locale.ENGLISH); 198 if (encodingUpperCase.equals("IBM037") 201 || encodingUpperCase.equals("CP037") 202 || encodingUpperCase.equals("EBCDIC-CP-US") 203 || encodingUpperCase.equals("EBCDIC-CP-CA") 204 || encodingUpperCase.equals("EBCDIC-CP-WA") 205 || encodingUpperCase.equals("EBCDIC-CP-NL") 206 || encodingUpperCase.equals("CSIBM037")) { 207 writer = new EBCDICWriter(out); 208 } 209 else if (encodingUpperCase.equals("UTF-16") 210 || encodingUpperCase.equals("ISO-10646-UCS-2")) { 211 writer = new OutputStreamWriter (out, "UnicodeBig"); 213 } 214 else if (encodingUpperCase.equals("ISO-8859-11") 215 || encodingUpperCase.equals("TIS-620")) { 216 writer = new OutputStreamWriter (out, "TIS620"); 219 } 220 else writer = new OutputStreamWriter (out, encoding); 221 writer = new BufferedWriter (writer); 222 this.escaper = TextWriterFactory.getTextWriter(writer, encoding); 223 224 } 225 226 227 242 public void write(Document doc) throws IOException { 243 244 escaper.reset(); 245 writeXMLDeclaration(); 248 int childCount = doc.getChildCount(); 249 for (int i = 0; i < childCount; i++) { 250 writeChild(doc.getChild(i)); 251 252 escaper.breakLine(); 256 } 257 escaper.flush(); 258 259 } 260 261 262 271 protected void writeXMLDeclaration() throws IOException { 272 273 escaper.writeMarkup("<?xml version=\"1.0\" encoding=\""); 274 escaper.writeMarkup(escaper.getEncoding()); 275 escaper.writeMarkup("\"?>"); 276 escaper.breakLine(); 277 278 } 279 280 281 315 protected void write(Element element) throws IOException { 316 317 if (escaper.isIndenting() 318 && !escaper.isPreserveSpace() 319 && !escaper.justBroke()) { 320 escaper.breakLine(); 321 } 322 323 boolean hasRealChildren = false; 325 int childCount = element.getChildCount(); 326 for (int i = 0; i < childCount; i++) { 327 Node child = element.getChild(i); 328 if (child.isText()) { 329 Text t = (Text) child; 330 if (t.isEmpty()) continue; 331 } 332 hasRealChildren = true; 333 break; 334 } 335 336 if (hasRealChildren) { 337 writeStartTag(element); 338 boolean wasPreservingWhiteSpace = escaper.isPreserveSpace(); 340 String newXMLSpaceValue = element.getAttributeValue( 341 "space", "http://www.w3.org/XML/1998/namespace"); 342 if (newXMLSpaceValue != null) { 343 if ("preserve".equals(newXMLSpaceValue)){ 344 escaper.setPreserveSpace(true); 345 } 346 else if ("default".equals(newXMLSpaceValue)){ 347 escaper.setPreserveSpace(false); 348 } 349 } 350 351 escaper.incrementIndent(); 352 for (int i = 0; i < childCount; i++) { 354 writeChild(element.getChild(i)); 355 } 356 escaper.decrementIndent(); 357 if (escaper.getIndent() > 0 && !escaper.isPreserveSpace()) { 358 if (hasNonTextChildren(element)) { 359 escaper.breakLine(); 360 } 361 } 362 writeEndTag(element); 363 364 if (newXMLSpaceValue != null) { 366 escaper.setPreserveSpace(wasPreservingWhiteSpace); 367 } 368 369 } 370 else { 371 writeEmptyElementTag(element); 372 } 373 escaper.flush(); 374 375 } 376 377 378 private boolean hasNonTextChildren(Element element) { 379 380 int childCount = element.getChildCount(); 381 for (int i = 0; i < childCount; i++) { 382 if (! element.getChild(i).isText()) return true; 383 } 384 return false; 385 386 } 387 388 389 403 protected void writeEndTag(Element element) throws IOException { 404 escaper.writeMarkup("</"); 405 escaper.writeMarkup(element.getQualifiedName()); 406 escaper.writeMarkup(">"); 407 } 408 409 410 432 protected void writeStartTag(Element element) throws IOException { 433 writeTagBeginning(element); 434 escaper.writeMarkup('>'); 435 } 436 437 438 467 protected void writeEmptyElementTag(Element element) 468 throws IOException { 469 writeTagBeginning(element); 470 escaper.writeMarkup("/>"); 471 } 472 473 474 private void writeTagBeginning(Element element) 477 throws IOException { 478 escaper.writeMarkup('<'); 479 escaper.writeMarkup(element.getQualifiedName()); 480 writeAttributes(element); 481 writeNamespaceDeclarations(element); 482 } 483 484 485 505 protected void writeAttributes(Element element) 506 throws IOException { 507 508 if (preserveBaseURI) { 510 ParentNode parent = element.getParent(); 511 if (element.getAttribute("base", 512 "http://www.w3.org/XML/1998/namespace") == null) { 513 String baseValue = element.getBaseURI(); 514 if (parent == null 515 || parent.isDocument() 516 || !element.getBaseURI() 517 .equals(parent.getBaseURI())) { 518 519 escaper.writeMarkup(' '); 520 Attribute baseAttribute = new Attribute( 521 "xml:base", 522 "http://www.w3.org/XML/1998/namespace", 523 baseValue); 524 write(baseAttribute); 525 } 526 } 527 } 528 529 int attributeCount = element.getAttributeCount(); 530 for (int i = 0; i < attributeCount; i++) { 531 Attribute attribute = element.getAttribute(i); 532 escaper.writeMarkup(' '); 533 write(attribute); 534 } 535 } 536 537 538 555 protected void writeNamespaceDeclarations(Element element) 556 throws IOException { 557 558 ParentNode parent = element.getParent(); 559 int count = element.getNamespaceDeclarationCount(); 560 for (int i = 0; i < count; i++) { 561 String additionalPrefix = element.getNamespacePrefix(i); 562 String uri = element.getNamespaceURI(additionalPrefix); 563 if (parent.isElement()) { 564 Element parentElement = (Element) parent; 565 if (uri.equals( 566 parentElement.getNamespaceURI(additionalPrefix))) { 567 continue; 568 } 569 } 570 else if (uri.equals("")) { 571 continue; } 573 574 escaper.writeMarkup(' '); 575 writeNamespaceDeclaration(additionalPrefix, uri); 576 } 577 } 578 579 580 598 protected void writeNamespaceDeclaration(String prefix, String uri) 599 throws IOException { 600 601 if ("".equals(prefix)) { 602 escaper.writeMarkup("xmlns"); 603 } 604 else { 605 escaper.writeMarkup("xmlns:"); 606 escaper.writeMarkup(prefix); 607 } 608 escaper.writeMarkup("=\""); 609 escaper.writePCDATA(uri); 610 escaper.writeMarkup('\"'); 611 612 } 613 614 615 630 protected void write(Attribute attribute) throws IOException { 631 escaper.writeMarkup(attribute.getQualifiedName()); 632 escaper.writeMarkup("=\""); 633 escaper.writeAttributeValue(attribute.getValue()); 634 escaper.writeMarkup('\"'); 635 } 636 637 638 654 protected void write(Comment comment) throws IOException { 655 if (escaper.isIndenting()) escaper.breakLine(); 656 escaper.writeMarkup("<!--"); 657 escaper.writeMarkup(comment.getValue()); 658 escaper.writeMarkup("-->"); 659 } 660 661 662 681 protected void write(ProcessingInstruction instruction) 682 throws IOException { 683 684 if (escaper.isIndenting()) escaper.breakLine(); 685 escaper.writeMarkup("<?"); 686 escaper.writeMarkup(instruction.getTarget()); 687 String value = instruction.getValue(); 688 if (!"".equals(value)) { 691 escaper.writeMarkup(' '); 692 escaper.writeMarkup(value); 693 } 694 escaper.writeMarkup("?>"); 695 696 } 697 698 719 protected void write(Text text) throws IOException { 720 721 String value = text.getValue(); 725 if (text.isCDATASection() 726 && value.indexOf("]]>") == -1) { 727 if (!(escaper instanceof UnicodeWriter)) { 728 int length = value.length(); 729 for (int i = 0; i < length; i++) { 730 if (escaper.needsEscaping(value.charAt(i))) { 731 escaper.writePCDATA(value); 733 return; 734 } 735 } 736 } 737 escaper.writeMarkup("<![CDATA["); 738 escaper.writeMarkup(value); 739 escaper.writeMarkup("]]>"); 740 } 741 else if (isBoundaryWhitespace(text)) { 743 return; } 745 else { 746 escaper.writePCDATA(value); 747 } 748 749 } 750 751 752 private boolean isBoundaryWhitespace(Text text) { 753 754 if (getIndent() <= 0) return false; 755 756 if (!"".equals(text.getValue().trim())) return false; 758 ParentNode parent = text.getParent(); 759 760 int position = parent.indexOf(text); 761 762 if (position == 0 && parent.getChildCount() == 1) return false; 763 Node previous = null; 764 Node next = null; 765 if (position != 0) previous = parent.getChild(position-1); 766 if (position != parent.getChildCount()-1) { 767 next = parent.getChild(position+1); 768 } 769 if (previous == null || !previous.isText()) { 770 if (next == null || !next.isText()) { 771 return true; 772 } 773 } 774 775 return false; 776 777 } 778 779 780 794 protected void write(DocType doctype) throws IOException { 795 796 escaper.writeMarkup("<!DOCTYPE "); 797 escaper.writeMarkup(doctype.getRootElementName()); 798 if (doctype.getPublicID() != null) { 799 escaper.writeMarkup(" PUBLIC \"" + doctype.getPublicID() 800 + "\" \"" + doctype.getSystemID() + "\""); 801 } 802 else if (doctype.getSystemID() != null) { 803 escaper.writeMarkup( 804 " SYSTEM \"" + doctype.getSystemID() + "\""); 805 } 806 807 String internalDTDSubset = doctype.getInternalDTDSubset(); 808 if (!internalDTDSubset.equals("")) { 809 escaper.writeMarkup(" ["); 810 escaper.breakLine(); 811 escaper.setInDocType(true); 812 escaper.writeMarkup(internalDTDSubset); 813 escaper.setInDocType(false); 814 escaper.writeMarkup("]"); 815 } 816 817 escaper.writeMarkup(">"); 818 819 } 820 821 822 838 protected void writeChild(Node node) throws IOException { 839 840 if (node.isElement()) { 841 write((Element) node); 842 } 843 else if (node.isText()) { 844 write((Text) node); 845 } 846 else if (node.isComment()) { 847 write((Comment) node); 848 } 849 else if (node.isProcessingInstruction()) { 850 write((ProcessingInstruction) node); 851 } 852 else if (node.isDocType()) { 853 write((DocType) node); 854 } 855 else { 856 throw new XMLException("Cannot write a " + 857 node.getClass().getName() + 858 " from the writeChildNode() method"); 859 } 860 861 } 862 863 864 880 protected final void writeEscaped(String text) throws IOException { 881 escaper.writePCDATA(text); 882 } 883 884 903 protected final void writeAttributeValue(String value) 904 throws IOException { 905 escaper.writeAttributeValue(value); 906 } 907 908 909 922 protected final void writeRaw(String text) throws IOException { 923 escaper.writeMarkup(text); 924 } 925 926 927 936 protected final void breakLine() throws IOException { 937 escaper.breakLine(); 938 } 939 940 941 953 public void flush() throws IOException { 954 escaper.flush(); 955 } 956 957 958 966 public int getIndent() { 967 return escaper.getIndent(); 968 } 969 970 971 1011 public void setIndent(int indent) { 1012 if (indent < 0) { 1013 throw new IllegalArgumentException ( 1014 "Indent cannot be negative" 1015 ); 1016 } 1017 escaper.setIndent(indent); 1018 } 1019 1020 1021 1030 public String getLineSeparator() { 1031 return escaper.getLineSeparator(); 1032 } 1033 1034 1035 1060 public void setLineSeparator(String lineSeparator) { 1061 escaper.setLineSeparator(lineSeparator); 1062 } 1063 1064 1065 1072 public int getMaxLength() { 1073 return escaper.getMaxLength(); 1074 } 1075 1076 1077 1117 public void setMaxLength(int maxLength) { 1118 escaper.setMaxLength(maxLength); 1119 } 1120 1121 1122 1132 public boolean getPreserveBaseURI() { 1133 return preserveBaseURI; 1134 } 1135 1136 1137 1153 public void setPreserveBaseURI(boolean preserve) { 1154 this.preserveBaseURI = preserve; 1155 } 1156 1157 1158 1166 public String getEncoding() { 1167 return escaper.getEncoding(); 1168 } 1169 1170 1200 public void setUnicodeNormalizationFormC(boolean normalize) { 1201 escaper.setNFC(normalize); 1202 } 1203 1204 1205 1215 public boolean getUnicodeNormalizationFormC() { 1216 return escaper.getNFC(); 1217 } 1218 1219 1220 1241 protected final int getColumnNumber() { 1242 return escaper.getColumnNumber(); 1243 } 1244 1245} | Popular Tags |