1 29 30 package com.caucho.xml2; 31 32 import com.caucho.util.*; 33 import com.caucho.vfs.Path; 34 import com.caucho.vfs.ReadStream; 35 import com.caucho.vfs.ReaderWriterStream; 36 import com.caucho.vfs.Vfs; 37 import com.caucho.vfs.WriteStream; 38 import com.caucho.xml2.readers.MacroReader; 39 import com.caucho.xml2.readers.Utf16Reader; 40 import com.caucho.xml2.readers.Utf8Reader; 41 import com.caucho.xml2.readers.XmlReader; 42 43 import org.w3c.dom.Document ; 44 import org.w3c.dom.Node ; 45 import org.xml.sax.InputSource ; 46 import org.xml.sax.Locator ; 47 import org.xml.sax.SAXException ; 48 import org.xml.sax.SAXParseException ; 49 50 import javax.xml.namespace.QName ; 51 import java.io.FileNotFoundException ; 52 import java.io.IOException ; 53 import java.io.InputStream ; 54 import java.util.*; 55 import java.util.logging.Level ; 56 57 63 public class DtdParser 64 { 65 private static final L10N L = new L10N(DtdParser.class); 66 67 static HashMap<String ,String > _attrTypes = new HashMap<String ,String >(); 68 static Entities _entities = new XmlEntities(); 69 70 private XmlParser _xmlParser; 71 72 73 QAttributes _attributes; 74 QAttributes _nullAttributes; 75 76 boolean _inDtd; 77 boolean _strictComments = true; 78 79 CharBuffer _text; 80 CharBuffer _eltName; 81 CharBuffer _cb; 82 CharBuffer _buf = new CharBuffer(); 83 String _textFilename; 84 int _textLine; 85 86 char []_textBuffer = new char[1024]; 87 int _textLength; 88 int _textCapacity = _textBuffer.length; 89 boolean _isIgnorableWhitespace; 90 boolean _isJspText; 91 92 CharBuffer _name = new CharBuffer(); 93 CharBuffer _nameBuffer = new CharBuffer(); 94 95 MacroReader _macro = new MacroReader(); 96 int _macroIndex = 0; 97 int _macroLength = 0; 98 char []_macroBuffer; 99 100 QName []_elementNames = new QName [64]; 101 NamespaceMap []_namespaces = new NamespaceMap[64]; 102 int []_elementLines = new int[64]; 103 int _elementTop; 104 105 NamespaceMap _namespaceMap; 106 107 ArrayList<String > _attrNames = new ArrayList<String >(); 108 ArrayList<String > _attrValues = new ArrayList<String >(); 109 110 ReadStream _is; 111 XmlReader _reader; 112 113 String _extPublicId; 114 String _extSystemId; 115 116 QName _activeNode; 117 QName _topNamespaceNode; 118 boolean _isTagStart; 119 boolean _stopOnIncludeEnd; 120 boolean _hasTopElement; 121 boolean _hasDoctype; 122 QDocumentType _dtd; 123 124 public DtdParser(XmlParser xmlParser, QDocumentType dtd) 125 { 126 _xmlParser = xmlParser; 127 _dtd = dtd; 128 } 129 130 131 146 int parseDoctypeDecl(QDocumentType doctype) 147 throws IOException , SAXException 148 { 149 _hasDoctype = true; 150 int ch = 0; 151 152 for (ch = _xmlParser.skipWhitespace(read()); 153 ch >= 0 && ch != ']'; 154 ch = _xmlParser.skipWhitespace(read())) { 155 if (ch == '<') { 156 if ((ch = read()) == '!') { 157 if (XmlChar.isNameStart(ch = read())) { 158 ch = _xmlParser.parseName(_text, ch); 159 String name = _text.toString(); 160 161 if (name.equals("ELEMENT")) 162 parseElementDecl(doctype); 163 else if (name.equals("ATTLIST")) 164 parseAttlistDecl(doctype); 165 else if (name.equals("NOTATION")) 166 parseNotationDecl(doctype); 167 else if (name.equals("ENTITY")) 168 parseEntityDecl(doctype); 169 else 170 throw error("unknown declaration '" + name + "'"); 171 } 172 else if (ch == '-') 173 parseComment(); 174 else if (ch == '[') { 175 ch = _xmlParser.parseName(_text, read()); 176 String name = _text.toString(); 177 178 if (name.equals("IGNORE")) { 179 parseIgnore(); 180 } 181 else if (name.equals("INCLUDE")) { 182 parseIgnore(); 183 } 184 else 185 throw error(L.l("unknown declaration '{0}'", name)); 186 } 187 } 188 else if (ch == '?') { 189 parsePI(); 190 } 191 else 192 throw error(L.l("expected markup at {0}", badChar(ch))); 193 } 194 else if (ch == '%') { 195 ch = _xmlParser.parseName(_buf, read()); 196 197 if (ch != ';') 198 throw error(L.l("'%{0};' expects ';' at {1}. Parameter entities have a '%name;' syntax.", _buf, badChar(ch))); 199 200 addPEReference(_text, _buf.toString()); 201 } 202 else { 203 throw error(L.l("expected '<' at {0}", badChar(ch))); 204 } 205 206 _text.clear(); 207 } 208 _text.clear(); 209 210 return read(); 211 } 212 213 private int parseNameToken(CharBuffer name, int ch) 214 throws IOException , SAXException 215 { 216 name.clear(); 217 218 if (! XmlChar.isNameChar(ch)) 219 throw error(L.l("expected name at {0}", badChar(ch))); 220 221 for (; XmlChar.isNameChar(ch); ch = read()) 222 name.append((char) ch); 223 224 return ch; 225 } 226 227 private void appendText(String s) 228 { 229 if (_text.length() == 0) { 230 _textFilename = getFilename(); 231 _textLine = getLine(); 232 } 233 234 _text.append(s); 235 } 236 237 private int parseCharacterReference() 238 throws IOException , SAXException 239 { 240 int ch = read(); 241 242 int radix = 10; 243 if (ch == 'x') { 244 radix = 16; 245 ch = read(); 246 } 247 248 int value = 0; 249 for (; ch != ';'; ch = read()) { 250 if (ch >= '0' && ch <= '9') 251 value = radix * value + ch - '0'; 252 else if (radix == 16 && ch >= 'a' && ch <= 'f') 253 value = radix * value + ch - 'a' + 10; 254 else if (radix == 16 && ch >= 'A' && ch <= 'F') 255 value = radix * value + ch - 'A' + 10; 256 else 257 throw error(L.l("malformed entity ref at {0}", badChar(ch))); 258 } 259 260 if (value > 0xffff) 261 throw error(L.l("malformed entity ref at {0}", "" + value)); 262 263 if (! isChar(value)) 265 throw error(L.l("illegal character ref at {0}", badChar(value))); 266 267 return value; 268 } 269 270 285 private int parseValue(CharBuffer value, int ch, boolean isGeneral) 286 throws IOException , SAXException 287 { 288 int end = ch; 289 290 value.clear(); 291 292 if (end == '\'' || end == '"') 293 ch = read(); 294 else { 295 value.append((char) end); 296 for (ch = read(); 297 ch >= 0 && XmlChar.isNameChar(ch); 298 ch = read()) 299 value.append((char) ch); 300 301 throw error(L.l("XML attribute value must be quoted at '{0}'. XML attribute syntax is either attr=\"value\" or attr='value'.", 302 value)); 303 } 304 305 while (ch != -1 && (end != 0 && ch != end 306 || end == 0 && isAttributeChar(ch))) { 307 if (end == 0 && ch == '/') { 308 ch = read(); 309 if (! isWhitespace(ch) && ch != '>') { 310 value.append('/'); 311 value.append((char) ch); 312 } 313 else { 314 unread(ch); 315 return '/'; 316 } 317 } 318 else if (ch == '&') { 319 if ((ch = read()) == '#') 320 value.append((char) parseCharacterReference()); 321 else if (! isGeneral) { 322 value.append('&'); 323 value.append((char) ch); 324 } 325 else if (XmlChar.isNameStart(ch)) { 326 ch = _xmlParser.parseName(_buf, ch); 327 String name = _buf.toString(); 328 329 if (ch != ';') 330 throw error(L.l("expected '{0}' at {1}", ";", badChar(ch))); 331 else { 332 int lookup = _entities.getEntity(name); 333 334 if (lookup >= 0 && lookup <= 0xffff) { 335 ch = read(); 336 value.append((char) lookup); 337 continue; 338 } 339 340 QEntity entity = _dtd == null ? null : _dtd.getEntity(name); 341 if (entity != null && entity._value != null) 342 _xmlParser.setMacroAttr(entity._value); 343 else 344 throw error(L.l("expected local reference at '&{0};'", name)); 345 } 346 } 347 } 348 else if (ch == '%' && ! isGeneral) { 349 ch = read(); 350 351 if (! XmlChar.isNameStart(ch)) { 352 value.append('%'); 353 continue; 354 } 355 else { 356 ch = _xmlParser.parseName(_buf, ch); 357 358 if (ch != ';') 359 throw error(L.l("expected '{0}' at {1}", ";", badChar(ch))); 360 else 361 addPEReference(value, _buf.toString()); 362 } 363 } 364 else if (isGeneral) { 365 if (ch == '\r') { 366 ch = read(); 367 if (ch != '\n') { 368 value.append('\n'); 369 continue; 370 } 371 } 372 value.append((char) ch); 373 } 374 else if (ch == '\r') { 375 value.append(' '); 376 377 if ((ch = read()) != '\n') 378 continue; 379 } 380 else if (ch == '\n') 381 value.append(' '); 382 else 383 value.append((char) ch); 384 385 ch = read(); 386 } 387 388 if (end != 0) 389 ch = read(); 390 391 return ch; 392 } 393 394 private boolean isAttributeChar(int ch) 395 { 396 switch (ch) { 397 case ' ': case '\t': case '\n': case '\r': 398 return false; 399 case '<': case '>': case '\'':case '"': case '=': 400 return false; 401 default: 402 return true; 403 } 404 } 405 406 private int parsePI() 407 throws IOException , SAXException 408 { 409 int ch; 410 411 ch = read(); 412 if (! XmlChar.isNameStart(ch)) 413 throw error(L.l("expected name after '<?' at {0}. Processing instructions expect a name like <?foo ... ?>", badChar(ch))); 414 ch = _xmlParser.parseName(_text, ch); 415 416 String piName = _text.toString(); 417 if (! piName.equals("xml")) 418 return parsePITail(piName, ch); 419 else { 420 throw error(L.l("<?xml ... ?> occurs after content. The <?xml ... ?> prolog must be at the document start.")); 421 422 } 423 } 424 425 private int parsePITail(String piName, int ch) 426 throws IOException , SAXException 427 { 428 ch = _xmlParser.skipWhitespace(ch); 429 430 _text.clear(); 431 while (ch != -1) { 432 if (ch == '?') { 433 if ((ch = read()) == '>') 434 break; 435 else 436 _text.append('?'); 437 } else { 438 _text.append((char) ch); 439 ch = read(); 440 } 441 } 442 443 QProcessingInstruction pi; 444 pi = new QProcessingInstruction(piName, _text.toString()); 445 pi._owner = _dtd._owner; 446 _dtd.appendChild(pi); 447 448 return read(); 449 } 450 451 454 private void parseComment() 455 throws IOException , SAXException 456 { 457 int ch = read(); 458 459 if (ch != '-') 460 throw error(L.l("expected comment at {0}", badChar(ch))); 461 462 ch = read(); 463 464 comment: 465 while (ch != -1) { 466 if (ch == '-') { 467 ch = read(); 468 469 while (ch == '-') { 470 if ((ch = read()) == '>') 471 break comment; 472 else if (_strictComments) 473 throw error(L.l("XML forbids '--' in comments")); 474 else if (ch == '-') { 475 } 476 else { 477 break; 478 } 479 } 480 } else if (! XmlChar.isChar(ch)) { 481 throw error(L.l("bad character {0}", hex(ch))); 482 } else { 483 ch = read(); 484 } 485 } 486 487 QComment comment = new QComment(_buf.toString()); 488 comment._owner = _dtd._owner; 489 _dtd.appendChild(comment); 490 } 491 492 495 private void parseIgnore() 496 throws IOException , SAXException 497 { 498 int ch = read(); 499 500 while (ch >= 0) { 501 if (ch != ']') { 502 ch = read(); 503 } 504 else if ((ch = read()) != ']') { 505 } 506 else if ((ch = read()) == '>') 507 return; 508 } 509 } 510 511 private int parseContentSpec(QElementDef def, int ch) 512 throws IOException , SAXException 513 { 514 ch = expandPE(ch); 515 516 if (XmlChar.isNameStart(ch)) { 517 ch = _xmlParser.parseName(_text, ch); 518 String name = _text.toString(); 519 520 if (name.equals("EMPTY")) { 521 def._content = "EMPTY"; 522 return ch; 523 } 524 else if (name.equals("ANY")) { 525 def._content = "ANY"; 526 return ch; 527 } 528 else 529 throw error(L.l("expected EMPTY or ANY at '{0}'", name)); 530 } 531 else if (ch != '(') { 532 throw error(L.l("expected grammar definition starting with '(' at {0}. <!ELEMENT> definitions have the syntax <!ELEMENT name - - (grammar)>", badChar(ch))); 533 } 534 else { 535 QContentParticle cp = new QContentParticle(); 536 def._content = cp; 537 538 return parseContentParticle(cp, true); 539 } 540 } 541 542 546 private int parseContentParticle(QContentParticle cp, boolean isTop) 547 throws IOException , SAXException 548 { 549 boolean hasCdata = false; 550 cp._separator = 0; 551 cp._repeat = 0; 552 int ch; 553 554 ch = expandPE(read()); 555 556 for (; ch != -1; ch = expandPE(ch)) { 557 if (ch == '(') { 558 QContentParticle child = new QContentParticle(); 559 cp.addChild(child); 560 561 ch = parseContentParticle(child, false); 562 } 563 else if (XmlChar.isNameStart(ch)) { 564 ch = _xmlParser.parseName(_text, ch); 565 cp.addChild(_text.toString()); 566 } 567 else if (ch == '#') { 568 ch = _xmlParser.parseName(_text, read()); 569 String name = _text.toString(); 570 571 if (cp._children.size() != 0) 572 throw error(L.l("'#{0}' must occur first", name)); 573 if (! isTop) 574 throw error(L.l("'#{0}' may only occur at top level", name)); 575 576 if (name.equals("PCDATA")) 577 cp.addChild("#PCDATA"); 578 else 579 throw error(L.l("illegal content particle at '#{0}'", name)); 580 581 hasCdata = true; 582 } 583 else 584 throw error(L.l("expected content particle at {0}", badChar(ch))); 585 586 ch = expandPE(ch); 587 588 if (ch == '?' || ch == '*' || ch == '+') { 589 Object child = cp.getChild(cp.getChildSize() - 1); 590 if (child instanceof QContentParticle) { 591 QContentParticle cpChild = (QContentParticle) child; 592 cpChild._repeat = ch; 593 } 594 else { 595 QContentParticle cpChild = new QContentParticle(); 596 cpChild.addChild(child); 597 cpChild._repeat = ch; 598 cp.setChild(cp.getChildSize() - 1, cpChild); 599 } 600 601 ch = expandPE(read()); 602 } 603 604 if (ch == ')') 605 break; 606 else if (cp._separator == 0) { 607 if (ch == '|') 608 cp._separator = ch; 609 else if (hasCdata) 610 throw error(L.l("#PCDATA must be separated by '|' at {0}", 611 badChar(ch))); 612 else if (ch == ',') 613 cp._separator = ch; 614 else 615 throw error(L.l("expected separator at {0}", badChar(ch))); 616 617 ch = read(); 618 } else if (ch != cp._separator) 619 throw error(L.l("expected '{0}' at {1}", 620 "" + (char) cp._separator, badChar(ch))); 621 else 622 ch = read(); 623 } 624 625 ch = expandPE(read()); 626 627 if (hasCdata && (ch == '+' || ch == '?')) 628 throw error(L.l("pcdata clause can not have {0}", badChar(ch))); 629 else if (ch == '*' || ch == '+' || ch == '?') { 630 cp._repeat = ch; 631 return read(); 632 } 633 else 634 return ch; 635 } 636 637 private int expandPE(int ch) 638 throws IOException , SAXException 639 { 640 ch = _xmlParser.skipWhitespace(ch); 641 642 while (ch == '%') { 643 parsePEReference(); 644 ch = _xmlParser.skipWhitespace(read()); 645 } 646 647 return ch; 648 } 649 650 654 private void parsePEReference() 655 throws IOException , SAXException 656 { 657 int ch = _xmlParser.parseName(_buf, read()); 658 659 if (ch != ';') 660 throw error(L.l("'%{0};' expects ';' at {1}. Parameter entities have a '%name;' syntax.", _buf, badChar(ch))); 661 662 addPEReference(_text, _buf.toString()); 663 } 664 665 668 private void addPEReference(CharBuffer value, String name) 669 throws IOException , SAXException 670 { 671 QEntity entity = _dtd.getParameterEntity(name); 672 673 if (entity == null && ! _dtd.isExternal()) 674 throw error(L.l("'%{0};' is an unknown parameter entity. Parameter entities must be defined in an <!ENTITY> declaration before use.", name)); 675 else if (entity != null && entity._value != null) { 676 _xmlParser.setMacro(entity._value); 677 } 678 else if (entity != null && entity.getSystemId() != null) { 679 _xmlParser.pushInclude(entity.getPublicId(), entity.getSystemId()); 680 } 681 else { 682 value.append("%"); 683 value.append(name); 684 value.append(";"); 685 } 686 } 687 688 691 private void parseElementDecl(QDocumentType doctype) 692 throws IOException , SAXException 693 { 694 int ch = _xmlParser.skipWhitespace(read()); 695 696 ch = _xmlParser.parseName(_text, ch); 697 String name = _text.toString(); 698 699 ch = _xmlParser.skipWhitespace(ch); 700 701 QElementDef def = _dtd.addElement(name); 702 def.setLocation(getSystemId(), getFilename(), getLine(), getColumn()); 703 704 boolean needsStartTag = true; 705 boolean needsEndTag = true; 706 707 ch = parseContentSpec(def, ch); 708 709 ch = _xmlParser.skipWhitespace(ch); 710 711 if (ch != '>') 712 throw error(L.l("'<!ELEMENT' must close with '>' at {0}", badChar(ch))); 713 } 714 715 private static String toAttrDefault(CharBuffer text) 716 { 717 for (int i = 0; i < text.length(); i++) { 718 int ch = text.charAt(i); 719 720 if (ch == '"') { 721 text.delete(i, i + 1); 722 text.insert(i, """); 723 i--; 724 } else if (ch == '\'') { 725 text.delete(i, i + 1); 726 text.insert(i, "'"); 727 i--; 728 } 729 } 730 731 return text.toString(); 732 } 733 734 737 private void parseAttlistDecl(QDocumentType doctype) 738 throws IOException , SAXException 739 { 740 int ch = _xmlParser.skipWhitespace(read()); 741 742 ch = _xmlParser.parseName(_text, ch); 743 String name = _text.toString(); 744 745 ch = _xmlParser.skipWhitespace(ch); 746 747 QElementDef def = _dtd.addElement(name); 748 749 while (XmlChar.isNameStart((ch = expandPE(ch)))) { 750 ch = _xmlParser.parseName(_text, ch); 751 String attrName = _text.toString(); 752 753 String attrType = null; 754 ArrayList<String > enumeration = null; 755 ch = expandPE(ch); 756 if (ch == '(') { 757 attrType = "#ENUM"; 758 enumeration = new ArrayList<String >(); 759 do { 760 ch = expandPE(read()); 761 762 ch = parseNameToken(_text, ch); 763 enumeration.add(_text.toString()); 764 765 ch = expandPE(ch); 766 } while (ch == '|'); 767 768 if (ch != ')') 769 throw error(L.l("expected '{0}' at {1}. <!ATTRLIST> enumerations definitions are enclosed in '(' ... ')'.", ")", badChar(ch))); 770 ch = read(); 771 } 772 else { 773 ch = _xmlParser.parseName(_text, ch); 774 attrType = _text.toString(); 775 776 if (attrType.equals("NOTATION")) { 777 enumeration = new ArrayList<String >(); 778 ch = expandPE(ch); 779 if (ch != '(') 780 throw error(L.l("expected '{0}' at {1}", "(", badChar(ch))); 781 782 do { 783 ch = expandPE(read()); 784 785 ch = _xmlParser.parseName(_text, ch); 786 enumeration.add(_text.toString()); 787 788 ch = expandPE(ch); 789 } while (ch == '|'); 790 791 if (ch != ')') 792 throw error(L.l("expected '{0}' at {1}", ")", badChar(ch))); 793 ch = read(); 794 } 795 else if (_attrTypes.get(attrType) != null) { 796 } 797 else 798 throw error(L.l("expected attribute type at '{0}'", attrType)); 799 } 800 801 ch = _xmlParser.skipWhitespace(ch); 802 String qualifier = null; 803 String attrDefault = null; 804 if (ch == '#') { 805 ch = _xmlParser.parseName(_text, read()); 806 qualifier = "#" + _text.toString(); 807 808 if (qualifier.equals("#IMPLIED")) { 809 } 810 else if (qualifier.equals("#REQUIRED")) { 811 } 812 else if (qualifier.equals("#FIXED")) { 813 ch = _xmlParser.skipWhitespace(ch); 814 ch = parseValue(_text, ch, false); 815 attrDefault = _text.toString(); 816 } else 817 throw error(L.l("expected attribute default at '{0}'", 818 qualifier)); 819 } 820 else if (ch != '>') { 821 ch = parseValue(_text, ch, false); 822 attrDefault = _text.toString(); 823 } 824 825 def.addAttribute(attrName, attrType, enumeration, 826 qualifier, attrDefault); 827 if (attrType != null && attrType.equals("ID")) 828 doctype.setElementId(name, attrName); 829 830 ch = _xmlParser.skipWhitespace(ch); 831 } 832 833 if (ch != '>') 834 throw error(L.l("expected '{0}' at {1}", ">", badChar(ch))); 835 } 836 837 840 private void parseNotationDecl(QDocumentType doctype) 841 throws IOException , SAXException 842 { 843 int ch = _xmlParser.skipWhitespace(read()); 844 845 ch = _xmlParser.parseName(_text, ch); 846 String name = _text.toString(); 847 848 ch = _xmlParser.skipWhitespace(ch); 849 ch = _xmlParser.parseName(_text, ch); 850 String key = _text.toString(); 851 852 ch = _xmlParser.skipWhitespace(ch); 853 ch = parseValue(_text, ch, false); 854 String id = _text.toString(); 855 856 ch = _xmlParser.skipWhitespace(ch); 857 858 QNotation notation; 859 860 if (key.equals("PUBLIC")) { 861 String systemId = null; 862 863 if (ch == '"' || ch == '\'') { 864 ch = parseValue(_text, ch, false); 865 ch = _xmlParser.skipWhitespace(ch); 866 systemId = _text.toString(); 867 } 868 869 notation = new QNotation(name, id, systemId); 870 notation._owner = doctype._owner; 871 notation.setLocation(getSystemId(), getFilename(), getLine(), getColumn()); 872 } 873 else if (key.equals("SYSTEM")) { 874 notation = new QNotation(name, null, id); 875 notation._owner = doctype._owner; 876 notation.setLocation(getSystemId(), getFilename(), getLine(), getColumn()); 877 } 878 else 879 throw error(L.l("expected PUBLIC or SYSTEM at '{0}'", key)); 880 881 doctype.addNotation(notation); 882 doctype.appendChild(notation); 883 884 if (ch != '>') 885 throw error(L.l("expected '{0}' at {1}", ">", badChar(ch))); 886 } 887 888 892 private int parseExternalID(int ch) 893 throws IOException , SAXException 894 { 895 ch = _xmlParser.parseName(_text, ch); 896 String key = _text.toString(); 897 ch = _xmlParser.skipWhitespace(ch); 898 899 _extSystemId = null; 900 _extPublicId = null; 901 if (key.equals("PUBLIC")) { 902 ch = parseValue(_text, ch, false); 903 _extPublicId = _text.toString(); 904 ch = _xmlParser.skipWhitespace(ch); 905 906 if (_extPublicId.indexOf('&') > 0) 907 throw error(L.l("Illegal character '&' in PUBLIC identifier '{0}'", 908 _extPublicId)); 909 910 ch = parseValue(_text, ch, false); 911 ch = _xmlParser.skipWhitespace(ch); 912 _extSystemId = _text.toString(); 913 } 914 else if (key.equals("SYSTEM")) { 915 ch = parseValue(_text, ch, false); 916 _extSystemId = _text.toString(); 917 } 918 else 919 throw error(L.l("expected PUBLIC or SYSTEM at '{0}'", key)); 920 921 return ch; 922 } 923 924 927 private void parseEntityDecl(QDocumentType doctype) 928 throws IOException , SAXException 929 { 930 int ch = _xmlParser.skipWhitespace(read()); 931 932 boolean isPe = ch == '%'; 933 934 if (isPe) 935 ch = _xmlParser.skipWhitespace(read()); 936 937 ch = _xmlParser.parseName(_text, ch); 938 String name = _text.toString(); 939 940 ch = _xmlParser.skipWhitespace(ch); 941 942 QEntity entity; 943 if (ch == '"' || ch == '\'') { 944 ch = parseValue(_text, ch, false); 945 946 entity = new QEntity(name, _text.toString(), null, null); 947 entity._owner = doctype._owner; 948 entity.setLocation(getSystemId(), getFilename(), getLine(), getColumn()); 949 } 950 else { 951 ch = parseExternalID(ch); 952 953 entity = new QEntity(name, null, _extPublicId, _extSystemId); 954 entity._owner = doctype._owner; 955 entity.setLocation(getSystemId(), getFilename(), getLine(), getColumn()); 956 957 ch = _xmlParser.skipWhitespace(ch); 958 if (! isPe && XmlChar.isNameStart(ch)) { 959 ch = _xmlParser.parseName(_text, ch); 960 String key = _text.toString(); 961 if (key.equals("NDATA")) { 962 ch = _xmlParser.skipWhitespace(ch); 963 ch = _xmlParser.parseName(_text, ch); 964 965 String ndata = _text.toString(); 966 967 entity._ndata = ndata; 968 } else 969 throw error(L.l("expected 'NDATA' at '{0}'", key)); 970 } 971 } 972 973 entity._isPe = isPe; 974 975 if (isPe) 976 doctype.addParameterEntity(entity); 977 else 978 doctype.addEntity(entity); 979 980 doctype.appendChild(entity); 981 982 ch = _xmlParser.skipWhitespace(ch); 983 984 if (ch != '>') 985 throw error(L.l("expected '>' at {0}", badChar(ch))); 986 } 987 988 private boolean isWhitespace(int ch) 989 { 990 return ch <= 0x20 && (ch == 0x20 || ch == 0x9 || ch == 0xa || ch == 0xd); 991 } 992 993 private boolean isChar(int ch) 994 { 995 return (ch >= 0x20 && ch <= 0xd7ff || 996 ch == 0x9 || 997 ch == 0xa || 998 ch == 0xd || 999 ch >= 0xe000 && ch <= 0xfffd); 1000 } 1001 1002 1005 private static String hex(int value) 1006 { 1007 CharBuffer cb = CharBuffer.allocate(); 1008 1009 for (int b = 3; b >= 0; b--) { 1010 int v = (value >> (4 * b)) & 0xf; 1011 if (v < 10) 1012 cb.append((char) (v + '0')); 1013 else 1014 cb.append((char) (v - 10 + 'a')); 1015 } 1016 1017 return cb.close(); 1018 } 1019 1020 private int read() 1021 throws IOException , SAXException 1022 { 1023 return _xmlParser.read(); 1024 } 1025 1026 public void unread(int ch) 1027 { 1028 _xmlParser.unread(ch); 1029 } 1030 1031 private String getSystemId() 1032 { 1033 return _xmlParser.getSystemId(); 1034 } 1035 1036 private String getFilename() 1037 { 1038 return _xmlParser.getFilename(); 1039 } 1040 1041 private XmlParseException error(String msg) 1042 { 1043 return _xmlParser.error(msg); 1044 } 1045 1046 private int getLine() 1047 { 1048 return _xmlParser.getLine(); 1049 } 1050 1051 private int getColumn() 1052 { 1053 return _xmlParser.getColumn(); 1054 } 1055 1056 private String badChar(int ch) 1057 { 1058 return _xmlParser.badChar(ch); 1059 } 1060 1061 static { 1062 _attrTypes.put("CDATA", "CDATA"); 1063 _attrTypes.put("ID", "ID"); 1064 _attrTypes.put("IDREF", "IDREF"); 1065 _attrTypes.put("IDREFS", "IDREFS"); 1066 _attrTypes.put("ENTITY", "ENTITY"); 1067 _attrTypes.put("ENTITIES", "ENTITIES"); 1068 _attrTypes.put("NMTOKEN", "NMTOKEN"); 1069 _attrTypes.put("NMTOKENS", "NMTOKENS"); 1070 } 1071} 1072 | Popular Tags |