1 /* 2 * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package org.openjdk.buildtools.dtdbuilder; 27 28 import javax.swing.text.html.parser.*; 29 import java.net.URL; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.util.Enumeration; 33 import java.util.Vector; 34 import java.util.Hashtable; 35 import java.util.BitSet; 36 import java.text.MessageFormat; 37 38 /** 39 * A parser for DTDs. This parser roughly corresponds to the 40 * rules specified in "The SGML Handbook" by Charles F. Goldfarb. 41 * The end result of parsing the stream is a DTD object. 42 * 43 * 44 * @see DTD 45 * @see DTDInputStream 46 * @author Arthur van Hoff 47 */ 48 final 49 class DTDParser implements DTDConstants { 50 DTDBuilder dtd; 51 DTDInputStream in; 52 int ch; 53 char str[] = new char[128]; 54 int strpos = 0; 55 int nerrors = 0; 56 57 /** 58 * Report an error. 59 */ 60 void error(String err, String arg1, String arg2, String arg3) { 61 nerrors++; 62 63 String msgParams[] = {arg1, arg2, arg3}; 64 65 String str = getSubstProp("dtderr." + err, msgParams); 66 if (str == null) { 67 str = err + "[" + arg1 + "," + arg2 + "," + arg3 + "]"; 68 } 69 System.err.println("line " + in.ln + ", dtd " + dtd + ": " + str); 70 } 71 void error(String err, String arg1, String arg2) { 72 error(err, arg1, arg2, "?"); 73 } 74 void error(String err, String arg1) { 75 error(err, arg1, "?", "?"); 76 } 77 void error(String err) { 78 error(err, "?", "?", "?"); 79 } 80 81 private String getSubstProp(String propName, String args[]) { 82 String prop = System.getProperty(propName); 83 84 if (prop == null) { 85 return null; 86 } 87 88 return MessageFormat.format(prop, (Object[])args); 89 } 90 91 /** 92 * Expect a character. 93 */ 94 boolean expect(int c) throws IOException { 95 if (ch != c) { 96 char str[] = {(char)c}; 97 error("expected", "'" + new String(str) + "'"); 98 return false; 99 } 100 ch = in.read(); 101 return true; 102 } 103 104 /** 105 * Add a char to the string buffer. 106 */ 107 void addString(int c) { 108 if (strpos == str.length) { 109 char newstr[] = new char[str.length * 2]; 110 System.arraycopy(str, 0, newstr, 0, str.length); 111 str = newstr; 112 } 113 str[strpos++] = (char)c; 114 } 115 116 /** 117 * Get the string which was accumulated in the buffer. 118 * Pos is the starting position of the string. 119 */ 120 String getString(int pos) { 121 char newstr[] = new char[strpos - pos]; 122 System.arraycopy(str, pos, newstr, 0, strpos - pos); 123 strpos = pos; 124 return new String(newstr); 125 } 126 127 /** 128 * Get the chars which were accumulated in the buffer. 129 * Pos is the starting position of the string. 130 */ 131 char[] getChars(int pos) { 132 char newstr[] = new char[strpos - pos]; 133 System.arraycopy(str, pos, newstr, 0, strpos - pos); 134 strpos = pos; 135 return newstr; 136 } 137 138 /** 139 * Skip spaces. [5] 297:23 140 */ 141 void skipSpace() throws IOException { 142 while (true) { 143 switch (ch) { 144 case '\n': 145 case ' ': 146 case '\t': 147 ch = in.read(); 148 break; 149 150 default: 151 return; 152 } 153 } 154 } 155 156 /** 157 * Skip tag spaces (includes comments). [65] 372:1 158 */ 159 void skipParameterSpace() throws IOException { 160 while (true) { 161 switch (ch) { 162 case '\n': 163 case ' ': 164 case '\t': 165 ch = in.read(); 166 break; 167 case '-': 168 if ((ch = in.read()) != '-') { 169 in.push(ch); 170 ch = '-'; 171 return; 172 } 173 174 in.replace++; 175 while (true) { 176 switch (ch = in.read()) { 177 case '-': 178 if ((ch = in.read()) == '-') { 179 ch = in.read(); 180 in.replace--; 181 skipParameterSpace(); 182 return; 183 } 184 break; 185 186 case -1: 187 error("eof.arg", "comment"); 188 in.replace--; 189 return; 190 } 191 } 192 default: 193 return; 194 } 195 } 196 } 197 198 /** 199 * Parse identifier. Uppercase characters are automatically 200 * folded to lowercase. Returns falsed if no identifier is found. 201 */ 202 @SuppressWarnings("fallthrough") 203 boolean parseIdentifier(boolean lower) throws IOException { 204 switch (ch) { 205 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 206 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': 207 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': 208 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': 209 case 'Y': case 'Z': 210 if (lower) { 211 ch = 'a' + (ch - 'A'); 212 } 213 /* fall through */ 214 215 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 216 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': 217 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': 218 case 's': case 't': case 'u': case 'v': case 'w': case 'x': 219 case 'y': case 'z': 220 break; 221 222 default: 223 return false; 224 } 225 226 addString(ch); 227 ch = in.read(); 228 parseNameToken(lower); 229 return true; 230 } 231 232 /** 233 * Parses name token. If <code>lower</code> is true, upper case letters 234 * are folded to lower case. Returns falsed if no token is found. 235 */ 236 @SuppressWarnings("fallthrough") 237 boolean parseNameToken(boolean lower) throws IOException { 238 boolean first = true; 239 240 while (true) { 241 switch (ch) { 242 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 243 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': 244 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': 245 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': 246 case 'Y': case 'Z': 247 if (lower) { 248 ch = 'a' + (ch - 'A'); 249 } 250 /* fall through */ 251 252 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 253 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': 254 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': 255 case 's': case 't': case 'u': case 'v': case 'w': case 'x': 256 case 'y': case 'z': 257 258 case '0': case '1': case '2': case '3': case '4': 259 case '5': case '6': case '7': case '8': case '9': 260 261 case '.': case '-': 262 addString(ch); 263 ch = in.read(); 264 first = false; 265 break; 266 267 default: 268 return !first; 269 } 270 } 271 } 272 273 /** 274 * Parse a list of identifiers. 275 */ 276 Vector<String> parseIdentifierList(boolean lower) throws IOException { 277 Vector<String> elems = new Vector<>(); 278 skipSpace(); 279 switch (ch) { 280 case '(': 281 ch = in.read(); 282 skipParameterSpace(); 283 while (parseNameToken(lower)) { 284 elems.addElement(getString(0)); 285 skipParameterSpace(); 286 if (ch == '|') { 287 ch = in.read(); 288 skipParameterSpace(); 289 } 290 } 291 expect(')'); 292 skipParameterSpace(); 293 break; 294 295 default: 296 if (!parseIdentifier(lower)) { 297 error("expected", "identifier"); 298 break; 299 } 300 elems.addElement(getString(0)); 301 skipParameterSpace(); 302 break; 303 } 304 return elems; 305 } 306 307 /** 308 * Parse and Entity reference. Should be called when 309 * a & is encountered. The data is put in the string buffer. 310 * [59] 350:17 311 */ 312 private void parseEntityReference() throws IOException { 313 int pos = strpos; 314 315 if ((ch = in.read()) == '#') { 316 int n = 0; 317 ch = in.read(); 318 if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))) { 319 addString('#'); 320 } else { 321 while ((ch >= '0') && (ch <= '9')) { 322 n = (n * 10) + ch - '0'; 323 ch = in.read(); 324 } 325 if ((ch == ';') || (ch == '\n')) { 326 ch = in.read(); 327 } 328 addString(n); 329 return; 330 } 331 } 332 333 while (true) { 334 switch (ch) { 335 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 336 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': 337 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': 338 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': 339 case 'Y': case 'Z': 340 341 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 342 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': 343 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': 344 case 's': case 't': case 'u': case 'v': case 'w': case 'x': 345 case 'y': case 'z': 346 347 case '0': case '1': case '2': case '3': case '4': 348 case '5': case '6': case '7': case '8': case '9': 349 350 case '.': case '-': 351 addString(ch); 352 ch = in.read(); 353 break; 354 355 default: 356 if (strpos == pos) { 357 addString('&'); 358 return; 359 } 360 String nm = getString(pos); 361 Entity ent = dtd.getEntity(nm); 362 if (ent == null) { 363 error("undef.entref" + nm); 364 return; 365 } 366 if ((ch == ';') || (ch == '\n')) { 367 ch = in.read(); 368 } 369 char data[] = ent.getData(); 370 for (int i = 0 ; i < data.length ; i++) { 371 addString(data[i]); 372 } 373 return; 374 } 375 } 376 } 377 378 /** 379 * Parse an entity declaration. 380 * [101] 394:18 381 * REMIND: external entity type 382 */ 383 private void parseEntityDeclaration() throws IOException { 384 int type = GENERAL; 385 386 skipSpace(); 387 if (ch == '%') { 388 ch = in.read(); 389 type = PARAMETER; 390 skipSpace(); 391 } 392 if (ch == '#') { 393 addString('#'); 394 ch = in.read(); 395 } 396 if (!parseIdentifier(false)) { 397 error("expected", "identifier"); 398 return; 399 } 400 String nm = getString(0); 401 skipParameterSpace(); 402 if (parseIdentifier(false)) { 403 String tnm = getString(0); 404 int t = Entity.name2type(tnm); 405 if (t == 0) { 406 error("invalid.arg", "entity type", tnm); 407 } else { 408 type |= t; 409 } 410 skipParameterSpace(); 411 } 412 413 if ((ch != '"') && (ch != '\'')) { 414 error("expected", "entity value"); 415 skipParameterSpace(); 416 if (ch == '>') { 417 ch = in.read(); 418 } 419 return; 420 } 421 422 int term = ch; 423 ch = in.read(); 424 while ((ch != -1) && (ch != term)) { 425 if (ch == '&') { 426 parseEntityReference(); 427 } else { 428 addString(ch & 0xFF); 429 ch = in.read(); 430 } 431 } 432 if (ch == term) { 433 ch = in.read(); 434 } 435 if (in.replace == 0) { 436 char data[] = getChars(0); 437 dtd.defineEntity(nm, type, data); 438 } else { 439 strpos = 0; 440 } 441 skipParameterSpace(); 442 expect('>'); 443 } 444 445 /** 446 * Parse content model. 447 * [126] 410:1 448 * REMIND: data tag group 449 */ 450 ContentModel parseContentModel() throws IOException { 451 ContentModel m = null; 452 453 switch (ch) { 454 case '(': 455 ch = in.read(); 456 skipParameterSpace(); 457 ContentModel e = parseContentModel(); 458 459 if (ch != ')') { 460 m = new ContentModel(ch, e); 461 do { 462 ch = in.read(); 463 skipParameterSpace(); 464 e.next = parseContentModel(); 465 if (e.next.type == m.type) { 466 e.next = (ContentModel)e.next.content; 467 } 468 for (; e.next != null ; e = e.next); 469 } while (ch == m.type); 470 } else { 471 m = new ContentModel(',', e); 472 } 473 expect(')'); 474 break; 475 476 case '#': 477 ch = in.read(); 478 if (parseIdentifier(true)) { 479 m = new ContentModel('*', new ContentModel(dtd.getElement("#" + getString(0)))); 480 } else { 481 error("invalid", "content model"); 482 } 483 break; 484 485 default: 486 if (parseIdentifier(true)) { 487 m = new ContentModel(dtd.getElement(getString(0))); 488 } else { 489 error("invalid", "content model"); 490 } 491 break; 492 } 493 494 switch (ch) { 495 case '?': 496 case '*': 497 case '+': 498 m = new ContentModel(ch, m); 499 ch = in.read(); 500 break; 501 } 502 skipParameterSpace(); 503 504 return m; 505 } 506 507 /** 508 * Parse element declaration. 509 * [116] 405:6 510 */ 511 void parseElementDeclaration() throws IOException { 512 Vector<String> elems = parseIdentifierList(true); 513 BitSet inclusions = null; 514 BitSet exclusions = null; 515 boolean omitStart = false; 516 boolean omitEnd = false; 517 518 if ((ch == '-') || (ch == 'O')) { 519 omitStart = ch == 'O'; 520 ch = in.read(); 521 skipParameterSpace(); 522 523 if ((ch == '-') || (ch == 'O')) { 524 omitEnd = ch == 'O'; 525 ch = in.read(); 526 skipParameterSpace(); 527 } else { 528 expect('-'); 529 } 530 } 531 532 int type = MODEL; 533 ContentModel content = null; 534 if (parseIdentifier(false)) { 535 String nm = getString(0); 536 type = Element.name2type(nm); 537 if (type == 0) { 538 error("invalid.arg", "content type", nm); 539 type = EMPTY; 540 } 541 skipParameterSpace(); 542 } else { 543 content = parseContentModel(); 544 } 545 546 if ((type == MODEL) || (type == ANY)) { 547 if (ch == '-') { 548 ch = in.read(); 549 Vector<String> v = parseIdentifierList(true); 550 exclusions = new BitSet(); 551 for (Enumeration<String> e = v.elements() ; e.hasMoreElements() ;) { 552 exclusions.set(dtd.getElement(e.nextElement()).getIndex()); 553 } 554 } 555 if (ch == '+') { 556 ch = in.read(); 557 Vector<String> v = parseIdentifierList(true); 558 inclusions = new BitSet(); 559 for (Enumeration<String> e = v.elements() ; e.hasMoreElements() ;) { 560 inclusions.set(dtd.getElement(e.nextElement()).getIndex()); 561 } 562 } 563 } 564 expect('>'); 565 566 if (in.replace == 0) { 567 for (Enumeration<String> e = elems.elements() ; e.hasMoreElements() ;) { 568 dtd.defineElement(e.nextElement(), type, omitStart, omitEnd, content, exclusions, inclusions, null); 569 } 570 } 571 } 572 573 /** 574 * Parse an attribute declared value. 575 * [145] 422:6 576 */ 577 void parseAttributeDeclaredValue(AttributeList atts) throws IOException { 578 if (ch == '(') { 579 atts.values = parseIdentifierList(true); 580 atts.type = NMTOKEN; 581 return; 582 } 583 if (!parseIdentifier(false)) { 584 error("invalid", "attribute value"); 585 return; 586 } 587 atts.type = AttributeList.name2type(getString(0)); 588 skipParameterSpace(); 589 if (atts.type == NOTATION) { 590 atts.values = parseIdentifierList(true); 591 } 592 } 593 594 /** 595 * Parse an attribute value specification. 596 * [33] 331:1 597 */ 598 @SuppressWarnings("fallthrough") 599 String parseAttributeValueSpecification() throws IOException { 600 int delim = -1; 601 switch (ch) { 602 case '\'': 603 case '"': 604 delim = ch; 605 ch = in.read(); 606 } 607 while (true) { 608 switch (ch) { 609 case -1: 610 error("eof.arg", "attribute value"); 611 return getString(0); 612 613 case '&': 614 parseEntityReference(); 615 break; 616 617 case ' ': 618 case '\t': 619 case '\n': 620 if (delim == -1) { 621 return getString(0); 622 } 623 addString(' '); 624 ch = in.read(); 625 break; 626 627 case '\'': 628 case '"': 629 if (delim == ch) { 630 ch = in.read(); 631 return getString(0); 632 } 633 /* fall through */ 634 635 default: 636 addString(ch & 0xFF); 637 ch = in.read(); 638 break; 639 } 640 } 641 } 642 643 /** 644 * Parse an attribute default value. 645 * [147] 425:1 646 */ 647 void parseAttributeDefaultValue(AttributeList atts) throws IOException { 648 if (ch == '#') { 649 ch = in.read(); 650 if (!parseIdentifier(true)) { 651 error("invalid", "attribute value"); 652 return; 653 } 654 skipParameterSpace(); 655 atts.modifier = AttributeList.name2type(getString(0)); 656 if (atts.modifier != FIXED) { 657 return; 658 } 659 } 660 atts.value = parseAttributeValueSpecification(); 661 skipParameterSpace(); 662 } 663 664 /** 665 * Parse an attribute definition list declaration. 666 * [141] 420:15 667 * REMIND: associated notation name 668 */ 669 void parseAttlistDeclaration() throws IOException { 670 Vector<String> elems = parseIdentifierList(true); 671 AttributeList attlist = null, atts = null; 672 673 while (parseIdentifier(true)) { 674 if (atts == null) { 675 attlist = atts = new AttributeList(getString(0)); 676 } else { 677 atts.next = new AttributeList(getString(0)); 678 atts = atts.next; 679 } 680 skipParameterSpace(); 681 parseAttributeDeclaredValue(atts); 682 parseAttributeDefaultValue(atts); 683 684 if ((atts.modifier == IMPLIED) && (atts.values != null) && (atts.values.size() == 1)) { 685 atts.value = (String)atts.values.elementAt(0); 686 } 687 } 688 689 expect('>'); 690 691 if (in.replace == 0) { 692 for (Enumeration<String> e = elems.elements() ; e.hasMoreElements() ;) { 693 dtd.defineAttributes(e.nextElement(), attlist); 694 } 695 } 696 } 697 698 /** 699 * Parse an ignored section until ]]> is encountered. 700 */ 701 void parseIgnoredSection() throws IOException { 702 int depth = 1; 703 in.replace++; 704 while (true) { 705 switch (ch) { 706 case '<': 707 if ((ch = in.read()) == '!') { 708 if ((ch = in.read()) == '[') { 709 ch = in.read(); 710 depth++; 711 } 712 } 713 break; 714 case ']': 715 if ((ch = in.read()) == ']') { 716 if ((ch = in.read()) == '>') { 717 ch = in.read(); 718 if (--depth == 0) { 719 in.replace--; 720 return; 721 } 722 } 723 } 724 break; 725 case -1: 726 error("eof"); 727 in.replace--; 728 return; 729 730 default: 731 ch = in.read(); 732 break; 733 } 734 } 735 } 736 737 /** 738 * Parse a marked section declaration. 739 * [93] 391:13 740 * REMIND: deal with all status keywords 741 */ 742 void parseMarkedSectionDeclaration() throws IOException { 743 ch = in.read(); 744 skipSpace(); 745 if (!parseIdentifier(true)) { 746 error("expected", "section status keyword"); 747 return; 748 } 749 String str = getString(0); 750 skipSpace(); 751 expect('['); 752 if ("ignore".equals(str)) { 753 parseIgnoredSection(); 754 } else { 755 if (!"include".equals(str)) { 756 error("invalid.arg", "section status keyword", str); 757 } 758 parseSection(); 759 expect(']'); 760 expect(']'); 761 expect('>'); 762 } 763 } 764 765 /** 766 * Parse an external identifier 767 * [73] 379:1 768 */ 769 void parseExternalIdentifier() throws IOException { 770 if (parseIdentifier(false)) { 771 String id = getString(0); 772 skipParameterSpace(); 773 774 if (id.equals("PUBLIC")) { 775 if ((ch == '\'') || (ch == '"')) { 776 parseAttributeValueSpecification(); 777 } else { 778 error("expected", "public identifier"); 779 } 780 skipParameterSpace(); 781 } else if (!id.equals("SYSTEM")) { 782 error("invalid", "external identifier"); 783 } 784 if ((ch == '\'') || (ch == '"')) { 785 parseAttributeValueSpecification(); 786 } 787 skipParameterSpace(); 788 } 789 } 790 791 /** 792 * Parse document type declaration. 793 * [110] 403:1 794 */ 795 void parseDocumentTypeDeclaration() throws IOException { 796 skipParameterSpace(); 797 if (!parseIdentifier(true)) { 798 error("expected", "identifier"); 799 } else { 800 skipParameterSpace(); 801 } 802 strpos = 0; 803 parseExternalIdentifier(); 804 805 if (ch == '[') { 806 ch = in.read(); 807 parseSection(); 808 expect(']'); 809 skipParameterSpace(); 810 } 811 expect('>'); 812 } 813 814 /** 815 * Parse a section of the input upto EOF or ']'. 816 */ 817 @SuppressWarnings("fallthrough") 818 void parseSection() throws IOException { 819 while (true) { 820 switch (ch) { 821 case ']': 822 return; 823 824 case '<': 825 switch (ch = in.read()) { 826 case '!': 827 switch (ch = in.read()) { 828 case '[': 829 parseMarkedSectionDeclaration(); 830 break; 831 832 case '-': 833 skipParameterSpace(); 834 expect('>'); 835 break; 836 837 default: 838 if (parseIdentifier(true)) { 839 String str = getString(0); 840 841 if (str.equals("element")) { 842 parseElementDeclaration(); 843 844 } else if (str.equals("entity")) { 845 parseEntityDeclaration(); 846 847 } else if (str.equals("attlist")) { 848 parseAttlistDeclaration(); 849 850 } else if (str.equals("doctype")) { 851 parseDocumentTypeDeclaration(); 852 853 } else if (str.equals("usemap")) { 854 error("ignoring", "usemap"); 855 while ((ch != -1) && (ch != '>')) { 856 ch = in.read(); 857 } 858 expect('>'); 859 } else if (str.equals("shortref")) { 860 error("ignoring", "shortref"); 861 while ((ch != -1) && (ch != '>')) { 862 ch = in.read(); 863 } 864 expect('>'); 865 } else if (str.equals("notation")) { 866 error("ignoring", "notation"); 867 while ((ch != -1) && (ch != '>')) { 868 ch = in.read(); 869 } 870 expect('>'); 871 } else { 872 error("markup"); 873 } 874 } else { 875 error("markup"); 876 while ((ch != -1) && (ch != '>')) { 877 ch = in.read(); 878 } 879 expect('>'); 880 } 881 } 882 } 883 break; 884 885 case -1: 886 return; 887 888 default: 889 char str[] = {(char)ch}; 890 error("invalid.arg", "character", "'" + new String(str) + "' / " + ch); 891 /* fall through */ 892 893 case ' ': 894 case '\t': 895 case '\n': 896 ch = in.read(); 897 break; 898 } 899 } 900 } 901 902 /** 903 * Parse a DTD. 904 * @return the dtd or null if an error occurred. 905 */ 906 DTD parse(InputStream in, DTDBuilder dtd) { 907 try { 908 this.dtd = dtd; 909 this.in = new DTDInputStream(in, dtd); 910 911 ch = this.in.read(); 912 parseSection(); 913 914 if (ch != -1) { 915 error("premature"); 916 } 917 } catch (IOException e) { 918 error("ioexception"); 919 } catch (Exception e) { 920 error("exception", e.getClass().getName(), e.getMessage()); 921 e.printStackTrace(); 922 } catch (ThreadDeath e) { 923 error("terminated"); 924 } 925 return (nerrors > 0) ? null : dtd; 926 } 927 }