1 22 23 28 29 package org.xquark.serialize; 30 31 import java.io.*; 32 import java.util.Arrays ; 33 34 import org.xml.sax.Attributes ; 35 import org.xml.sax.SAXException ; 36 import org.xquark.util.DefaultXMLFilter; 37 import org.xquark.util.SAXConstants; 38 39 48 public class BasicXMLSerializer extends DefaultXMLFilter { 49 private static final String RCSRevision = "$Revision: 1.5 $"; 50 51 private static final String RCSName = "$Name: $"; 52 53 private final static int INDENT_CHAR_ARRAY_SIZE = 10; 54 55 private final static char[] INDENT_CHAR_ARRAY = { '\t', '\t', '\t', '\t', 56 '\t', '\t', '\t', '\t', '\t', '\t' }; 57 58 private PrintWriter writer; 59 60 private boolean indent = true; 61 62 private boolean orderAttributes = false; 63 64 private SortableAttributes orderedAtts; 65 66 private boolean useIgnorableWhitespaces = true; 67 68 private String encoding = null; 69 70 private String userSetEncoding = null; 71 72 private boolean generateXMLDeclaration = true; 73 74 private boolean generateEncodingDeclaration = true; 75 76 private boolean closeStreamAutomatically = false; 77 78 79 private int depth = -1; 80 81 82 private int level = 0; 83 84 private boolean cdata = false; 85 86 private boolean elementPending = false; 87 88 private boolean lastStartIsStart = false; 89 90 private boolean charDataEncountered = false; 91 92 private boolean extraDataEncountered = false; 93 94 private boolean prologGenerated = false; 95 96 private StringBuffer buf = new StringBuffer (); 97 98 BasicXMLSerializer() { 99 } 100 101 108 public BasicXMLSerializer(OutputStream out) 109 throws UnsupportedEncodingException { 110 setOutputStream(out); 111 } 112 113 120 public BasicXMLSerializer(Writer out) { 121 setWriter(out); 122 } 123 124 135 public BasicXMLSerializer(OutputStream out, String encoding) 136 throws UnsupportedEncodingException { 137 setDefaultEncoding(encoding); 138 setOutputStream(out); 139 } 140 141 151 public BasicXMLSerializer(Writer out, String encoding) { 152 this(); 153 setDefaultEncoding(encoding); 154 setWriter(out); 155 } 156 157 167 public void setOutputStream(OutputStream out) 168 throws UnsupportedEncodingException { 169 Writer outputWriter; 170 if (userSetEncoding == null) { 171 if (out.equals(System.out) || out.equals(System.err)) { 172 encoding = null; 173 outputWriter = new OutputStreamWriter(out); 174 } else { 175 encoding = "UTF-8"; 176 outputWriter = new OutputStreamWriter(out, "UTF-8"); 177 } 178 } else { 179 encoding = userSetEncoding; 180 outputWriter = new OutputStreamWriter(out, encoding); 181 } 182 183 writer = new PrintWriter(new BufferedWriter(outputWriter), false); 184 } 185 186 196 public void setWriter(Writer out) { 197 writer = new PrintWriter(out, false); 198 199 encoding = userSetEncoding; 200 } 201 202 211 public void setIndent(boolean indent) { 212 this.indent = indent; 213 } 214 215 223 public void setUseIgnorableWhitespaces(boolean use) { 224 useIgnorableWhitespaces = use; 225 } 226 227 237 public void setCanonicalOutput(boolean mode) { 238 orderAttributes = mode; 239 } 240 241 248 public void setDefaultEncoding(String encoding) { 249 this.userSetEncoding = encoding; 250 } 251 252 258 public void setGenerateXMLDeclaration(boolean enable) { 259 generateXMLDeclaration = enable; 260 } 261 262 269 public void setGenerateEncodingDeclaration(boolean enable) { 270 generateEncodingDeclaration = enable; 271 } 272 273 279 public void setAutoStreamClose(boolean close) { 280 closeStreamAutomatically = close; 281 } 282 283 286 public boolean getIndent() { 287 return indent; 288 } 289 290 293 public boolean getUseIgnorableWhitespaces() { 294 return useIgnorableWhitespaces; 295 } 296 297 302 public boolean getCanonicalOutput() { 303 return orderAttributes; 304 } 305 306 309 public String getDefaultEncoding() { 310 return userSetEncoding; 311 } 312 313 318 public boolean getGenerateXMLDeclaration() { 319 return generateXMLDeclaration; 320 } 321 322 327 public boolean getAutoStreamClose() { 328 return closeStreamAutomatically; 329 } 330 331 334 public void reset() { 335 level = 0; 337 cdata = false; 338 elementPending = false; 339 lastStartIsStart = false; 340 charDataEncountered = false; 341 prologGenerated = false; 342 depth = -1; 343 } 344 345 348 public void close() { 349 writer.close(); 350 } 351 352 355 public void flush() { 356 writer.flush(); 357 } 358 359 363 public void startDocument() throws SAXException { 364 if (generateXMLDeclaration) { 365 if (encoding == null) 366 writer.write("<?xml version=\"1.0\"?>"); 367 else { 368 writer.write("<?xml version=\"1.0\""); 369 if (generateEncodingDeclaration) 370 writer.write(" encoding=\"" + encoding + "\""); 371 writer.write("?>"); 372 } 373 prologGenerated = true; 374 } 375 } 376 377 public void characters(char[] ch, int start, int length) 378 throws SAXException { 379 if (length <= 0) 380 return; 381 382 completeStartTag(); 384 385 if (cdata) 386 writer.write(ch, start, length); 387 else { 388 int max = start + length; 389 int i, last = start; 390 for (i = start; i < max; i++) { 391 if (isCharDataForbidden(ch[i])) { 392 writer.write(ch, last, i - last); 393 writer.write(getPredefinedEntityRef(ch[i])); 394 last = i + 1; 395 } else if (ch[i] == 0xA) { 396 writer.write(ch, last, i - last); 397 writer.println(); 398 last = i + 1; 399 } 400 } 401 if ((last == start) || (last < max)) 402 writer.write(ch, last, i - last); 403 } 404 405 charDataEncountered = true; 406 } 407 408 public void ignorableWhitespace(char[] ch, int start, int length) 409 throws SAXException { 410 if (indent || !useIgnorableWhitespaces || (length <= 0)) 411 return; 412 413 completeStartTag(); 415 416 int max = start + length; 417 int i, last = start; 418 for (i = start; i < max; i++) { 419 if (ch[i] == 0xA) { 420 writer.write(ch, last, i - last); 421 writer.println(); 422 last = i + 1; 423 } 424 } 425 if ((last == start) || (last < max)) 426 writer.write(ch, last, i - last); 427 } 428 429 private boolean isCharDataForbidden(int ch) { 430 return (ch == '<') || (ch == '&') || (ch == '>'); } 434 435 private boolean isAttributeForbidden(int ch) { 436 return (ch == '<') || (ch == '&') || (ch == '"'); 437 } 438 439 private String getPredefinedEntityRef(int ch) { 440 switch (ch) { 444 case '<': 445 return "<"; 446 case '>': 447 return ">"; 448 case '"': 449 return """; 450 case '\'': 451 return "'"; 452 case '&': 453 return "&"; 454 } 455 return null; 456 } 457 458 public void startElement(String namespaceURI, String localName, 459 String qName, Attributes atts) throws SAXException { 460 completeStartTag(); 462 463 depth++; 464 if (!charDataEncountered && carriageReturn(depth)) 465 level = depth + 1; 466 467 buf.append('<'); 469 buf.append(qName); 470 471 if (orderAttributes) 473 atts = canonicalizeAttributes(atts); 474 475 int nbAtts = atts.getLength(); 476 String attValue; 477 for (int i = 0; i < nbAtts; i++) { 478 buf.append(' '); 479 buf.append(atts.getQName(i)); 480 buf.append("=\""); 481 482 attValue = atts.getValue(i); 483 484 int max = attValue.length(); 485 int j, last = 0; 486 for (j = 0; j < max; j++) { 487 if (isAttributeForbidden(attValue.charAt(j))) { 488 buf.append(attValue.substring(last, j)); 489 buf.append(getPredefinedEntityRef(attValue.charAt(j))); 490 last = j + 1; 491 } 492 } 493 if ((last == 0) || (last < max)) 494 buf.append(attValue.substring(last, j)); 495 496 buf.append('"'); 497 } 498 499 extraDataEncountered = false; 500 charDataEncountered = false; 501 elementPending = true; 502 lastStartIsStart = true; 503 } 504 505 private Attributes canonicalizeAttributes(Attributes atts) { 506 int nbAtts = atts.getLength(); 507 if (nbAtts <= 1) 508 return atts; 509 510 if (orderedAtts == null) 511 orderedAtts = new SortableAttributes(atts); 512 else 513 orderedAtts.setAttributes(atts); 514 515 orderedAtts.sort(); 516 return orderedAtts; 517 } 518 519 public void completeStartTag() { 520 if (elementPending) { 522 buf.append('>'); 523 writer.write(buf.toString()); 524 buf.setLength(0); 525 elementPending = false; 526 } 527 } 528 529 public void processingInstruction(String target, String data) 530 throws SAXException { 531 completeStartTag(); 532 533 writer.write("<?"); 534 writer.write(target); 535 writer.write(' '); 536 537 char[] ch = data.toCharArray(); 538 int max = data.length(); 539 int i, last = 0; 540 for (i = 0; i < max; i++) { 541 if (ch[i] == 0xA) { 542 writer.write(ch, last, i - last); 543 writer.println(); 544 last = i + 1; 545 } 546 } 547 if ((last == 0) || (last < max)) 548 writer.write(ch, last, i - last); 549 550 writer.write("?>"); 551 extraDataEncountered = true; 552 } 553 554 public void endElement(String namespaceURI, String localName, String qName) 555 throws SAXException { 556 if (elementPending) { 558 buf.append("/>"); 559 writer.write(buf.toString()); 560 buf.setLength(0); 561 } else { 562 if (!charDataEncountered && (!lastStartIsStart || (depth == 0)) 563 && carriageReturn(depth)) 564 level = depth; 565 566 buf.append("</"); 568 buf.append(qName); 569 buf.append('>'); 570 writer.write(buf.toString()); 571 buf.setLength(0); 572 } 573 charDataEncountered = false; 574 extraDataEncountered = false; 575 elementPending = false; 576 lastStartIsStart = false; 577 depth--; 578 writer.flush(); 579 } 580 581 public void endDocument() throws SAXException { 582 writer.println(); 583 if (closeStreamAutomatically) 584 writer.close(); 585 else 586 writer.flush(); 587 reset(); 588 } 589 590 public void startCDATA() throws SAXException { 591 completeStartTag(); 593 writer.print("<![CDATA["); 594 cdata = true; 595 } 596 597 public void endCDATA() throws SAXException { 598 writer.print("]]>"); 599 cdata = false; 600 } 601 602 public void comment(char[] ch, int start, int length) throws SAXException { 603 completeStartTag(); 604 605 writer.write("<!--"); 606 607 int max = start + length; 608 int i, last = start; 609 for (i = start; i < max; i++) { 610 if (ch[i] == 0xA) { 611 writer.write(ch, last, i - last); 612 writer.println(); 613 last = i + 1; 614 } 615 } 616 if ((last == start) || (last < max)) 617 writer.write(ch, last, i - last); 618 619 writer.write("-->"); 620 extraDataEncountered = true; 621 } 622 623 private class SortableAttributes implements Attributes { 624 SortableAttribute[] attTab; 625 626 int nbAtts = 0; 627 628 SortableAttributes(Attributes atts) { 629 setAttributes(atts); 630 } 631 632 void setAttributes(Attributes atts) { 633 nbAtts = atts.getLength(); 634 635 if ((attTab == null) || (attTab.length < nbAtts)) { 636 attTab = new SortableAttribute[nbAtts]; 637 for (int i = 0; i < nbAtts; i++) 638 attTab[i] = new SortableAttribute(atts.getURI(i), atts 639 .getLocalName(i), atts.getQName(i), atts 640 .getValue(i)); 641 } else 642 for (int i = 0; i < nbAtts; i++) 643 attTab[i].set(atts.getURI(i), atts.getLocalName(i), atts 644 .getQName(i), atts.getValue(i)); 645 } 646 647 void sort() { 648 Arrays.sort(attTab, 0, nbAtts); 649 } 650 651 public int getIndex(String str) { 652 return -1; } 654 655 public int getIndex(String str, String str1) { 656 return -1; } 658 659 public int getLength() { 660 return nbAtts; 661 } 662 663 public String getLocalName(int param) { 664 return attTab[param].localName; 665 } 666 667 public String getQName(int param) { 668 return attTab[param].QName; 669 } 670 671 public String getType(int param) { 672 return ""; } 674 675 public String getType(String str) { 676 return ""; } 678 679 public String getType(String str, String str1) { 680 return ""; } 682 683 public String getURI(int param) { 684 return attTab[param].namespaceURI; 685 } 686 687 public String getValue(String str) { 688 return ""; } 690 691 public String getValue(int param) { 692 return attTab[param].value; 693 } 694 695 public String getValue(String str, String str1) { 696 return ""; } 698 699 } 700 701 private boolean carriageReturn(int indent) { 705 boolean ret = false; 706 if (this.indent || (level == 0)) { 707 ret = (indent <= level); 708 if (ret) { 710 if (indent == 0) { 711 if (prologGenerated) 712 writer.println(); 713 } else { 714 writer.println(); 715 indent(indent); 716 } 717 } 718 } 719 return ret; 720 } 721 722 private void indent(int indent) { 723 if (indent < 0) 724 return; 725 int quotient = indent / INDENT_CHAR_ARRAY_SIZE; 726 727 for (int i = 1; i <= quotient; i++) { 728 writer.write(INDENT_CHAR_ARRAY, 0, INDENT_CHAR_ARRAY_SIZE); 729 } 730 writer.write(INDENT_CHAR_ARRAY, 0, indent % INDENT_CHAR_ARRAY_SIZE); 731 } 732 733 749 private class SortableAttribute implements Comparable , SAXConstants { 750 String namespaceURI; 751 752 String localName; 753 754 String QName; 755 756 String value; 757 758 SortableAttribute(String namespaceURI, String localName, String QName, 759 String value) { 760 set(namespaceURI, localName, QName, value); 761 } 762 763 void set(String namespaceURI, String localName, String QName, 764 String value) { 765 this.namespaceURI = namespaceURI; 766 this.localName = localName; 767 this.QName = QName; 768 this.value = value; 769 } 770 771 public int compareTo(Object o) { 772 SortableAttribute obj = (SortableAttribute) o; 773 774 if (isNamespace()) { 775 if (obj.isNamespace()) 776 return localName.compareTo(obj.localName); 777 else 778 return -1; 779 } else if (obj.isNamespace()) 780 return 1; 781 else { 783 int comparison = namespaceURI.compareTo(obj.namespaceURI); 784 if (comparison == 0) return localName.compareTo(obj.localName); 786 else 787 return comparison; 788 } 789 } 790 791 boolean isNamespace() { 792 return (namespaceURI.equals(XMLNS_URI)); 793 } 794 } 795 796 } | Popular Tags |