1 28 29 package com.caucho.xml; 30 31 import com.caucho.util.CharBuffer; 32 import com.caucho.vfs.Path; 33 import com.caucho.vfs.ReadStream; 34 import com.caucho.vfs.ReaderWriterStream; 35 import com.caucho.vfs.Vfs; 36 import com.caucho.vfs.WriteStream; 37 import com.caucho.xml.readers.MacroReader; 38 import com.caucho.xml.readers.Utf16Reader; 39 import com.caucho.xml.readers.Utf8Reader; 40 import com.caucho.xml.readers.XmlReader; 41 42 import org.w3c.dom.Document ; 43 import org.w3c.dom.Node ; 44 import org.xml.sax.InputSource ; 45 import org.xml.sax.Locator ; 46 import org.xml.sax.SAXException ; 47 import org.xml.sax.SAXParseException ; 48 49 import java.io.FileNotFoundException ; 50 import java.io.IOException ; 51 import java.io.InputStream ; 52 import java.util.ArrayList ; 53 import java.util.Arrays ; 54 import java.util.logging.Level ; 55 56 62 public class XmlParser extends AbstractParser { 63 public static final String XMLNS = "http://www.w3.org/2000/xmlns/"; 65 public static final String XML = "http://www.w3.org/XML/1998/namespace"; 66 67 static final QName DOC_NAME = new QName(null, "#document", null); 68 static final QName TEXT_NAME = new QName(null, "#text", null); 69 static final QName JSP_NAME = new QName(null, "#jsp", null); 70 static final QName WHITESPACE_NAME = new QName(null, "#whitespace", null); 71 static final QName JSP_ATTRIBUTE_NAME = new QName("xtp", "jsp-attribute", null); 72 73 QAttributes _attributes; 74 QAttributes _nullAttributes; 75 76 boolean _inDtd; 77 78 CharBuffer _text; 79 CharBuffer _eltName; 80 CharBuffer _cb; 81 CharBuffer _buf = new CharBuffer(); 82 String _textFilename; 83 int _textLine; 84 85 char []_textBuffer = new char[1024]; 86 int _textLength; 87 int _textCapacity = _textBuffer.length; 88 boolean _isIgnorableWhitespace; 89 boolean _isJspText; 90 91 CharBuffer _name = new CharBuffer(); 92 CharBuffer _nameBuffer = new CharBuffer(); 93 94 MacroReader _macro = new MacroReader(); 95 int _macroIndex = 0; 96 int _macroLength = 0; 97 char []_macroBuffer; 98 99 QName []_elementNames = new QName[64]; 100 NamespaceMap []_namespaces = new NamespaceMap[64]; 101 int []_elementLines = new int[64]; 102 int _elementTop; 103 104 NamespaceMap _namespaceMap; 105 106 ArrayList <String > _attrNames = new ArrayList <String >(); 107 ArrayList <String > _attrValues = new ArrayList <String >(); 108 109 ReadStream _is; 110 XmlReader _reader; 111 112 String _extPublicId; 113 String _extSystemId; 114 115 QName _activeNode; 116 QName _topNamespaceNode; 117 boolean _isTagStart; 118 boolean _stopOnIncludeEnd; 119 boolean _hasTopElement; 120 boolean _hasDoctype; 121 boolean _isHtml; 122 Locator _locator = new LocatorImpl(this); 123 124 public XmlParser() 125 { 126 clear(); 127 } 128 129 135 XmlParser(Policy policy, QDocumentType dtd) 136 { 137 super(policy, dtd); 138 139 clear(); 140 } 141 142 145 void init() 146 { 147 super.init(); 148 149 _attributes = new QAttributes(); 150 _nullAttributes = new QAttributes(); 151 _eltName = new CharBuffer(); 152 _text = new CharBuffer(); 153 154 _isHtml = _policy instanceof HtmlPolicy; 155 156 159 _textLength = 0; 160 _isIgnorableWhitespace = true; 161 _elementTop = 0; 162 _elementLines[0] = 1; 163 164 _line = 1; 165 166 _dtd = null; 167 _inDtd = false; 168 _isTagStart = false; 169 _stopOnIncludeEnd = false; 170 171 _extPublicId = null; 172 _extSystemId = null; 173 174 _filename = null; 175 _publicId = null; 176 _systemId = null; 177 178 _hasTopElement = false; 179 _hasDoctype = false; 180 181 _macroIndex = 0; 182 _macroLength = 0; 183 184 _reader = null; 185 186 188 _policy.init(); 189 } 190 191 198 Document parseInt(ReadStream is) 199 throws IOException , SAXException 200 { 201 _is = is; 202 203 if (_filename == null && _systemId != null) 204 _filename = _systemId; 205 else if (_filename == null) 206 _filename = _is.getUserPath(); 207 208 if (_systemId == null) { 209 _systemId = _is.getPath().getURL(); 210 if ("null:".equals(_systemId) || "string:".equals(_systemId)) 211 _systemId = "stream"; 212 } 213 214 218 _policy.setNamespaceAware(_isNamespaceAware); 219 220 if (_filename == null) 221 _filename = _systemId; 222 223 if (_filename == null) 224 _filename = "stream"; 225 226 if (_dtd != null) 227 _dtd.setSystemId(_systemId); 228 229 if (_builder != null) { 230 if (! "string:".equals(_systemId) && ! "stream".equals(_systemId)) 231 _builder.setSystemId(_systemId); 232 _builder.setFilename(_is.getPath().getURL()); 233 } 234 235 if (_contentHandler == null) 236 _contentHandler = new org.xml.sax.helpers.DefaultHandler (); 237 238 _contentHandler.setDocumentLocator(_locator); 239 240 if (_owner == null) 241 _owner = new QDocument(); 242 if (_defaultEncoding != null) 243 _owner.setAttribute("encoding", _defaultEncoding); 244 _owner.addDepend(is.getPath()); 245 246 _activeNode = DOC_NAME; 247 248 _policy.setStream(is); 249 _policy.setNamespace(_namespaceMap); 250 251 _contentHandler.startDocument(); 252 253 int ch = parseXMLDeclaration(null); 254 255 ch = skipWhitespace(ch); 256 parseNode(ch, false); 257 258 264 265 if (_strictXml && ! _hasTopElement) 266 throw error(L.l("XML file has no top-element. All well-formed XML files have a single top-level element.")); 267 268 if (_contentHandler != null) 269 _contentHandler.endDocument(); 270 271 QDocument owner = _owner; 272 _owner = null; 273 274 return owner; 275 } 276 277 284 private void parseNode(int ch, boolean special) 285 throws IOException , SAXException 286 { 287 289 _text.clear(); 290 291 loop: 292 while (true) { 293 if (_textLength == 0) { 294 _textFilename = getFilename(); 295 _textLine = getLine(); 296 } 297 298 switch (ch) { 299 case -1: 300 if (_textLength != 0) 301 appendText(); 302 if (! _stopOnIncludeEnd && _reader.getNext() != null) { 303 popInclude(); 304 if (_reader != null) 305 parseNode(_reader.read(), special); 306 return; 307 } 308 closeTag(""); 309 return; 310 311 case ' ': case '\t': case '\n': case '\r': 312 if (! _normalizeWhitespace) 313 addText((char) ch); 314 else if (_textLength == 0) { 315 if (! _isTagStart) 316 addText(' '); 317 } 318 else if (_textBuffer[_textLength - 1] != ' ') { 319 addText(' '); 320 } 321 ch = _reader.read(); 322 break; 323 324 case 0xffff: 325 return; 327 328 default: 329 addText((char) ch); 330 ch = _reader.read(); 331 break; 332 333 case '/': 334 if (! special) { 335 addText((char) ch); 336 ch = _reader.read(); 337 continue; 338 } 339 ch = _reader.read(); 340 if (ch == '>' || ch == -1) { 341 appendText(); 342 popNode(); 343 return; 344 } 345 addText('/'); 346 break; 347 348 case '&': 349 ch = parseEntityReference(); 350 break; 351 352 case '<': 353 boolean endTag = false; 354 ch = _reader.read(); 355 356 if (ch == '/' && ! special) { 357 if (_normalizeWhitespace && 358 _textLength > 0 && _textBuffer[_textLength - 1] == ' ') { 359 _textLength--; 360 } 361 appendText(); 362 363 ch = _reader.parseName(_name, _reader.read()); 364 365 if (ch != '>') { 366 while (XmlChar.isWhitespace(ch)) 368 ch = _reader.read(); 369 370 if (ch != '>') 371 throw error(L.l("`</{0}>' expected `>' at {1}. Closing tags must close immediately after the tag name.", _name, badChar(ch))); 372 } 373 374 closeTag(_policy.getName(_name).getName()); 375 ch = _reader.read(); 376 } 377 else if (XmlChar.isNameStart(ch)) { 379 appendText(); 380 381 parseElement(ch); 382 ch = _reader.read(); 383 } 384 else if (ch == '!') { 386 if ((ch = _reader.read()) == '[') { 388 parseCdata(); 389 ch = _reader.read(); 390 } 391 else if (ch == '-') { 393 parseComment(); 394 395 ch = _reader.read(); 396 } 397 else if (XmlChar.isNameStart(ch)) { 398 appendText(); 399 ch = _reader.parseName(_name, ch); 400 String declName = _name.toString(); 401 if (declName.equals("DOCTYPE")) { 402 parseDoctype(ch); 403 if (_contentHandler instanceof DOMBuilder) 404 ((DOMBuilder) _contentHandler).dtd(_dtd); 405 406 ch = _reader.read(); 407 } else if (_forgiving && declName.equalsIgnoreCase("doctype")) { 408 parseDoctype(ch); 409 if (_contentHandler instanceof DOMBuilder) 410 ((DOMBuilder) _contentHandler).dtd(_dtd); 411 412 ch = _reader.read(); 413 } else 414 throw error(L.l("expected `<!DOCTYPE' declaration at {0}", declName)); 415 } else if (_forgiving) { 416 addText("<!"); 417 } else 418 throw error(L.l("expected `<!DOCTYPE' declaration at {0}", badChar(ch))); 419 } 420 else if (ch == '?') { 422 ch = parsePI(); 423 } 424 else if (_strictXml) { 425 throw error(L.l("expected tag name after `<' at {0}. Open tag names must immediately follow the open brace like `<foo ...>'", badChar(ch))); 426 } 427 else if (_isJsp && ch == '%') { 429 ch = _reader.read(); 430 431 appendText(); 432 _isJspText = ch != '='; 433 434 addText("<%"); 435 436 while (ch >= 0) { 437 if (ch == '%') { 438 ch = _reader.read(); 439 if (ch == '>') { 440 addText("%>"); 441 ch = _reader.read(); 442 break; 443 } 444 else 445 addText('%'); 446 } 447 else { 448 addText((char) ch); 449 ch = _reader.read(); 450 } 451 } 452 453 appendText(); 454 _isJspText = false; 455 } 456 else { 457 addText('<'); 458 } 459 } 460 } 461 } 462 463 466 private void parseDoctype(int ch) 467 throws IOException , SAXException 468 { 469 if (_activeNode != DOC_NAME) 470 throw error(L.l("<!DOCTYPE immediately follow the <?xml ...?> declaration.")); 471 472 _inDtd = true; 473 474 ch = skipWhitespace(ch); 475 ch = _reader.parseName(_nameBuffer, ch); 476 String name = _nameBuffer.toString(); 477 ch = skipWhitespace(ch); 478 479 if (_dtd == null) 480 _dtd = new QDocumentType(name); 481 482 _dtd.setName(name); 483 484 if (XmlChar.isNameStart(ch)) { 485 ch = parseExternalID(ch); 486 ch = skipWhitespace(ch); 487 488 _dtd._publicId = _extPublicId; 489 _dtd._systemId = _extSystemId; 490 } 491 492 if (_dtd._systemId != null && ! _dtd._systemId.equals("")) { 493 InputStream is = null; 494 495 unread(ch); 496 497 XmlReader oldReader = _reader; 498 boolean hasInclude = false; 499 500 try { 501 pushInclude(_extPublicId, _extSystemId); 502 hasInclude = true; 503 } catch (Exception e) { 504 if (log.isLoggable(Level.FINEST)) 505 log.log(Level.FINER, e.toString(), e); 506 else 507 log.finer(e.toString()); 508 } 509 510 if (hasInclude) { 511 _stopOnIncludeEnd = true; 512 try { 513 ch = parseDoctypeDecl(_dtd); 514 } catch (XmlParseException e) { 515 if (_extSystemId != null && 516 _extSystemId.startsWith("http")) { 517 log.log(Level.FINE, e.toString(), e); 518 } 519 else 520 throw e; 521 } 522 _stopOnIncludeEnd = false; 523 524 while (_reader != null && _reader != oldReader) 525 popInclude(); 526 } 527 528 if (_reader != null) 529 ch = skipWhitespace(read()); 530 } 531 532 if (ch == '[') 533 ch = parseDoctypeDecl(_dtd); 534 535 ch = skipWhitespace(ch); 536 537 _inDtd = false; 538 539 if (ch != '>') 540 throw error(L.l("expected `>' in <!DOCTYPE at {0}", 541 badChar(ch))); 542 } 543 544 559 private int parseDoctypeDecl(QDocumentType doctype) 560 throws IOException , SAXException 561 { 562 _hasDoctype = true; 563 int ch = 0; 564 565 for (ch = skipWhitespace(read()); 566 ch >= 0 && ch != ']'; 567 ch = skipWhitespace(read())) { 568 if (ch == '<') { 569 if ((ch = read()) == '!') { 570 if (XmlChar.isNameStart(ch = read())) { 571 ch = _reader.parseName(_text, ch); 572 String name = _text.toString(); 573 574 if (name.equals("ELEMENT")) 575 parseElementDecl(doctype); 576 else if (name.equals("ATTLIST")) 577 parseAttlistDecl(doctype); 578 else if (name.equals("NOTATION")) 579 parseNotationDecl(doctype); 580 else if (name.equals("ENTITY")) 581 parseEntityDecl(doctype); 582 else 583 throw error("unknown declaration `" + name + "'"); 584 } 585 else if (ch == '-') 586 parseComment(); 587 else if (ch == '[') { 588 ch = _reader.parseName(_text, read()); 589 String name = _text.toString(); 590 591 if (name.equals("IGNORE")) { 592 parseIgnore(); 593 } 594 else if (name.equals("INCLUDE")) { 595 parseIgnore(); 596 } 597 else 598 throw error("unknown declaration `" + name + "'"); 599 } 600 } 601 else if (ch == '?') { 602 parsePI(); 603 } 604 else 605 throw error(L.l("expected markup at {0}", badChar(ch))); 606 } 607 else if (ch == '%') { 608 ch = _reader.parseName(_buf, read()); 609 610 if (ch != ';') 611 throw error(L.l("`%{0};' expects `;' at {1}. Parameter entities have a `%name;' syntax.", _buf, badChar(ch))); 612 613 addPEReference(_text, _buf.toString()); 614 } 615 else { 616 throw error(L.l("expected '<' at {0}", badChar(ch))); 617 } 618 619 _text.clear(); 620 } 621 _text.clear(); 622 623 return read(); 624 } 625 626 631 private void parseElement(int ch) 632 throws IOException , SAXException 633 { 634 ch = _reader.parseName(_eltName, ch); 635 636 NamespaceMap oldNamespace = _namespaceMap; 637 638 if (ch != '>' && ch != '/') 639 ch = parseAttributes(ch, true); 640 else 641 _attributes.clear(); 642 643 QName qname = _policy.getName(_eltName); 644 645 if (_isValidating && _dtd != null) { 646 QElementDef elementDef = _dtd.getElement(qname.getName()); 647 648 if (elementDef != null) 649 elementDef.fillDefaults(_attributes); 650 } 651 652 if (ch == '/') { 653 if ((ch = _reader.read()) == '>') { 655 addElement(qname, true, _attributes, oldNamespace); 656 } 657 else { 659 addElement(qname, false, _attributes, oldNamespace); 660 parseNode(ch, true); 661 } 662 } else if (ch == '>') { 663 addElement(qname, false, _attributes, oldNamespace); 664 } else 665 throw error(L.l("unexpected character {0} while parsing `{1}' attributes. Expected an attribute name or `>' or `/>'. XML element syntax is:\n <name attr-1=\"value-1\" ... attr-n=\"value-n\">", 666 badChar(ch), qname.getName())); 667 } 668 669 676 private int parseAttributes(int ch, boolean isElement) 677 throws IOException , SAXException 678 { 679 ch = skipWhitespace(ch); 680 _attributes.clear(); 681 682 _attrNames.clear(); 683 _attrValues.clear(); 684 685 boolean hasWhitespace = true; 686 687 while (ch != -1) { 688 if (! XmlChar.isNameStart(ch)) { 689 if (! _isJsp || ch != '<') 690 break; 691 692 ch = parseJspAttribute(isElement); 693 continue; 694 } 695 696 if (! hasWhitespace) 697 throw error(L.l("attributes must be separated by whitespace")); 698 699 hasWhitespace = false; 700 701 ch = _reader.parseName(_text, ch); 702 703 if (! _text.startsWith("xmlns")) { 704 } 705 else { 706 QName name; 707 708 if (_isNamespaceAware && _contentHandler instanceof DOMBuilder) 709 name = _policy.getNamespaceName(_text); 710 else 711 name = new QName(_text.toString(), null); 712 713 String prefix; 714 715 if (_text.length() > 5) { 716 prefix = _text.substring(6); 717 718 if (prefix.equals("")) 719 throw error(L.l("'{0}' is an illegal namespace declaration.", 720 _text)); 721 } 722 else { 723 prefix = ""; 724 } 725 726 _text.clear(); 727 ch = skipWhitespace(ch); 728 if (ch != '=') 729 throw error(L.l("xmlns: needs value at {0}", badChar(ch))); 730 ch = skipWhitespace(_reader.read()); 731 ch = parseValue(_text, ch, true); 732 733 hasWhitespace = isWhitespace(ch); 734 735 ch = skipWhitespace(ch); 736 737 String uri = _text.toString(); 739 740 if (_isXmlnsPrefix) { 741 _namespaceMap = new NamespaceMap(_namespaceMap, prefix, uri); 742 _policy.setNamespace(_namespaceMap); 743 744 _contentHandler.startPrefixMapping(prefix, uri); 745 } 746 747 if (isElement && _isXmlnsAttribute) { 749 _attributes.add(name, uri); 750 } 751 752 continue; 753 } 754 755 String attrName = _text.toString(); 756 _attrNames.add(attrName); 757 758 _text.clear(); 759 ch = skipWhitespace(ch); 760 761 String value = null; 762 763 if (ch == '=') { 764 ch = skipWhitespace(_reader.read()); 765 ch = parseValue(_text, ch, true); 766 767 hasWhitespace = isWhitespace(ch); 768 769 ch = skipWhitespace(ch); 770 771 value = _text.toString(); 772 } 773 else if (_strictAttributes) { 774 throw error(L.l("attribute `{0}' expects value at {1}. XML requires attributes to have explicit values.", 775 attrName, badChar(ch))); 776 } 777 else { 778 value = attrName; hasWhitespace = true; 780 } 781 782 _attrValues.add(value); 783 } 784 785 int len = _attrNames.size(); 786 for (int i = 0; i < len; i++) { 787 String attrName = _attrNames.get(i); 788 String value = _attrValues.get(i); 789 790 _text.clear(); 791 _text.append(attrName); 792 QName name; 793 794 if (_contentHandler instanceof DOMBuilder) 795 name = _policy.getAttributeName(_eltName, _text, true); 796 else 797 name = _policy.getAttributeName(_eltName, _text); 798 799 _attributes.add(name, value); 800 } 801 802 return ch; 803 } 804 805 819 private int parseJspAttribute(boolean isElement) 820 throws IOException , XmlParseException 821 { 822 int ch = _reader.read(); 823 824 if (ch != '%') 825 throw error(L.l("unexpected char `{0}' in element", "%")); 826 827 ch = _reader.read(); 828 if (ch != '=') 829 throw error(L.l("unexpected char `{0}' in element", "=")); 830 831 _text.clear(); 832 ch = _reader.read(); 833 while (ch >= 0) { 834 if (ch == '%') { 835 ch = _reader.read(); 836 if (ch == '>') { 837 ch = _reader.read(); 838 break; 839 } 840 _text.append((char) ch); 841 } 842 else { 843 _text.append((char) ch); 844 ch = _reader.read(); 845 } 846 } 847 848 String value = _text.toString(); 849 850 if (isElement) 851 _attributes.add(JSP_ATTRIBUTE_NAME, value); 852 853 return ch; 854 } 855 856 861 private void closeTag(String endTagName) 862 throws IOException , SAXException 863 { 864 while (_activeNode != null && _activeNode != DOC_NAME) { 865 switch (_policy.elementCloseAction(this, _activeNode, endTagName)) { 866 case Policy.POP: 867 870 popNode(); 871 return; 872 873 case Policy.POP_AND_LOOP: 874 877 popNode(); 878 break; 879 880 case Policy.IGNORE: 881 return; 882 883 default: 884 throw new RuntimeException (); 885 } 886 } 887 888 if (! _extraForgiving && endTagName != null && ! endTagName.equals("")) 889 throw error(L.l("Unexpected end tag `</{0}>' at top-level. All open tags have already been closed.", 890 endTagName)); 891 } 892 893 896 private void handleResinInclude() 897 throws IOException , SAXException 898 { 899 String filename = _attributes.getValue("path"); 900 901 if (filename == null || filename.equals("")) 902 filename = _attributes.getValue("href"); 903 904 if (filename.equals("")) 905 throw error(L.l("<resin:include> expects a `path' attribute.")); 906 907 pushInclude(filename); 908 } 909 910 913 private void handleResinIncludeDirectory() 914 throws IOException , SAXException 915 { 916 String filename = _attributes.getValue("path"); 917 918 if (filename == null || filename.equals("")) 919 filename = _attributes.getValue("href"); 920 921 String extension = _attributes.getValue("extension"); 922 923 if (filename.equals("")) 924 throw error(L.l("<resin:include> expects a `path' attribute.")); 925 926 Path pwd; 927 if (_searchPath != null) 928 pwd = _searchPath; 929 else 930 pwd = Vfs.lookup(_systemId).getParent(); 931 932 Path dir = pwd.lookup(filename); 933 if (! dir.isDirectory()) 934 throw error(L.l("`{0}' is not a directory for resin:include-directory. The href for resin:include-directory must refer to a directory.", 935 dir.getNativePath())); 936 937 String []list = dir.list(); 938 Arrays.sort(list); 939 for (int i = list.length - 1; i >= 0; i--) { 940 if (list[i].startsWith(".") || 941 extension != null && ! list[i].endsWith(extension)) 942 continue; 943 944 pushInclude(dir.lookup(list[i]).getPath()); 945 } 946 } 947 948 private int parseNameToken(CharBuffer name, int ch) 949 throws IOException , SAXException 950 { 951 name.clear(); 952 953 if (! XmlChar.isNameChar(ch)) 954 throw error(L.l("expected name at {0}", badChar(ch))); 955 956 for (; XmlChar.isNameChar(ch); ch = _reader.read()) 957 name.append((char) ch); 958 959 return ch; 960 } 961 962 965 private void popNode() 966 throws SAXException 967 { 968 QName node = _activeNode; 969 970 if (_activeNode != DOC_NAME) { 971 String uri = _activeNode.getNamespaceURI(); 972 String localName = _activeNode.getLocalName(); 973 974 if (uri == null) { 975 uri = ""; 976 977 if (_isNamespaceAware) 978 localName = _activeNode.getName(); 979 else 980 localName = ""; 981 } 982 983 _contentHandler.endElement(uri, 984 localName, 985 _activeNode.getName()); 986 } 987 988 if (_elementTop > 0) { 989 _elementTop--; 990 NamespaceMap oldMap = _namespaces[_elementTop]; 991 992 popNamespaces(oldMap); 993 994 _activeNode = _elementNames[_elementTop]; 995 } 996 997 if (_elementTop == 0) 998 _activeNode = DOC_NAME; 999 } 1000 1001 public void pushNamespace(String prefix, String uri) 1002 { 1003 _namespaceMap = new NamespaceMap(_namespaceMap, prefix, uri); 1004 1005 _policy.setNamespace(_namespaceMap); 1006 } 1007 1008 private void popNamespaces(NamespaceMap oldMap) 1009 throws SAXException 1010 { 1011 for (; 1012 _namespaceMap != null && _namespaceMap != oldMap; 1013 _namespaceMap = _namespaceMap.next) { 1014 _contentHandler.endPrefixMapping(_namespaceMap.prefix); 1015 } 1016 _namespaceMap = oldMap; 1017 _policy.setNamespace(_namespaceMap); 1018 } 1019 1020 private void appendText(String s) 1021 { 1022 if (_text.length() == 0) { 1023 _textFilename = getFilename(); 1024 _textLine = getLine(); 1025 } 1026 1027 _text.append(s); 1028 } 1029 1030 1038 private int parseEntityReference() 1039 throws IOException , SAXException 1040 { 1041 int ch; 1042 1043 ch = _reader.read(); 1044 1045 if (ch == '#') { 1047 addText((char) parseCharacterReference()); 1048 1049 return _reader.read(); 1050 } 1051 else if (XmlChar.isNameStart(ch)) { 1053 ch = _reader.parseName(_buf, ch); 1054 1055 if (ch != ';' && _strictXml) 1056 throw error(L.l("`&{0};' expected `;' at {0}. Entity references have a `&name;' syntax.", _buf, badChar(ch))); 1057 else if (ch != ';') { 1058 addText('&'); 1059 addText(_buf.toString()); 1060 return ch; 1061 } 1062 1063 addEntityReference(_buf.toString()); 1064 1065 ch = _reader.read(); 1066 1067 return ch; 1068 } else if (_strictXml) { 1069 throw error(L.l("expected name at {0}", badChar(ch))); 1070 } else { 1071 addText('&'); 1072 return ch; 1073 } 1074 } 1075 1076 private int parseCharacterReference() 1077 throws IOException , SAXException 1078 { 1079 int ch = _reader.read(); 1080 1081 int radix = 10; 1082 if (ch == 'x') { 1083 radix = 16; 1084 ch = _reader.read(); 1085 } 1086 1087 int value = 0; 1088 for (; ch != ';'; ch = _reader.read()) { 1089 if (ch >= '0' && ch <= '9') 1090 value = radix * value + ch - '0'; 1091 else if (radix == 16 && ch >= 'a' && ch <= 'f') 1092 value = radix * value + ch - 'a' + 10; 1093 else if (radix == 16 && ch >= 'A' && ch <= 'F') 1094 value = radix * value + ch - 'A' + 10; 1095 else 1096 throw error(L.l("malformed entity ref at {0}", badChar(ch))); 1097 } 1098 1099 if (value > 0xffff) 1100 throw error(L.l("malformed entity ref at {0}", "" + value)); 1101 1102 if (_strictCharacters && ! isChar(value)) 1104 throw error(L.l("illegal character ref at {0}", badChar(value))); 1105 1106 return value; 1107 } 1108 1109 1112 private void addEntityReference(String name) 1113 throws IOException , SAXException 1114 { 1115 boolean expand = ! _entitiesAsText || _hasDoctype || ! _switchToXml; 1116 1118 if (! expand) { 1119 addText("&" + name + ";"); 1120 return; 1121 } 1122 1123 int ch = _entities.getEntity(name); 1124 if (ch >= 0 && ch <= 0xffff) { 1125 addText((char) ch); 1126 return; 1127 } 1128 1129 QEntity entity = _dtd == null ? null : _dtd.getEntity(name); 1130 1131 if (! _expandEntities) { 1132 addText("&" + name + ";"); 1133 return; 1134 } 1135 1136 if (entity == null && (_dtd == null || _dtd.getName() == null || 1137 ! _dtd.isExternal())) { 1138 if (_strictXml) 1139 throw error(L.l("`&{0};' is an unknown entity. XML predefines only `<', `&', `>', `'' and `"'. All other entities must be defined in an <!ENTITY> definition in the DTD.", name)); 1140 else { 1141 if (expand && _contentHandler instanceof DOMBuilder) { 1142 appendText(); 1143 ((DOMBuilder) _contentHandler).entityReference(name); 1144 } 1145 else 1146 addText("&" + name + ";"); 1147 } 1148 } 1149 else if (entity != null) { 1150 if (expand && entity._isSpecial && entity._value != null) 1151 addText(entity._value); 1152 else if (entity.getSystemId() != null) { 1153 if (pushSystemEntity(entity)) { 1154 } 1155 1160 else if (_contentHandler instanceof DOMBuilder) { 1161 appendText(); 1162 ((DOMBuilder) _contentHandler).entityReference(name); 1163 } 1164 else 1165 addText("&" + name + ";"); 1166 } 1167 else if (expand && entity._value != null) 1168 setMacro(entity._value); 1169 else 1170 addText("&" + name + ";"); 1171 } 1172 else { 1173 if (expand && _contentHandler instanceof DOMBuilder) { 1174 appendText(); 1175 ((DOMBuilder) _contentHandler).entityReference(name); 1176 } 1177 else addText("&" + name + ";"); 1179 } 1180 } 1181 1182 private boolean pushSystemEntity(QEntity entity) 1183 throws IOException , SAXException 1184 { 1185 String publicId = entity.getPublicId(); 1186 String systemId = entity.getSystemId(); 1187 String value = null; 1188 InputSource source = null; 1189 ReadStream is = null; 1190 1191 if (_entityResolver != null) 1192 source = _entityResolver.resolveEntity(publicId, systemId); 1193 1194 if (source != null && source.getByteStream() != null) 1195 is = Vfs.openRead(source.getByteStream()); 1196 else if (source != null && source.getCharacterStream() != null) 1197 is = Vfs.openRead(source.getCharacterStream()); 1198 else if (source != null && source.getSystemId() != null && 1199 _searchPath.lookup(source.getSystemId()).isFile()) { 1200 _owner.addDepend(_searchPath.lookup(source.getSystemId())); 1201 is = _searchPath.lookup(source.getSystemId()).openRead(); 1202 } 1203 else if (systemId != null && ! systemId.equals("")) { 1204 String path = systemId; 1205 if (path.startsWith("file:")) 1206 path = path.substring(5); 1207 if (_searchPath.lookup(path).isFile()) { 1208 _owner.addDepend(_searchPath.lookup(path)); 1209 is = _searchPath.lookup(path).openRead(); 1210 } 1211 } 1212 1213 if (is == null) 1214 return false; 1215 1216 _filename = systemId; 1217 _systemId = systemId; 1218 1219 Path oldSearchPath = _searchPath; 1220 Path path = is.getPath(); 1221 if (path != null) { 1222 _owner.addDepend(path); 1223 1224 if (_searchPath != null) { 1225 _searchPath = path.getParent(); 1226 _reader.setSearchPath(oldSearchPath); 1227 } 1228 } 1229 1230 _is = is; 1231 _line = 1; 1232 1233 XmlReader oldReader = _reader; 1234 _reader = null; 1235 1236 int ch = parseXMLDeclaration(oldReader); 1237 unread(ch); 1238 1239 return true; 1240 } 1241 1242 1257 private int parseValue(CharBuffer value, int ch, boolean isGeneral) 1258 throws IOException , SAXException 1259 { 1260 int end = ch; 1261 1262 value.clear(); 1263 1264 if (end == '\'' || end == '"') 1265 ch = _reader.read(); 1266 else if (_strictAttributes) { 1267 value.append((char) end); 1268 for (ch = _reader.read(); 1269 ch >= 0 && XmlChar.isNameChar(ch); 1270 ch = _reader.read()) 1271 value.append((char) ch); 1272 1273 throw error(L.l("XML attribute value must be quoted at `{0}'. XML attribute syntax is either attr=\"value\" or attr='value'.", 1274 value)); 1275 } 1276 else 1277 end = 0; 1278 1279 while (ch != -1 && (end != 0 && ch != end || 1280 end == 0 && isAttributeChar(ch))) { 1281 if (end == 0 && ch == '/') { 1282 ch = _reader.read(); 1283 if (! isWhitespace(ch) && ch != '>') { 1284 value.append('/'); 1285 value.append((char) ch); 1286 } 1287 else { 1288 unread(ch); 1289 return '/'; 1290 } 1291 } 1292 else if (ch == '&' && ! _entitiesAsText) { 1293 if ((ch = _reader.read()) == '#') 1294 value.append((char) parseCharacterReference()); 1295 else if (! isGeneral) { 1296 value.append('&'); 1297 value.append((char) ch); 1298 } 1299 else if (XmlChar.isNameStart(ch)) { 1300 ch = _reader.parseName(_buf, ch); 1301 String name = _buf.toString(); 1302 1303 if (ch != ';' && _strictXml) 1304 throw error(L.l("expected `{0}' at {1}", ";", badChar(ch))); 1305 else if (ch != ';') { 1306 value.append('&'); 1307 value.append(name); 1308 continue; 1309 } else { 1310 int lookup = _entities.getEntity(name); 1311 1312 if (lookup >= 0 && lookup <= 0xffff) { 1313 ch = _reader.read(); 1314 value.append((char) lookup); 1315 continue; 1316 } 1317 1318 QEntity entity = _dtd == null ? null : _dtd.getEntity(name); 1319 if (entity != null && entity._value != null) 1320 setMacroAttr(entity._value); 1321 else if (_strictXml) 1322 throw error(L.l("expected local reference at `&{0};'", name)); 1323 else { 1324 value.append('&'); 1325 value.append(name); 1326 value.append(';'); 1327 } 1328 } 1329 } 1330 } 1331 else if (ch == '%' && ! isGeneral) { 1332 ch = _reader.read(); 1333 1334 if (! XmlChar.isNameStart(ch)) { 1335 value.append('%'); 1336 continue; 1337 } 1338 else { 1339 ch = _reader.parseName(_buf, ch); 1340 1341 if (ch != ';') 1342 throw error(L.l("expected `{0}' at {1}", ";", badChar(ch))); 1343 else 1344 addPEReference(value, _buf.toString()); 1345 } 1346 } 1347 else if (ch == '<' && _isJsp) { 1348 value.append('<'); 1349 1350 ch = _reader.read(); 1351 1352 if (ch != '%') 1353 continue; 1354 1355 value.append('%'); 1356 1357 ch = _reader.read(); 1358 while (ch >= 0) { 1359 if (ch == '%') { 1360 ch = _reader.read(); 1361 if (ch == '>') { 1362 value.append("%>"); 1363 break; 1364 } 1365 else 1366 value.append('%'); 1367 } 1368 else { 1369 value.append((char) ch); 1370 ch = _reader.read(); 1371 } 1372 } 1373 } 1374 else if (isGeneral) { 1375 if (ch == '\r') { 1376 ch = _reader.read(); 1377 if (ch != '\n') { 1378 value.append('\n'); 1379 continue; 1380 } 1381 } 1382 value.append((char) ch); 1383 } 1384 else if (ch == '\r') { 1385 value.append(' '); 1386 1387 if ((ch = _reader.read()) != '\n') 1388 continue; 1389 } 1390 else if (ch == '\n') 1391 value.append(' '); 1392 else 1393 value.append((char) ch); 1394 1395 ch = _reader.read(); 1396 } 1397 1398 if (end != 0) 1399 ch = _reader.read(); 1400 1401 return ch; 1402 } 1403 1404 private boolean isAttributeChar(int ch) 1405 { 1406 switch (ch) { 1407 case ' ': case '\t': case '\n': case '\r': 1408 return false; 1409 case '<': case '>': case '\'':case '"': case '=': 1410 return false; 1411 default: 1412 return true; 1413 } 1414 } 1415 1416 private void parsePcdata(QNode node) throws IOException , SAXException 1417 { 1418 int ch; 1419 String tail = "</" + node.getNodeName() + ">"; 1420 1421 _text.clear(); 1422 ch = _reader.read(); 1423 if (ch == '\n') 1424 ch = _reader.read(); 1425 1426 for (; ch != -1; ch = _reader.read()) { 1427 addText((char) ch); 1428 1429 if (_text.endsWith(tail)) { 1430 _text.setLength(_text.length() - tail.length()); 1431 if (_text.length() > 1 && _text.charAt(_text.length() - 1) == '\n') 1432 _text.setLength(_text.length() - 1); 1433 appendText(); 1434 return; 1435 } 1436 } 1437 1438 throw error("bad pcdata"); 1439 } 1440 1441 private int parseXMLDeclaration(XmlReader oldReader) 1442 throws IOException , SAXException 1443 { 1444 int startOffset = _is.getOffset(); 1445 boolean isEBCDIC = false; 1446 int ch = _is.read(); 1447 1448 XmlReader reader = null; 1449 1450 if (ch == 0xfe) { 1452 ch = _is.read(); 1453 if (ch == 0xff) { 1454 _owner.setAttribute("encoding", "UTF-16"); 1455 _is.setEncoding("utf-16"); 1456 1457 reader = new Utf16Reader(this, _is); 1458 1459 ch = reader.read(); 1460 } 1461 } 1462 else if (ch == 0xff) { 1464 ch = _is.read(); 1465 if (ch == 0xfe) { 1466 _owner.setAttribute("encoding", "UTF-16"); 1467 _is.setEncoding("utf-16"); 1468 1469 reader = new Utf16Reader(this, _is); 1470 ((Utf16Reader) reader).setReverse(true); 1471 1472 ch = reader.read(); 1473 } 1474 } 1475 else if (ch == 0x00) { 1477 ch = _is.read(); 1478 _owner.setAttribute("encoding", "UTF-16"); 1479 _is.setEncoding("utf-16"); 1480 1481 reader = new Utf16Reader(this, _is); 1482 } 1483 else if (ch == 0xef) { 1485 ch = _is.read(); 1486 if (ch == 0xbb) { 1487 ch = _is.read(); 1488 1489 if (ch == 0xbf) { 1490 ch = _is.read(); 1491 1492 _owner.setAttribute("encoding", "UTF-8"); 1493 _is.setEncoding("utf-8"); 1494 1495 reader = new Utf8Reader(this, _is); 1496 } 1497 } 1498 } 1499 else if (ch == 0x4c) { 1500 _is.unread(); 1503 _is.setEncoding("cp500"); 1505 1506 isEBCDIC = true; 1507 1508 reader = new XmlReader(this, _is); 1509 1510 ch = reader.read(); 1511 } 1512 else { 1513 int ch2 = _is.read(); 1514 1515 if (ch2 == 0x00) { 1516 _owner.setAttribute("encoding", "UTF-16LE"); 1517 _is.setEncoding("utf-16le"); 1518 1519 reader = new Utf16Reader(this, _is); 1520 ((Utf16Reader) reader).setReverse(true); 1521 } 1522 else if (ch2 > 0) 1523 _is.unread(); 1524 } 1525 1526 if (reader != null && reader != oldReader) { 1527 } 1528 else if (_policy instanceof HtmlPolicy || 1529 _is.getSource() instanceof ReaderWriterStream) { 1530 reader = new XmlReader(this, _is); 1531 } 1532 else { 1533 reader = new Utf8Reader(this, _is); 1534 } 1535 1536 if (ch == '\n') 1537 reader.setLine(2); 1538 1539 reader.setSystemId(_systemId); 1540 if (_systemId == null) 1541 reader.setSystemId(_filename); 1542 reader.setFilename(_filename); 1543 reader.setPublicId(_publicId); 1544 1545 reader.setNext(oldReader); 1546 1547 _reader = reader; 1548 1549 1550 1556 1557 if (ch != '<') 1558 return ch; 1559 1560 if (parseXMLDecl(_reader) && isEBCDIC) { 1561 _is.setOffset(startOffset); 1563 1564 ch = _reader.read(); 1565 if (ch != '<') 1566 throw new IllegalStateException (); 1567 1568 parseXMLDecl(_reader); 1569 } 1570 1571 return _reader.read(); 1572 } 1573 1574 private boolean parseXMLDecl(XmlReader reader) 1575 throws IOException , SAXException 1576 { 1577 int ch = reader.read(); 1578 if (ch != '?') { 1579 unread((char) ch); 1580 unread('<'); 1581 return false; 1582 } 1583 1584 ch = _reader.read(); 1585 if (! XmlChar.isNameStart(ch)) 1586 throw error(L.l("expected name after '<?' at {0}. Processing instructions expect a name like <?foo ... ?>", badChar(ch))); 1587 ch = _reader.parseName(_text, ch); 1588 1589 String piName = _text.toString(); 1590 if (! piName.equals("xml")) { 1591 ch = parsePITail(piName, ch); 1592 unread(ch); 1593 return false; 1594 } 1595 1596 if (_switchToXml && _activeNode == DOC_NAME && ! _inDtd) { 1597 _policy = new XmlPolicy(); 1598 } 1599 1600 ch = parseAttributes(ch, false); 1601 1602 if (ch != '?') 1603 throw error(L.l("expected `?' at {0}. Processing instructions end with `?>' like <?foo ... ?>", badChar(ch))); 1604 if ((ch = _reader.read()) != '>') 1605 throw error(L.l("expected `>' at {0}. Processing instructions end with `?>' like <?foo ... ?>", ">", badChar(ch))); 1606 1607 for (int i = 0; i < _attributes.getLength(); i++) { 1608 QName name = _attributes.getName(i); 1609 String value = _attributes.getValue(i); 1610 1611 if (_owner != null) 1612 _owner.setAttribute(name.getName(), value); 1613 1614 if (name.getName().equals("encoding")) { String encoding = value; 1616 1617 if (! _isStaticEncoding && 1618 ! encoding.equalsIgnoreCase("UTF-8") && 1619 ! encoding.equalsIgnoreCase("UTF-16") && 1620 ! (_is.getSource() instanceof ReaderWriterStream)) { 1621 _is.setEncoding(encoding); 1622 1623 XmlReader oldReader = _reader; 1624 1625 _reader = new XmlReader(this, _is); 1626 1628 _reader.setLine(oldReader.getLine()); 1629 1630 _reader.setSystemId(_filename); 1631 _reader.setPublicId(null); 1632 } 1633 } 1634 } 1635 1636 return true; 1637 } 1638 1639 private int parsePI() 1640 throws IOException , SAXException 1641 { 1642 int ch; 1643 1644 appendText(); 1645 ch = _reader.read(); 1646 if (! XmlChar.isNameStart(ch)) 1647 throw error(L.l("expected name after '<?' at {0}. Processing instructions expect a name like <?foo ... ?>", badChar(ch))); 1648 ch = _reader.parseName(_text, ch); 1649 1650 String piName = _text.toString(); 1651 if (! piName.equals("xml")) 1652 return parsePITail(piName, ch); 1653 else if (_switchToXml && _activeNode == DOC_NAME && ! _inDtd) { 1654 _policy = new XmlPolicy(); 1655 return parsePITail(piName, ch); 1656 } 1657 else { 1658 throw error(L.l("<?xml ... ?> occurs after content. The <?xml ... ?> prolog must be at the document start.")); 1659 1660 } 1661 } 1662 1663 private int parsePITail(String piName, int ch) 1664 throws IOException , SAXException 1665 { 1666 ch = skipWhitespace(ch); 1667 1668 _text.clear(); 1669 while (ch != -1) { 1670 if (ch == '?') { 1671 if ((ch = _reader.read()) == '>') 1672 break; 1673 else 1674 _text.append('?'); 1675 } else { 1676 _text.append((char) ch); 1677 ch = _reader.read(); 1678 } 1679 } 1680 1681 if (_inDtd) { 1682 QProcessingInstruction pi; 1683 pi = new QProcessingInstruction(piName, _text.toString()); 1684 pi._owner = _dtd._owner; 1685 _dtd.appendChild(pi); 1686 } 1687 else 1688 _contentHandler.processingInstruction(piName, _text.toString()); 1689 1690 return _reader.read(); 1691 } 1692 1693 1696 private void parseComment() 1697 throws IOException , SAXException 1698 { 1699 if (! _skipComments) 1700 appendText(); 1701 1702 int ch = _reader.read(); 1703 1704 if (ch != '-') 1705 throw error(L.l("expected comment at {0}", badChar(ch))); 1706 1707 ch = _reader.read(); 1708 1709 if (! _skipComments) 1710 _buf.clear(); 1711 1712 comment: 1713 while (ch != -1) { 1714 if (ch == '-') { 1715 ch = _reader.read(); 1716 1717 while (ch == '-') { 1718 if ((ch = _reader.read()) == '>') 1719 break comment; 1720 else if (_strictComments) 1721 throw error(L.l("XML forbids `--' in comments")); 1722 else if (ch == '-') { 1723 if (! _skipComments) 1724 _buf.append('-'); 1725 } 1726 else { 1727 if (! _skipComments) 1728 _buf.append("--"); 1729 break; 1730 } 1731 } 1732 1733 _buf.append('-'); 1734 } else if (! XmlChar.isChar(ch)) { 1735 throw error(L.l("bad character {0}", hex(ch))); 1736 } else { 1737 _buf.append((char) ch); 1738 ch = _reader.read(); 1739 } 1740 } 1741 1742 if (_inDtd) { 1743 QComment comment = new QComment(_buf.toString()); 1744 comment._owner = _dtd._owner; 1745 _dtd.appendChild(comment); 1746 } 1747 else if (_skipComments) { 1748 } 1749 else if (_contentHandler instanceof XMLWriter && ! _skipComments) { 1750 ((XMLWriter) _contentHandler).comment(_buf.toString()); 1751 _isIgnorableWhitespace = true; 1752 } 1753 else if (_lexicalHandler != null) { 1754 _lexicalHandler.comment(_buf.getBuffer(), 0, _buf.getLength()); 1755 _isIgnorableWhitespace = true; 1756 } 1757 } 1758 1759 1766 private void parseCdata() 1767 throws IOException , SAXException 1768 { 1769 int ch; 1770 1771 if (_forgiving) { 1772 if ((ch = _reader.read()) != 'C') { 1773 appendText("<![" + (char) ch); 1774 return; 1775 } 1776 else if ((ch = _reader.read()) != 'D') { 1777 appendText("<![C" + (char) ch); 1778 return; 1779 } 1780 else if ((ch = _reader.read()) != 'A') { 1781 appendText("<![CD" + (char) ch); 1782 return; 1783 } 1784 else if ((ch = _reader.read()) != 'T') { 1785 appendText("<![CDA" + (char) ch); 1786 return; 1787 } 1788 else if ((ch = _reader.read()) != 'A') { 1789 appendText("<![CDAT" + (char) ch); 1790 return; 1791 } 1792 else if ((ch = _reader.read()) != '[') { 1793 appendText("<![CDATA" + (char) ch); 1794 return; 1795 } 1796 } 1797 else if ((ch = _reader.read()) != 'C' || 1798 (ch = _reader.read()) != 'D' || 1799 (ch = _reader.read()) != 'A' || 1800 (ch = _reader.read()) != 'T' || 1801 (ch = _reader.read()) != 'A' || 1802 (ch = _reader.read()) != '[') { 1803 throw error(L.l("expected `<![CDATA[' at {0}", badChar(ch))); 1804 } 1805 1806 ch = _reader.read(); 1807 1808 if (_lexicalHandler != null) { 1809 _lexicalHandler.startCDATA(); 1810 appendText(); 1811 } 1812 else if (! _isCoalescing) 1813 appendText(); 1814 1815 cdata: 1816 while (ch != -1) { 1817 if (ch == ']') { 1818 ch = _reader.read(); 1819 1820 while (ch == ']') { 1821 if ((ch = _reader.read()) == '>') 1822 break cdata; 1823 else if (ch == ']') 1824 addText(']'); 1825 else { 1826 addText(']'); 1827 break; 1828 } 1829 } 1830 1831 addText(']'); 1832 } else if (_strictCharacters && ! isChar(ch)) { 1833 throw error(L.l("expected character in cdata at {0}", badChar(ch))); 1834 } else { 1835 addText((char) ch); 1836 ch = _reader.read(); 1837 } 1838 } 1839 1840 if (_lexicalHandler != null) { 1841 appendText(); 1842 _lexicalHandler.endCDATA(); 1843 } 1844 else if (! _isCoalescing) 1845 appendText(); 1846 } 1847 1848 1851 private void parseIgnore() 1852 throws IOException , SAXException 1853 { 1854 int ch = read(); 1855 1856 while (ch >= 0) { 1857 if (ch != ']') { 1858 ch = read(); 1859 } 1860 else if ((ch = read()) != ']') { 1861 } 1862 else if ((ch = read()) == '>') 1863 return; 1864 } 1865 } 1866 1867 private int parseContentSpec(QElementDef def, int ch) 1868 throws IOException , SAXException 1869 { 1870 ch = expandPE(ch); 1871 1872 if (XmlChar.isNameStart(ch)) { 1873 ch = _reader.parseName(_text, ch); 1874 String name = _text.toString(); 1875 1876 if (name.equals("EMPTY")) { 1877 def._content = "EMPTY"; 1878 return ch; 1879 } 1880 else if (name.equals("ANY")) { 1881 def._content = "ANY"; 1882 return ch; 1883 } 1884 else 1885 throw error(L.l("expected EMPTY or ANY at `{0}'", name)); 1886 } 1887 else if (ch != '(') { 1888 throw error(L.l("expected grammar definition starting with '(' at {0}. <!ELEMENT> definitions have the syntax <!ELEMENT name - - (grammar)>", badChar(ch))); 1889 } 1890 else { 1891 QContentParticle cp = new QContentParticle(); 1892 def._content = cp; 1893 1894 return parseContentParticle(cp, true); 1895 } 1896 } 1897 1898 1902 private int parseContentParticle(QContentParticle cp, boolean isTop) 1903 throws IOException , SAXException 1904 { 1905 boolean hasCdata = false; 1906 cp._separator = 0; 1907 cp._repeat = 0; 1908 int ch; 1909 1910 ch = expandPE(_reader.read()); 1911 1912 for (; ch != -1; ch = expandPE(ch)) { 1913 if (ch == '(') { 1914 QContentParticle child = new QContentParticle(); 1915 cp.addChild(child); 1916 1917 ch = parseContentParticle(child, false); 1918 } 1919 else if (XmlChar.isNameStart(ch)) { 1920 ch = _reader.parseName(_text, ch); 1921 cp.addChild(_text.toString()); 1922 } 1923 else if (ch == '#') { 1924 ch = _reader.parseName(_text, _reader.read()); 1925 String name = _text.toString(); 1926 1927 if (_strictXml && cp._children.size() != 0) 1928 throw error(L.l("`#{0}' must occur first", name)); 1929 if (_strictXml && ! isTop) 1930 throw error(L.l("`#{0}' may only occur at top level", name)); 1931 1932 if (name.equals("PCDATA")) 1933 cp.addChild("#PCDATA"); 1934 else 1935 throw error(L.l("illegal content particle at `#{0}'", name)); 1936 1937 hasCdata = true; 1938 } 1939 else 1940 throw error(L.l("expected content particle at {0}", badChar(ch))); 1941 1942 ch = expandPE(ch); 1943 1944 if (ch == '?' || ch == '*' || ch == '+') { 1945 Object child = cp.getChild(cp.getChildSize() - 1); 1946 if (child instanceof QContentParticle) { 1947 QContentParticle cpChild = (QContentParticle) child; 1948 cpChild._repeat = ch; 1949 } 1950 else { 1951 QContentParticle cpChild = new QContentParticle(); 1952 cpChild.addChild(child); 1953 cpChild._repeat = ch; 1954 cp.setChild(cp.getChildSize() - 1, cpChild); 1955 } 1956 1957 ch = expandPE(_reader.read()); 1958 } 1959 1960 if (ch == ')') 1961 break; 1962 else if (cp._separator == 0) { 1963 if (ch == '|') 1964 cp._separator = ch; 1965 else if (hasCdata && _strictXml) 1966 throw error(L.l("#PCDATA must be separated by `|' at {0}", 1967 badChar(ch))); 1968 else if (ch == ',') 1969 cp._separator = ch; 1970 else if (! _strictXml && ch =='&') 1971 cp._separator = ch; 1972 else 1973 throw error(L.l("expected separator at {0}", badChar(ch))); 1974 1975 ch = _reader.read(); 1976 } else if (ch != cp._separator) 1977 throw error(L.l("expected `{0}' at {1}", 1978 "" + (char) cp._separator, badChar(ch))); 1979 else 1980 ch = _reader.read(); 1981 } 1982 1983 ch = expandPE(_reader.read()); 1984 1985 if (_strictXml && hasCdata && (ch == '+' || ch == '?')) 1986 throw error(L.l("pcdata clause can not have {0}", badChar(ch))); 1987 else if (ch == '*' || ch == '+' || ch == '?') { 1988 cp._repeat = ch; 1989 return _reader.read(); 1990 } 1991 else 1992 return ch; 1993 } 1994 1995 private int expandPE(int ch) 1996 throws IOException , SAXException 1997 { 1998 ch = skipWhitespace(ch); 1999 2000 while (ch == '%') { 2001 parsePEReference(); 2002 ch = skipWhitespace(_reader.read()); 2003 } 2004 2005 return ch; 2006 } 2007 2008 2012 private void parsePEReference() 2013 throws IOException , SAXException 2014 { 2015 int ch = _reader.parseName(_buf, _reader.read()); 2016 2017 if (ch != ';') 2018 throw error(L.l("`%{0};' expects `;' at {1}. Parameter entities have a `%name;' syntax.", _buf, badChar(ch))); 2019 2020 addPEReference(_text, _buf.toString()); 2021 } 2022 2023 2026 private void addPEReference(CharBuffer value, String name) 2027 throws IOException , SAXException 2028 { 2029 QEntity entity = _dtd.getParameterEntity(name); 2030 2031 if (entity == null && ! _dtd.isExternal()) 2032 throw error(L.l("`%{0};' is an unknown parameter entity. Parameter entities must be defined in an <!ENTITY> declaration before use.", name)); 2033 else if (entity != null && entity._value != null) { 2034 setMacro(entity._value); 2035 } 2036 else if (entity != null && entity.getSystemId() != null) { 2037 pushInclude(entity.getPublicId(), entity.getSystemId()); 2038 } 2039 else { 2040 value.append("%"); 2041 value.append(name); 2042 value.append(";"); 2043 } 2044 } 2045 2046 2049 private void parseElementDecl(QDocumentType doctype) 2050 throws IOException , SAXException 2051 { 2052 int ch = skipWhitespace(_reader.read()); 2053 2054 ch = _reader.parseName(_text, ch); 2055 String name = _text.toString(); 2056 2057 ch = skipWhitespace(ch); 2058 2059 QElementDef def = _dtd.addElement(name); 2060 def.setLocation(getSystemId(), getFilename(), getLine(), getColumn()); 2061 2062 boolean needsStartTag = true; 2063 boolean needsEndTag = true; 2064 2065 if (_optionalTags && (ch == 'O' || ch == '-')) { 2066 needsStartTag = ch == '-'; 2067 2068 ch = skipWhitespace(ch); 2069 2070 if (ch == '0') 2071 needsEndTag = false; 2072 else if (ch == '-') 2073 needsEndTag = true; 2074 else 2075 throw error(L.l("unknown short tag")); 2076 } 2077 2078 ch = parseContentSpec(def, ch); 2079 2080 ch = skipWhitespace(ch); 2081 2082 if (ch != '>') 2083 throw error(L.l("`<!ELEMENT' must close with `>' at {0}", badChar(ch))); 2084 } 2085 2086 private static String toAttrDefault(CharBuffer text) 2087 { 2088 for (int i = 0; i < text.length(); i++) { 2089 int ch = text.charAt(i); 2090 2091 if (ch == '"') { 2092 text.delete(i, i + 1); 2093 text.insert(i, """); 2094 i--; 2095 } else if (ch == '\'') { 2096 text.delete(i, i + 1); 2097 text.insert(i, "'"); 2098 i--; 2099 } 2100 } 2101 2102 return text.toString(); 2103 } 2104 2105 2108 private void parseAttlistDecl(QDocumentType doctype) 2109 throws IOException , SAXException 2110 { 2111 int ch = skipWhitespace(_reader.read()); 2112 2113 ch = _reader.parseName(_text, ch); 2114 String name = _text.toString(); 2115 2116 ch = skipWhitespace(ch); 2117 2118 QElementDef def = _dtd.addElement(name); 2119 2120 while (XmlChar.isNameStart((ch = expandPE(ch)))) { 2121 ch = _reader.parseName(_text, ch); 2122 String attrName = _text.toString(); 2123 2124 String attrType = null; 2125 ArrayList <String > enumeration = null; 2126 ch = expandPE(ch); 2127 if (ch == '(') { 2128 attrType = "#ENUM"; 2129 enumeration = new ArrayList <String >(); 2130 do { 2131 ch = expandPE(_reader.read()); 2132 2133 ch = parseNameToken(_text, ch); 2134 enumeration.add(_text.toString()); 2135 2136 ch = expandPE(ch); 2137 } while (ch == '|'); 2138 2139 if (ch != ')') 2140 throw error(L.l("expected `{0}' at {1}. <!ATTRLIST> enumerations definitions are enclosed in '(' ... ')'.", ")", badChar(ch))); 2141 ch = _reader.read(); 2142 } 2143 else { 2144 ch = _reader.parseName(_text, ch); 2145 attrType = _text.toString(); 2146 2147 if (attrType.equals("NOTATION")) { 2148 enumeration = new ArrayList <String >(); 2149 ch = expandPE(ch); 2150 if (ch != '(') 2151 throw error(L.l("expected `{0}' at {1}", "(", badChar(ch))); 2152 2153 do { 2154 ch = expandPE(_reader.read()); 2155 2156 ch = _reader.parseName(_text, ch); 2157 enumeration.add(_text.toString()); 2158 2159 ch = expandPE(ch); 2160 } while (ch == '|'); 2161 2162 if (ch != ')') 2163 throw error(L.l("expected `{0}' at {1}", ")", badChar(ch))); 2164 ch = _reader.read(); 2165 } 2166 else if (_attrTypes.get(attrType) != null) { 2167 } 2168 else 2169 throw error(L.l("expected attribute type at `{0}'", attrType)); 2170 } 2171 2172 ch = skipWhitespace(ch); 2173 String qualifier = null; 2174 String attrDefault = null; 2175 if (ch == '#') { 2176 ch = _reader.parseName(_text, _reader.read()); 2177 qualifier = "#" + _text.toString(); 2178 2179 if (qualifier.equals("#IMPLIED")) { 2180 } 2181 else if (qualifier.equals("#REQUIRED")) { 2182 } 2183 else if (qualifier.equals("#FIXED")) { 2184 ch = skipWhitespace(ch); 2185 ch = parseValue(_text, ch, false); 2186 attrDefault = _text.toString(); 2187 } else 2188 throw error(L.l("expected attribute default at `{0}'", 2189 qualifier)); 2190 } 2191 else if (ch != '>') { 2192 ch = parseValue(_text, ch, false); 2193 attrDefault = _text.toString(); 2194 } 2195 2196 def.addAttribute(attrName, attrType, enumeration, 2197 qualifier, attrDefault); 2198 if (attrType != null && attrType.equals("ID")) 2199 doctype.setElementId(name, attrName); 2200 2201 ch = skipWhitespace(ch); 2202 } 2203 2204 if (ch != '>') 2205 throw error(L.l("expected `{0}' at {1}", ">", badChar(ch))); 2206 } 2207 2208 2211 private void parseNotationDecl(QDocumentType doctype) 2212 throws IOException , SAXException 2213 { 2214 int ch = skipWhitespace(_reader.read()); 2215 2216 ch = _reader.parseName(_text, ch); 2217 String name = _text.toString(); 2218 2219 ch = skipWhitespace(ch); 2220 ch = _reader.parseName(_text, ch); 2221 String key = _text.toString(); 2222 2223 ch = skipWhitespace(ch); 2224 ch = parseValue(_text, ch, false); 2225 String id = _text.toString(); 2226 2227 ch = skipWhitespace(ch); 2228 2229 QNotation notation; 2230 2231 if (key.equals("PUBLIC")) { 2232 String systemId = null; 2233 2234 if (ch == '"' || ch == '\'') { 2235 ch = parseValue(_text, ch, false); 2236 ch = skipWhitespace(ch); 2237 systemId = _text.toString(); 2238 } 2239 2240 notation = new QNotation(name, id, systemId); 2241 notation._owner = doctype._owner; 2242 notation.setLocation(getSystemId(), getFilename(), getLine(), getColumn()); 2243 } 2244 else if (key.equals("SYSTEM")) { 2245 notation = new QNotation(name, null, id); 2246 notation._owner = doctype._owner; 2247 notation.setLocation(getSystemId(), getFilename(), getLine(), getColumn()); 2248 } 2249 else 2250 throw error(L.l("expected PUBLIC or SYSTEM at `{0}'", key)); 2251 2252 doctype.addNotation(notation); 2253 doctype.appendChild(notation); 2254 2255 if (ch != '>') 2256 throw error(L.l("expected `{0}' at {1}", ">", badChar(ch))); 2257 } 2258 2259 2263 private int parseExternalID(int ch) 2264 throws IOException , SAXException 2265 { 2266 ch = _reader.parseName(_text, ch); 2267 String key = _text.toString(); 2268 ch = skipWhitespace(ch); 2269 2270 _extSystemId = null; 2271 _extPublicId = null; 2272 if (key.equals("PUBLIC") || _forgiving && key.equalsIgnoreCase("public")) { 2273 ch = parseValue(_text, ch, false); 2274 _extPublicId = _text.toString(); 2275 ch = skipWhitespace(ch); 2276 2277 if (_extPublicId.indexOf('&') > 0) 2278 throw error(L.l("Illegal character '&' in PUBLIC identifier '{0}'", 2279 _extPublicId)); 2280 2281 ch = parseValue(_text, ch, false); 2282 ch = skipWhitespace(ch); 2283 _extSystemId = _text.toString(); 2284 } 2285 else if (key.equals("SYSTEM") || 2286 _forgiving && key.equalsIgnoreCase("system")) { 2287 ch = parseValue(_text, ch, false); 2288 _extSystemId = _text.toString(); 2289 } 2290 else 2291 throw error(L.l("expected PUBLIC or SYSTEM at `{0}'", key)); 2292 2293 return ch; 2294 } 2295 2296 2299 private void parseEntityDecl(QDocumentType doctype) 2300 throws IOException , SAXException 2301 { 2302 int ch = skipWhitespace(_reader.read()); 2303 2304 boolean isPe = ch == '%'; 2305 2306 if (isPe) 2307 ch = skipWhitespace(_reader.read()); 2308 2309 ch = _reader.parseName(_text, ch); 2310 String name = _text.toString(); 2311 2312 ch = skipWhitespace(ch); 2313 2314 QEntity entity; 2315 if (ch == '"' || ch == '\'') { 2316 ch = parseValue(_text, ch, false); 2317 2318 entity = new QEntity(name, _text.toString(), null, null); 2319 entity._owner = doctype._owner; 2320 entity.setLocation(getSystemId(), getFilename(), getLine(), getColumn()); 2321 } 2322 else { 2323 ch = parseExternalID(ch); 2324 2325 entity = new QEntity(name, null, _extPublicId, _extSystemId); 2326 entity._owner = doctype._owner; 2327 entity.setLocation(getSystemId(), getFilename(), getLine(), getColumn()); 2328 2329 ch = skipWhitespace(ch); 2330 if (! isPe && XmlChar.isNameStart(ch)) { 2331 ch = _reader.parseName(_text, ch); 2332 String key = _text.toString(); 2333 if (key.equals("NDATA")) { 2334 ch = skipWhitespace(ch); 2335 ch = _reader.parseName(_text, ch); 2336 2337 String ndata = _text.toString(); 2338 2339 entity._ndata = ndata; 2340 } else 2341 throw error(L.l("expected `NDATA' at `{0}'", key)); 2342 } 2343 } 2344 2345 entity._isPe = isPe; 2346 2347 if (isPe) 2348 doctype.addParameterEntity(entity); 2349 else 2350 doctype.addEntity(entity); 2351 2352 doctype.appendChild(entity); 2353 2354 ch = skipWhitespace(ch); 2355 2356 if (ch != '>') 2357 throw error(L.l("expected `>' at {0}", badChar(ch))); 2358 } 2359 2360 private boolean isWhitespace(int ch) 2361 { 2362 return ch <= 0x20 && (ch == 0x20 || ch == 0x9 || ch == 0xa || ch == 0xd); 2363 } 2364 2365 private boolean isChar(int ch) 2366 { 2367 return (ch >= 0x20 && ch <= 0xd7ff || 2368 ch == 0x9 || 2369 ch == 0xa || 2370 ch == 0xd || 2371 ch >= 0xe000 && ch <= 0xfffd); 2372 } 2373 2374 2377 private static String hex(int value) 2378 { 2379 CharBuffer cb = CharBuffer.allocate(); 2380 2381 for (int b = 3; b >= 0; b--) { 2382 int v = (value >> (4 * b)) & 0xf; 2383 if (v < 10) 2384 cb.append((char) (v + '0')); 2385 else 2386 cb.append((char) (v - 10 + 'a')); 2387 } 2388 2389 return cb.close(); 2390 } 2391 2392 2395 public String getFilename() 2396 { 2397 return _filename; 2398 } 2399 2400 2403 public int getLine() 2404 { 2405 return _line; 2406 } 2407 2408 2411 private int getColumn() 2412 { 2413 return 0; 2414 } 2415 2416 2419 int getNodeLine() 2420 { 2421 if (_elementTop > 0) 2422 return _elementLines[_elementTop - 1]; 2423 else 2424 return 1; 2425 } 2426 2427 2430 public String getPublicId() 2431 { 2432 if (_reader != null) 2433 return _reader.getPublicId(); 2434 else 2435 return _publicId; 2436 } 2437 2438 2441 public String getSystemId() 2442 { 2443 if (_reader != null) 2444 return _reader.getSystemId(); 2445 else if (_systemId != null) 2446 return _systemId; 2447 else 2448 return _filename; 2449 } 2450 2451 public void setLine(int line) 2452 { 2453 _line = line; 2454 } 2455 2456 public int getLineNumber() { return getLine(); } 2457 public int getColumnNumber() { return getColumn(); } 2458 2459 2462 private void addText(String s) 2463 throws IOException , SAXException 2464 { 2465 int len = s.length(); 2466 2467 for (int i = 0; i < len; i++) 2468 addText(s.charAt(i)); 2469 } 2470 2471 2474 private void addText(char ch) 2475 throws IOException , SAXException 2476 { 2477 if (_textLength >= _textCapacity) { 2478 appendText(); 2479 } 2480 2481 if (_textLength > 0 && _textBuffer[_textLength - 1] == '\r') { 2482 _textBuffer[_textLength - 1] = '\n'; 2483 if (ch == '\n') 2484 return; 2485 } 2486 2487 if (_isIgnorableWhitespace && ! XmlChar.isWhitespace(ch)) 2488 _isIgnorableWhitespace = false; 2489 2490 _textBuffer[_textLength++] = ch; 2491 } 2492 2493 2496 private void appendText() 2497 throws IOException , SAXException 2498 { 2499 if (_textLength > 0) { 2500 if (_activeNode == DOC_NAME) { 2501 if (_isJspText) { 2502 _contentHandler.characters(_textBuffer, 0, _textLength); 2503 } 2504 else if (_isIgnorableWhitespace) { 2505 } 2506 else if (_strictXml) 2507 throw error(L.l("expected top element at `{0}'", 2508 new String (_textBuffer, 0, _textLength))); 2509 else { 2510 addChild(TEXT_NAME); 2511 _contentHandler.characters(_textBuffer, 0, _textLength); 2512 } 2513 } 2514 else if (_isJspText) { 2515 _contentHandler.characters(_textBuffer, 0, _textLength); 2516 } 2517 else if (_isIgnorableWhitespace) { 2518 if (_isHtml) 2519 _contentHandler.characters(_textBuffer, 0, _textLength); 2520 else 2521 _contentHandler.ignorableWhitespace(_textBuffer, 0, _textLength); 2522 } 2523 else if (_strictXml && ! _isIgnorableWhitespace && _activeNode == DOC_NAME) { 2524 } 2525 else { 2526 if (_isJspText) { 2527 } 2528 else if (_isIgnorableWhitespace) 2529 addChild(WHITESPACE_NAME); 2530 else 2531 addChild(TEXT_NAME); 2532 _contentHandler.characters(_textBuffer, 0, _textLength); 2533 } 2534 2535 _textLength = 0; 2536 _isIgnorableWhitespace = true; 2537 } 2538 } 2539 2540 private void addElement(String child, boolean isEmpty, 2541 QAttributes attributes, 2542 NamespaceMap oldNamespace) 2543 throws IOException , SAXException 2544 { 2545 _text.clear(); 2546 _text.append(child); 2547 addElement(_policy.getName(_text), isEmpty, attributes, oldNamespace); 2548 } 2549 2550 2558 private void addElement(QName child, boolean isEmpty, 2559 QAttributes attributes, NamespaceMap oldNamespace) 2560 throws IOException , SAXException 2561 { 2562 if (! _doResinInclude) { 2563 } 2564 else if (child.getName() == "include" && 2565 child.getNamespaceURI() == "http://caucho.com/ns/resin/core" || 2566 child.getName() == "resin:include") { 2567 if (! isEmpty) 2568 throw error(L.l("resin:include must be an empty tag")); 2569 2570 handleResinInclude(); 2571 return; 2572 } 2573 else if (child.getName() == "include-directory" && 2574 child.getNamespaceURI() == "http://caucho.com/ns/resin/core" || 2575 child.getName() == "resin:include-directory") { 2576 if (! isEmpty) 2577 throw error(L.l("resin:include-directory must be an empty tag")); 2578 2579 handleResinIncludeDirectory(); 2580 return; 2581 } 2582 2583 if (_activeNode == DOC_NAME && _hasTopElement && _strictXml) 2584 throw error(L.l("expected a single top-level element at `{0}'", 2585 child.getName())); 2586 2587 _hasTopElement = true; 2588 2589 String childURI = child.getNamespaceURI(); 2590 String childLocal = child.getLocalName(); 2591 2592 if (childURI == null) { 2593 childURI = ""; 2594 2595 if (_isNamespaceAware) 2596 childLocal = child.getName(); 2597 else 2598 childLocal = ""; 2599 } 2600 2601 while (true) { 2602 int action = _policy.openAction(this, _activeNode, child); 2603 2604 switch (action) { 2605 case Policy.IGNORE: 2606 return; 2607 2608 case Policy.PUSH: 2609 2612 if (_contentHandler instanceof DOMBuilder) 2613 ((DOMBuilder) _contentHandler).startElement(child, attributes); 2614 else { 2615 _contentHandler.startElement(childURI, 2616 childLocal, 2617 child.getName(), 2618 attributes); 2619 } 2620 2621 if (isEmpty) { 2622 _contentHandler.endElement(childURI, 2623 childLocal, 2624 child.getName()); 2625 2626 popNamespaces(oldNamespace); 2627 } 2628 else { 2629 if (_elementTop == _elementNames.length) { 2630 int len = _elementNames.length; 2631 QName []names = new QName[2 * len]; 2632 NamespaceMap []newNamespaces = new NamespaceMap[2 * len]; 2633 int []lines = new int[2 * len]; 2634 System.arraycopy(_elementNames, 0, names, 0, len); 2635 System.arraycopy(_elementLines, 0, lines, 0, len); 2636 System.arraycopy(_namespaces, 0, newNamespaces, 0, len); 2637 _elementNames = names; 2638 _elementLines = lines; 2639 _namespaces = newNamespaces; 2640 } 2641 _namespaces[_elementTop] = oldNamespace; 2642 _elementLines[_elementTop] = getLine(); 2643 _elementNames[_elementTop] = _activeNode; 2644 _elementTop++; 2645 _activeNode = child; 2646 _isTagStart = true; 2647 } 2648 return; 2649 2650 case Policy.PUSH_EMPTY: 2651 2654 if (_contentHandler instanceof DOMBuilder) 2655 ((DOMBuilder) _contentHandler).startElement(child, attributes); 2656 else { 2657 _contentHandler.startElement(childURI, 2658 childLocal, 2659 child.getName(), 2660 attributes); 2661 } 2662 2663 _contentHandler.endElement(childURI, 2664 childLocal, 2665 child.getName()); 2666 2667 popNamespaces(oldNamespace); 2668 return; 2669 2670 case Policy.PUSH_OPT: 2671 addElement(_policy.getOpt(), false, _nullAttributes, oldNamespace); 2672 break; 2673 2674 case Policy.PUSH_VERBATIM: 2675 if (_contentHandler instanceof DOMBuilder) 2676 ((DOMBuilder) _contentHandler).startElement(child, attributes); 2677 else 2678 _contentHandler.startElement(childURI, 2679 childLocal, 2680 child.getName(), 2681 attributes); 2682 2683 scanVerbatim(child.getName()); 2684 appendText(); 2685 _contentHandler.endElement(childURI, 2686 childLocal, 2687 child.getName()); 2688 return; 2689 2690 case Policy.POP: 2691 2694 popNode(); 2695 2696 if (_activeNode == null) 2697 return; 2698 break; 2699 2700 default: 2701 throw error(L.l("can't add `{0}' to `{1}'", 2702 child.getName(), _activeNode.getName())); 2703 } 2704 } 2705 } 2706 2707 2710 private void addChild(QName child) 2711 throws IOException , SAXException 2712 { 2713 while (_activeNode != null) { 2714 int action = _policy.openAction(this, _activeNode, child); 2715 2716 switch (action) { 2717 case Policy.IGNORE: 2718 return; 2719 2720 case Policy.PUSH: 2721 _isTagStart = true; 2722 2723 case Policy.PUSH_EMPTY: 2724 2727 2733 return; 2734 2735 case Policy.PUSH_OPT: 2736 addElement(_policy.getOpt(), false, _nullAttributes, _namespaceMap); 2737 break; 2738 2739 case Policy.PUSH_VERBATIM: 2740 scanVerbatim(child.getName()); 2741 return; 2742 2743 case Policy.POP: 2744 2747 popNode(); 2748 break; 2749 default: 2750 throw error(L.l("cannot add `{0}' to `{1}'", 2751 child.getName(), _activeNode.getName())); 2752 } 2753 } 2754 } 2755 2756 private void scanVerbatim(String name) 2757 throws IOException , SAXException 2758 { 2759 int ch = _reader.read(); 2760 2761 while (ch >= 0) { 2762 if (ch != '<') { 2763 addText((char) ch); 2764 ch = _reader.read(); 2765 } 2766 else if ((ch = _reader.read()) != '/') 2767 addText('<'); 2768 else { 2769 ch = _reader.parseName(_eltName, _reader.read()); 2770 2771 if (! _eltName.matchesIgnoreCase(name)) { 2772 addText("</"); 2773 addText(_eltName.toString()); 2774 } 2775 else if (ch != '>') { 2776 addText("</"); 2777 addText(_eltName.toString()); 2778 } 2779 else { 2780 return; 2781 } 2782 } 2783 } 2784 2785 throw error(L.l("expected </{0}> at {1}", name, 2786 badChar(ch))); 2787 } 2788 2789 private int skipWhitespace(int ch) 2790 throws IOException , SAXException 2791 { 2792 while (ch <= 0x20 && (ch == 0x20 || ch == 0x9 || ch == 0xa || ch == 0xd)) { 2793 ch = read(); 2794 } 2795 2796 return ch; 2797 } 2798 2799 2800 public void setReader(XmlReader reader) 2801 { 2802 _reader = reader; 2803 } 2804 2805 2808 private void setMacroAttr(String text) 2809 throws IOException , SAXException 2810 { 2811 if (_reader != _macro) { 2812 _macro.init(this, _reader); 2813 _reader = _macro; 2814 } 2815 2816 int j = _macroIndex; 2817 for (int i = 0; i < text.length(); i++) { 2818 int ch = text.charAt(i); 2819 2820 if (ch == '\'') 2821 _macro.add("'"); 2822 else if (ch == '"') 2823 _macro.add("""); 2824 else 2825 _macro.add((char) ch); 2826 } 2827 } 2828 2829 private void pushInclude(String systemId) 2830 throws IOException , SAXException 2831 { 2832 pushInclude(null, systemId); 2833 } 2834 2839 private void pushInclude(String publicId, String systemId) 2840 throws IOException , SAXException 2841 { 2842 InputStream stream = openStream(systemId, publicId); 2843 if (stream == null) 2844 throw new FileNotFoundException (systemId); 2845 _is = Vfs.openRead(stream); 2846 Path oldSearchPath = _searchPath; 2847 Path path = _is.getPath(); 2848 if (path != null) { 2849 _owner.addDepend(path); 2850 2851 if (_searchPath != null) { 2852 _searchPath = path.getParent(); 2853 _reader.setSearchPath(oldSearchPath); 2854 } 2855 } 2856 2857 _filename = systemId; 2858 2868 2869 XmlReader oldReader = _reader; 2870 _reader = null; 2871 2872 _line = 1; 2873 2874 int ch = parseXMLDeclaration(oldReader); 2875 2876 XmlReader reader = _reader; 2877 2878 if (reader instanceof MacroReader) 2879 reader = reader.getNext(); 2880 2881 reader.setSystemId(systemId); 2882 reader.setFilename(systemId); 2883 reader.setPublicId(publicId); 2884 reader.setNext(oldReader); 2885 2886 unread(ch); 2887 } 2888 2889 private void popInclude() 2890 throws IOException , SAXException 2891 { 2892 XmlReader oldReader = _reader; 2893 _reader = _reader.getNext(); 2894 oldReader.setNext(null); 2895 _filename = _reader.getFilename(); 2896 _line = _reader.getLine(); 2897 _is = _reader.getReadStream(); 2898 if (_reader.getSearchPath() != null) 2899 _searchPath = _reader.getSearchPath(); 2900 } 2901 2902 private void setMacro(String text) 2903 throws IOException , SAXException 2904 { 2905 if (_reader == _macro) { 2906 } 2907 else if (_macro.getNext() == null) { 2908 _macro.init(this, _reader); 2909 _reader = _macro; 2910 } 2911 else { 2912 _macro = new MacroReader(); 2913 _macro.init(this, _reader); 2914 _reader = _macro; 2915 } 2916 2917 _macro.add(text); 2918 } 2919 2920 private int read() 2921 throws IOException , SAXException 2922 { 2923 int ch = _reader.read(); 2924 while (ch < 0 && _reader.getNext() != null) { 2925 if (_stopOnIncludeEnd) 2926 return -1; 2927 2928 popInclude(); 2929 ch = _reader.read(); 2930 } 2931 2932 return ch; 2933 } 2934 2935 2936 public void unread(int ch) 2937 { 2938 if (ch < 0) { 2939 return; 2940 } 2941 else if (_reader == _macro) { 2942 } 2943 else if (_macro.getNext() == null) { 2944 _macro.init(this, _reader); 2945 _reader = _macro; 2946 } 2947 else { 2948 _macro = new MacroReader(); 2949 _macro.init(this, _reader); 2950 _reader = _macro; 2951 } 2952 2953 _macro.prepend((char) ch); 2954 } 2955 2956 2961 XmlParseException error(String text) 2962 { 2963 if (_errorHandler != null) { 2964 SAXParseException e = new SAXParseException (text, _locator); 2965 2966 try { 2967 _errorHandler.fatalError(e); 2968 } catch (SAXException e1) { 2969 } 2970 } 2971 2972 return new XmlParseException(_filename + ":" + _line + ": " + text); 2973 } 2974 2975 public void free() 2976 { 2977 } 2978 2979 2982 static String badChar(int ch) 2983 { 2984 if (ch < 0 || ch == 0xffff) 2985 return L.l("end of file"); 2986 else if (ch == '\n' || ch == '\r') 2987 return L.l("end of line"); 2988 else if (ch >= 0x20 && ch <= 0x7f) 2989 return "`" + (char) ch + "'"; 2990 else 2991 return "`" + (char) ch + "' (\\u" + hex(ch) + ")"; 2992 } 2993 2994 private void printDebugNode(WriteStream s, Node node, int depth) 2995 throws IOException 2996 { 2997 if (node == null) 2998 return; 2999 3000 for (int i = 0; i < depth; i++) 3001 s.print(' '); 3002 3003 if (node.getFirstChild() != null) { 3004 s.println("<" + node.getNodeName() + ">"); 3005 for (Node child = node.getFirstChild(); 3006 child != null; 3007 child = child.getNextSibling()) { 3008 printDebugNode(s, child, depth + 2); 3009 } 3010 for (int i = 0; i < depth; i++) 3011 s.print(' '); 3012 s.println("</" + node.getNodeName() + ">"); 3013 } 3014 else 3015 s.println("<" + node.getNodeName() + "/>"); 3016 } 3017 3018 public static class LocatorImpl implements ExtendedLocator { 3019 XmlParser _parser; 3020 3021 LocatorImpl(XmlParser parser) 3022 { 3023 _parser = parser; 3024 } 3025 3026 public String getSystemId() 3027 { 3028 if (_parser._reader != null && _parser._reader.getSystemId() != null) 3029 return _parser._reader.getSystemId(); 3030 else if (_parser.getSystemId() != null) 3031 return _parser.getSystemId(); 3032 else if (_parser._reader != null && _parser._reader.getFilename() != null) 3033 return _parser._reader.getFilename(); 3034 else if (_parser.getFilename() != null) 3035 return _parser.getFilename(); 3036 else 3037 return null; 3038 } 3039 3040 public String getFilename() 3041 { 3042 if (_parser._reader != null && _parser._reader.getFilename() != null) 3043 return _parser._reader.getFilename(); 3044 else if (_parser.getFilename() != null) 3045 return _parser.getFilename(); 3046 else if (_parser._reader != null && _parser._reader.getSystemId() != null) 3047 return _parser._reader.getSystemId(); 3048 else if (_parser.getSystemId() != null) 3049 return _parser.getSystemId(); 3050 else 3051 return null; 3052 } 3053 3054 public String getPublicId() 3055 { 3056 if (_parser._reader != null) 3057 return _parser._reader.getPublicId(); 3058 else 3059 return _parser.getPublicId(); 3060 } 3061 3062 public int getLineNumber() 3063 { 3064 if (_parser._reader != null) 3065 return _parser._reader.getLine(); 3066 else 3067 return _parser.getLineNumber(); 3068 } 3069 3070 public int getColumnNumber() 3071 { 3072 return _parser.getColumnNumber(); 3073 } 3074 } 3075} 3076 | Popular Tags |