1 /* XMLStreamWriterImpl.java -- 2 Copyright (C) 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 package gnu.xml.stream; 39 40 import java.io.IOException; 41 import java.io.Writer; 42 import java.util.Enumeration; 43 import java.util.HashSet; 44 import java.util.LinkedList; 45 import java.util.Set; 46 47 import javax.xml.XMLConstants; 48 import javax.xml.namespace.NamespaceContext; 49 import javax.xml.stream.XMLStreamException; 50 import javax.xml.stream.XMLStreamWriter; 51 52 import org.xml.sax.helpers.NamespaceSupport; 53 54 /** 55 * Simple XML stream writer. 56 * 57 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> 58 */ 59 public class XMLStreamWriterImpl 60 implements XMLStreamWriter 61 { 62 63 /** 64 * The underlying character stream to write to. 65 */ 66 protected final Writer writer; 67 68 /** 69 * The encoding being used. 70 * Note that this must match the encoding of the character stream. 71 */ 72 protected final String encoding; 73 74 /** 75 * Whether prefix defaulting is being used. 76 * If true and a prefix has not been defined for a namespace specified on 77 * an element or an attribute, a new prefix and namespace declaration will 78 * be created. 79 */ 80 protected final boolean prefixDefaulting; 81 82 /** 83 * The namespace context used to determine the namespace-prefix mappings 84 * in scope. 85 */ 86 protected NamespaceContext namespaceContext; 87 88 /** 89 * The stack of elements in scope. 90 * Used to close the remaining elements. 91 */ 92 private LinkedList elements; 93 94 /** 95 * Whether a start element has been opened but not yet closed. 96 */ 97 private boolean inStartElement; 98 99 /** 100 * Whether we are in an empty element. 101 */ 102 private boolean emptyElement; 103 104 private NamespaceSupport namespaces; 105 private int count = 0; 106 107 private boolean xml11; 108 private boolean hasXML11RestrictedChars; 109 110 /** 111 * Constructor. 112 * @see #writer 113 * @see #encoding 114 * @see #prefixDefaulting 115 */ XMLStreamWriterImpl(Writer writer, String encoding, boolean prefixDefaulting)116 protected XMLStreamWriterImpl(Writer writer, String encoding, 117 boolean prefixDefaulting) 118 { 119 this.writer = writer; 120 this.encoding = encoding; 121 this.prefixDefaulting = prefixDefaulting; 122 elements = new LinkedList(); 123 namespaces = new NamespaceSupport(); 124 } 125 126 /** 127 * Write the end of a start-element event. 128 * This will close the element if it was defined to be an empty element. 129 */ endStartElement()130 private void endStartElement() 131 throws IOException 132 { 133 if (!inStartElement) 134 return; 135 if (emptyElement) 136 { 137 writer.write('/'); 138 elements.removeLast(); 139 namespaces.popContext(); 140 emptyElement = false; 141 } 142 writer.write('>'); 143 inStartElement = false; 144 } 145 writeStartElement(String localName)146 public void writeStartElement(String localName) 147 throws XMLStreamException 148 { 149 try 150 { 151 if (!isName(localName)) 152 throw new IllegalArgumentException("illegal Name: " + localName); 153 154 endStartElement(); 155 namespaces.pushContext(); 156 157 writer.write('<'); 158 writer.write(localName); 159 160 elements.addLast(new String[] { null, localName }); 161 inStartElement = true; 162 } 163 catch (IOException e) 164 { 165 XMLStreamException e2 = new XMLStreamException(e); 166 e2.initCause(e); 167 throw e2; 168 } 169 } 170 writeStartElement(String namespaceURI, String localName)171 public void writeStartElement(String namespaceURI, String localName) 172 throws XMLStreamException 173 { 174 try 175 { 176 if (namespaceURI != null && !isURI(namespaceURI)) 177 throw new IllegalArgumentException("illegal URI: " + namespaceURI); 178 if (!isName(localName)) 179 throw new IllegalArgumentException("illegal Name: " + localName); 180 181 endStartElement(); 182 namespaces.pushContext(); 183 184 String prefix = getPrefix(namespaceURI); 185 boolean isDeclared = (prefix != null); 186 if (!isDeclared) 187 { 188 if (prefixDefaulting) 189 prefix = createPrefix(namespaceURI); 190 else 191 throw new XMLStreamException("namespace " + namespaceURI + 192 " has not been declared"); 193 } 194 writer.write('<'); 195 if (!"".equals(prefix)) 196 { 197 writer.write(prefix); 198 writer.write(':'); 199 } 200 writer.write(localName); 201 inStartElement = true; 202 if (!isDeclared) 203 { 204 writeNamespaceImpl(prefix, namespaceURI); 205 } 206 207 elements.addLast(new String[] { prefix, localName }); 208 } 209 catch (IOException e) 210 { 211 XMLStreamException e2 = new XMLStreamException(e); 212 e2.initCause(e); 213 throw e2; 214 } 215 } 216 217 /** 218 * Creates a new unique prefix in the document. 219 * Subclasses may override this method to provide a suitably unique prefix 220 * for the given namespace. 221 * @param namespaceURI the namespace URI 222 */ createPrefix(String namespaceURI)223 protected String createPrefix(String namespaceURI) 224 { 225 Set prefixes = new HashSet(); 226 for (Enumeration e = namespaces.getPrefixes(); e.hasMoreElements(); ) 227 prefixes.add(e.nextElement()); 228 String ret; 229 do 230 { 231 ret = "ns" + (count++); 232 } 233 while (prefixes.contains(ret)); 234 return ret; 235 } 236 writeStartElement(String prefix, String localName, String namespaceURI)237 public void writeStartElement(String prefix, String localName, 238 String namespaceURI) 239 throws XMLStreamException 240 { 241 try 242 { 243 if (namespaceURI != null && !isURI(namespaceURI)) 244 throw new IllegalArgumentException("illegal URI: " + namespaceURI); 245 if (prefix != null && !isPrefix(prefix)) 246 throw new IllegalArgumentException("illegal NCName: " + prefix); 247 if (!isNCName(localName)) 248 throw new IllegalArgumentException("illegal NCName: " + localName); 249 250 endStartElement(); 251 namespaces.pushContext(); 252 253 String currentPrefix = getPrefix(namespaceURI); 254 boolean isCurrent = prefix.equals(currentPrefix); 255 writer.write('<'); 256 if (!"".equals(prefix)) 257 { 258 writer.write(prefix); 259 writer.write(':'); 260 } 261 writer.write(localName); 262 if (prefixDefaulting && !isCurrent) 263 { 264 writeNamespaceImpl(prefix, namespaceURI); 265 } 266 267 elements.addLast(new String[] { prefix, localName }); 268 inStartElement = true; 269 } 270 catch (IOException e) 271 { 272 XMLStreamException e2 = new XMLStreamException(e); 273 e2.initCause(e); 274 throw e2; 275 } 276 } 277 writeEmptyElement(String namespaceURI, String localName)278 public void writeEmptyElement(String namespaceURI, String localName) 279 throws XMLStreamException 280 { 281 writeStartElement(namespaceURI, localName); 282 emptyElement = true; 283 } 284 writeEmptyElement(String prefix, String localName, String namespaceURI)285 public void writeEmptyElement(String prefix, String localName, 286 String namespaceURI) 287 throws XMLStreamException 288 { 289 writeStartElement(prefix, localName, namespaceURI); 290 emptyElement = true; 291 } 292 writeEmptyElement(String localName)293 public void writeEmptyElement(String localName) 294 throws XMLStreamException 295 { 296 writeStartElement(localName); 297 emptyElement = true; 298 } 299 writeEndElement()300 public void writeEndElement() 301 throws XMLStreamException 302 { 303 if (elements.isEmpty()) 304 throw new IllegalStateException("no matching start element"); 305 try 306 { 307 endStartElement(); 308 String[] element = (String[]) elements.removeLast(); 309 namespaces.popContext(); 310 311 writer.write('<'); 312 writer.write('/'); 313 if (element[0] != null && !"".equals(element[0])) 314 { 315 writer.write(element[0]); 316 writer.write(':'); 317 } 318 writer.write(element[1]); 319 writer.write('>'); 320 } 321 catch (IOException e) 322 { 323 XMLStreamException e2 = new XMLStreamException(e); 324 e2.initCause(e); 325 throw e2; 326 } 327 } 328 writeEndDocument()329 public void writeEndDocument() 330 throws XMLStreamException 331 { 332 while (!elements.isEmpty()) 333 writeEndElement(); 334 } 335 close()336 public void close() 337 throws XMLStreamException 338 { 339 flush(); 340 } 341 flush()342 public void flush() 343 throws XMLStreamException 344 { 345 try 346 { 347 writer.flush(); 348 } 349 catch (IOException e) 350 { 351 XMLStreamException e2 = new XMLStreamException(e); 352 e2.initCause(e); 353 throw e2; 354 } 355 } 356 writeAttribute(String localName, String value)357 public void writeAttribute(String localName, String value) 358 throws XMLStreamException 359 { 360 if (!inStartElement) 361 throw new IllegalStateException(); 362 try 363 { 364 if (!isName(localName)) 365 throw new IllegalArgumentException("illegal Name: " + localName); 366 if (!isChars(value)) 367 throw new IllegalArgumentException("illegal character: " + value); 368 369 writer.write(' '); 370 writer.write(localName); 371 writer.write('='); 372 writer.write('"'); 373 if (hasXML11RestrictedChars) 374 writeEncodedWithRestrictedChars(value, true); 375 else 376 writeEncoded(value, true); 377 writer.write('"'); 378 } 379 catch (IOException e) 380 { 381 XMLStreamException e2 = new XMLStreamException(e); 382 e2.initCause(e); 383 throw e2; 384 } 385 } 386 writeAttribute(String prefix, String namespaceURI, String localName, String value)387 public void writeAttribute(String prefix, String namespaceURI, 388 String localName, String value) 389 throws XMLStreamException 390 { 391 if (!inStartElement) 392 throw new IllegalStateException(); 393 try 394 { 395 if (namespaceURI != null && !isURI(namespaceURI)) 396 throw new IllegalArgumentException("illegal URI: " + namespaceURI); 397 if (prefix != null && !isPrefix(prefix)) 398 throw new IllegalArgumentException("illegal NCName: " + prefix); 399 if (!isNCName(localName)) 400 throw new IllegalArgumentException("illegal NCName: " + localName); 401 if (!isChars(value)) 402 throw new IllegalArgumentException("illegal character: " + value); 403 404 String currentPrefix = getPrefix(namespaceURI); 405 if (currentPrefix == null) 406 { 407 if (prefixDefaulting) 408 writeNamespaceImpl(prefix, namespaceURI); 409 else 410 throw new XMLStreamException("namespace " + namespaceURI + 411 " is not bound"); 412 } 413 else if (!currentPrefix.equals(prefix)) 414 throw new XMLStreamException("namespace " + namespaceURI + 415 " is bound to prefix " + 416 currentPrefix); 417 writer.write(' '); 418 if (!"".equals(prefix)) 419 { 420 writer.write(prefix); 421 writer.write(':'); 422 } 423 writer.write(localName); 424 writer.write('='); 425 writer.write('"'); 426 if (hasXML11RestrictedChars) 427 writeEncodedWithRestrictedChars(value, true); 428 else 429 writeEncoded(value, true); 430 writer.write('"'); 431 } 432 catch (IOException e) 433 { 434 XMLStreamException e2 = new XMLStreamException(e); 435 e2.initCause(e); 436 throw e2; 437 } 438 } 439 writeAttribute(String namespaceURI, String localName, String value)440 public void writeAttribute(String namespaceURI, String localName, 441 String value) 442 throws XMLStreamException 443 { 444 if (!inStartElement) 445 throw new IllegalStateException(); 446 try 447 { 448 if (namespaceURI != null && !isURI(namespaceURI)) 449 throw new IllegalArgumentException("illegal URI: " + namespaceURI); 450 if (!isName(localName)) 451 throw new IllegalArgumentException("illegal Name: " + localName); 452 if (!isChars(value)) 453 throw new IllegalArgumentException("illegal character: " + value); 454 455 String prefix = getPrefix(namespaceURI); 456 if (prefix == null) 457 { 458 if (prefixDefaulting) 459 { 460 prefix = XMLConstants.DEFAULT_NS_PREFIX; 461 writeNamespaceImpl(prefix, namespaceURI); 462 } 463 else 464 throw new XMLStreamException("namespace " + namespaceURI + 465 " is not bound"); 466 } 467 writer.write(' '); 468 if (!"".equals(prefix)) 469 { 470 writer.write(prefix); 471 writer.write(':'); 472 } 473 writer.write(localName); 474 writer.write('='); 475 writer.write('"'); 476 if (hasXML11RestrictedChars) 477 writeEncodedWithRestrictedChars(value, true); 478 else 479 writeEncoded(value, true); 480 writer.write('"'); 481 } 482 catch (IOException e) 483 { 484 XMLStreamException e2 = new XMLStreamException(e); 485 e2.initCause(e); 486 throw e2; 487 } 488 } 489 writeNamespace(String prefix, String namespaceURI)490 public void writeNamespace(String prefix, String namespaceURI) 491 throws XMLStreamException 492 { 493 if (prefix == null || "".equals(prefix) || "xmlns".equals(prefix)) 494 { 495 writeDefaultNamespace(namespaceURI); 496 return; 497 } 498 if (!inStartElement) 499 throw new IllegalStateException(); 500 try 501 { 502 if (!isURI(namespaceURI)) 503 throw new IllegalArgumentException("illegal URI: " + namespaceURI); 504 if (!isPrefix(prefix)) 505 throw new IllegalArgumentException("illegal NCName: " + prefix); 506 } 507 catch (IOException e) 508 { 509 XMLStreamException e2 = new XMLStreamException(e); 510 e2.initCause(e); 511 throw e2; 512 } 513 writeNamespaceImpl(prefix, namespaceURI); 514 } 515 writeNamespaceImpl(String prefix, String namespaceURI)516 private void writeNamespaceImpl(String prefix, String namespaceURI) 517 throws XMLStreamException 518 { 519 try 520 { 521 if (prefix == null) 522 prefix = XMLConstants.DEFAULT_NS_PREFIX; 523 524 setPrefix(prefix, namespaceURI); 525 526 writer.write(' '); 527 writer.write("xmlns"); 528 if (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) 529 { 530 writer.write(':'); 531 writer.write(prefix); 532 } 533 writer.write('='); 534 writer.write('"'); 535 writer.write(namespaceURI); 536 writer.write('"'); 537 } 538 catch (IOException e) 539 { 540 XMLStreamException e2 = new XMLStreamException(e); 541 e2.initCause(e); 542 throw e2; 543 } 544 } 545 writeDefaultNamespace(String namespaceURI)546 public void writeDefaultNamespace(String namespaceURI) 547 throws XMLStreamException 548 { 549 if (!inStartElement) 550 throw new IllegalStateException(); 551 if (!isURI(namespaceURI)) 552 throw new IllegalArgumentException("illegal URI: " + namespaceURI); 553 writeNamespaceImpl(XMLConstants.DEFAULT_NS_PREFIX, namespaceURI); 554 } 555 writeComment(String data)556 public void writeComment(String data) 557 throws XMLStreamException 558 { 559 if (data == null) 560 return; 561 try 562 { 563 if (!isChars(data)) 564 throw new IllegalArgumentException("illegal XML character: " + data); 565 if (data.indexOf("--") != -1) 566 throw new IllegalArgumentException("illegal comment: " + data); 567 568 endStartElement(); 569 570 writer.write("<!--"); 571 if (hasXML11RestrictedChars) 572 { 573 int[] seq = UnicodeReader.toCodePointArray(data); 574 for (int i = 0; i < seq.length; i++) 575 { 576 int c = seq[i]; 577 if (XMLParser.isXML11RestrictedChar(c)) 578 writer.write("&#x" + Integer.toHexString(c) + ";"); 579 else 580 writer.write(Character.toChars(i)); 581 } 582 } 583 else 584 writer.write(data); 585 writer.write("-->"); 586 } 587 catch (IOException e) 588 { 589 XMLStreamException e2 = new XMLStreamException(e); 590 e2.initCause(e); 591 throw e2; 592 } 593 } 594 writeProcessingInstruction(String target)595 public void writeProcessingInstruction(String target) 596 throws XMLStreamException 597 { 598 writeProcessingInstruction(target, null); 599 } 600 writeProcessingInstruction(String target, String data)601 public void writeProcessingInstruction(String target, String data) 602 throws XMLStreamException 603 { 604 try 605 { 606 if (!isName(target) || "xml".equalsIgnoreCase(target)) 607 throw new IllegalArgumentException("illegal PITarget: " + target); 608 if (data != null && !isChars(data)) 609 throw new IllegalArgumentException("illegal XML character: " + data); 610 611 endStartElement(); 612 613 writer.write('<'); 614 writer.write('?'); 615 writer.write(target); 616 if (data != null) 617 { 618 writer.write(' '); 619 if (hasXML11RestrictedChars) 620 { 621 int[] seq = UnicodeReader.toCodePointArray(data); 622 for (int i = 0; i < seq.length; i++) 623 { 624 int c = seq[i]; 625 if (XMLParser.isXML11RestrictedChar(c)) 626 writer.write("&#x" + Integer.toHexString(c) + ";"); 627 else 628 writer.write(Character.toChars(i)); 629 } 630 } 631 else 632 writer.write(data); 633 } 634 writer.write('?'); 635 writer.write('>'); 636 } 637 catch (IOException e) 638 { 639 XMLStreamException e2 = new XMLStreamException(e); 640 e2.initCause(e); 641 throw e2; 642 } 643 } 644 writeCData(String data)645 public void writeCData(String data) 646 throws XMLStreamException 647 { 648 try 649 { 650 if (!isChars(data) || hasXML11RestrictedChars) 651 throw new IllegalArgumentException("illegal XML character: " + data); 652 if (data.indexOf("]]") != -1) 653 throw new IllegalArgumentException("illegal CDATA section: " + data); 654 655 endStartElement(); 656 657 writer.write("<![CDATA["); 658 writer.write(data); 659 writer.write("]]>"); 660 } 661 catch (IOException e) 662 { 663 XMLStreamException e2 = new XMLStreamException(e); 664 e2.initCause(e); 665 throw e2; 666 } 667 } 668 writeDTD(String dtd)669 public void writeDTD(String dtd) 670 throws XMLStreamException 671 { 672 try 673 { 674 // XXX: Should we parse the doctypedecl at this point to ensure 675 // wellformedness? 676 writer.write("<!DOCTYPE "); 677 writer.write(dtd); 678 writer.write('>'); 679 } 680 catch (IOException e) 681 { 682 XMLStreamException e2 = new XMLStreamException(e); 683 e2.initCause(e); 684 throw e2; 685 } 686 } 687 writeEntityRef(String name)688 public void writeEntityRef(String name) 689 throws XMLStreamException 690 { 691 try 692 { 693 if (!isName(name)) 694 throw new IllegalArgumentException("illegal Name: " + name); 695 696 endStartElement(); 697 698 writer.write('&'); 699 writer.write(name); 700 writer.write(';'); 701 } 702 catch (IOException e) 703 { 704 XMLStreamException e2 = new XMLStreamException(e); 705 e2.initCause(e); 706 throw e2; 707 } 708 } 709 writeStartDocument()710 public void writeStartDocument() 711 throws XMLStreamException 712 { 713 writeStartDocument(null, null); 714 } 715 writeStartDocument(String version)716 public void writeStartDocument(String version) 717 throws XMLStreamException 718 { 719 writeStartDocument(null, version); 720 } 721 writeStartDocument(String encoding, String version)722 public void writeStartDocument(String encoding, String version) 723 throws XMLStreamException 724 { 725 if (version == null) 726 version = "1.0"; 727 else if ("1.1".equals(version)) 728 xml11 = true; 729 encoding = this.encoding; // YES: the parameter must be ignored 730 if (encoding == null) 731 encoding = "UTF-8"; 732 if (!"1.0".equals(version) && !"1.1".equals(version)) 733 throw new IllegalArgumentException(version); 734 try 735 { 736 writer.write("<?xml version=\""); 737 writer.write(version); 738 writer.write("\" encoding=\""); 739 writer.write(encoding); 740 writer.write("\"?>"); 741 writer.write(System.getProperty("line.separator")); 742 } 743 catch (IOException e) 744 { 745 XMLStreamException e2 = new XMLStreamException(e); 746 e2.initCause(e); 747 throw e2; 748 } 749 } 750 writeCharacters(String text)751 public void writeCharacters(String text) 752 throws XMLStreamException 753 { 754 if (text == null) 755 return; 756 try 757 { 758 if (!isChars(text)) 759 throw new IllegalArgumentException("illegal XML character: " + text); 760 761 endStartElement(); 762 763 if (hasXML11RestrictedChars) 764 writeEncodedWithRestrictedChars(text, false); 765 else 766 writeEncoded(text, false); 767 } 768 catch (IOException e) 769 { 770 XMLStreamException e2 = new XMLStreamException(e); 771 e2.initCause(e); 772 throw e2; 773 } 774 } 775 writeCharacters(char[] text, int start, int len)776 public void writeCharacters(char[] text, int start, int len) 777 throws XMLStreamException 778 { 779 writeCharacters(new String(text, start, len)); 780 } 781 getPrefix(String uri)782 public String getPrefix(String uri) 783 throws XMLStreamException 784 { 785 String prefix = namespaces.getPrefix(uri); 786 if (prefix == null && namespaceContext != null) 787 prefix = namespaceContext.getPrefix(uri); 788 return prefix; 789 } 790 setPrefix(String prefix, String uri)791 public void setPrefix(String prefix, String uri) 792 throws XMLStreamException 793 { 794 try 795 { 796 if (!isURI(uri)) 797 throw new IllegalArgumentException("illegal URI: " + uri); 798 if (!isPrefix(prefix)) 799 throw new IllegalArgumentException("illegal NCName: " + prefix); 800 } 801 catch (IOException e) 802 { 803 XMLStreamException e2 = new XMLStreamException(e); 804 e2.initCause(e); 805 throw e2; 806 } 807 if (!namespaces.declarePrefix(prefix, uri)) 808 throw new XMLStreamException("illegal prefix " + prefix); 809 } 810 setDefaultNamespace(String uri)811 public void setDefaultNamespace(String uri) 812 throws XMLStreamException 813 { 814 if (!isURI(uri)) 815 throw new IllegalArgumentException("illegal URI: " + uri); 816 if (!namespaces.declarePrefix(XMLConstants.DEFAULT_NS_PREFIX, uri)) 817 throw new XMLStreamException("illegal default namespace prefix"); 818 } 819 setNamespaceContext(NamespaceContext context)820 public void setNamespaceContext(NamespaceContext context) 821 throws XMLStreamException 822 { 823 namespaceContext = context; 824 } 825 getNamespaceContext()826 public NamespaceContext getNamespaceContext() 827 { 828 return namespaceContext; 829 } 830 getProperty(String name)831 public Object getProperty(String name) 832 throws IllegalArgumentException 833 { 834 throw new IllegalArgumentException(name); 835 } 836 837 /** 838 * Write the specified text, ensuring that the content is suitably encoded 839 * for XML. 840 * @param text the text to write 841 * @param inAttr whether we are in an attribute value 842 */ writeEncoded(String text, boolean inAttr)843 private void writeEncoded(String text, boolean inAttr) 844 throws IOException 845 { 846 char[] chars = text.toCharArray(); 847 int start = 0; 848 int end = chars.length; 849 int len = 0; 850 for (int i = start; i < end; i++) 851 { 852 char c = chars[i]; 853 if (c == '<' || c == '>' || c == '&') 854 { 855 writer.write(chars, start, len); 856 if (c == '<') 857 writer.write("<"); 858 else if (c == '>') 859 writer.write(">"); 860 else 861 writer.write("&"); 862 start = i + 1; 863 len = 0; 864 } 865 else if (inAttr && (c == '"' || c == '\'')) 866 { 867 writer.write(chars, start, len); 868 if (c == '"') 869 writer.write("""); 870 else 871 writer.write("'"); 872 start = i + 1; 873 len = 0; 874 } 875 else 876 len++; 877 } 878 if (len > 0) 879 writer.write(chars, start, len); 880 } 881 882 /** 883 * Writes the specified text, in the knowledge that some of the 884 * characters are XML 1.1 restricted characters. 885 */ writeEncodedWithRestrictedChars(String text, boolean inAttr)886 private void writeEncodedWithRestrictedChars(String text, boolean inAttr) 887 throws IOException 888 { 889 int[] seq = UnicodeReader.toCodePointArray(text); 890 for (int i = 0; i < seq.length; i++) 891 { 892 int c = seq[i]; 893 switch (c) 894 { 895 case 0x3c: // '<' 896 writer.write("<"); 897 break; 898 case 0x3e: // '>' 899 writer.write(">"); 900 break; 901 case 0x26: // '&' 902 writer.write("&"); 903 break; 904 case 0x22: // '"' 905 if (inAttr) 906 writer.write("""); 907 else 908 writer.write(c); 909 break; 910 case 0x27: // '\'' 911 if (inAttr) 912 writer.write("'"); 913 else 914 writer.write(c); 915 break; 916 default: 917 if (XMLParser.isXML11RestrictedChar(c)) 918 writer.write("&#x" + Integer.toHexString(c) + ";"); 919 else 920 { 921 char[] chars = Character.toChars(c); 922 writer.write(chars, 0, chars.length); 923 } 924 } 925 } 926 } 927 isName(String text)928 private boolean isName(String text) 929 throws IOException 930 { 931 if (text == null) 932 return false; 933 int[] seq = UnicodeReader.toCodePointArray(text); 934 if (seq.length < 1) 935 return false; 936 if (!XMLParser.isNameStartCharacter(seq[0], xml11)) 937 return false; 938 for (int i = 1; i < seq.length; i++) 939 { 940 if (!XMLParser.isNameCharacter(seq[i], xml11)) 941 return false; 942 } 943 return true; 944 } 945 isPrefix(String text)946 private boolean isPrefix(String text) 947 throws IOException 948 { 949 if (XMLConstants.DEFAULT_NS_PREFIX.equals(text)) { 950 return true; 951 } 952 return isNCName(text); 953 } 954 isNCName(String text)955 private boolean isNCName(String text) 956 throws IOException 957 { 958 if (text == null) 959 return false; 960 int[] seq = UnicodeReader.toCodePointArray(text); 961 if (seq.length < 1) 962 return false; 963 if (!XMLParser.isNameStartCharacter(seq[0], xml11) || seq[0] == 0x3a) 964 return false; 965 for (int i = 1; i < seq.length; i++) 966 { 967 if (!XMLParser.isNameCharacter(seq[i], xml11) || seq[i] == 0x3a) 968 return false; 969 } 970 return true; 971 } 972 isChars(String text)973 private boolean isChars(String text) 974 throws IOException 975 { 976 if (text == null) 977 return false; 978 int[] seq = UnicodeReader.toCodePointArray(text); 979 hasXML11RestrictedChars = false; 980 if (xml11) 981 { 982 for (int i = 0; i < seq.length; i++) 983 { 984 if (!XMLParser.isXML11Char(seq[i])) 985 return false; 986 if (XMLParser.isXML11RestrictedChar(seq[i])) 987 hasXML11RestrictedChars = true; 988 } 989 } 990 else 991 { 992 for (int i = 0; i < seq.length; i++) 993 { 994 if (!XMLParser.isChar(seq[i])) 995 return false; 996 } 997 } 998 return true; 999 } 1000 isURI(String text)1001 private boolean isURI(String text) 1002 { 1003 if (text == null) 1004 return false; 1005 char[] chars = text.toCharArray(); 1006 if (chars.length < 1) 1007 return false; 1008 for (int i = 0; i < chars.length; i++) 1009 { 1010 if (chars[i] < 0x20 || chars[i] >= 0x7f) 1011 return false; 1012 } 1013 return true; 1014 } 1015 1016 } 1017