001 package com.thaiopensource.relaxng.parse.sax; 002 003 import com.thaiopensource.relaxng.parse.DataPatternBuilder; 004 import com.thaiopensource.relaxng.parse.Grammar; 005 import com.thaiopensource.relaxng.parse.GrammarSection; 006 import com.thaiopensource.relaxng.parse.IllegalSchemaException; 007 import com.thaiopensource.relaxng.parse.Include; 008 import com.thaiopensource.relaxng.parse.IncludedGrammar; 009 import com.thaiopensource.relaxng.parse.Location; 010 import com.thaiopensource.relaxng.parse.ParsedNameClass; 011 import com.thaiopensource.relaxng.parse.ParsedPattern; 012 import com.thaiopensource.relaxng.parse.SchemaBuilder; 013 import com.thaiopensource.relaxng.parse.Scope; 014 import com.thaiopensource.relaxng.parse.Annotations; 015 import com.thaiopensource.relaxng.parse.Context; 016 import com.thaiopensource.relaxng.parse.CommentList; 017 import com.thaiopensource.relaxng.parse.Div; 018 import com.thaiopensource.relaxng.parse.ElementAnnotationBuilder; 019 import com.thaiopensource.relaxng.parse.ParsedElementAnnotation; 020 import com.thaiopensource.relaxng.parse.ParsedPatternFuture; 021 import com.thaiopensource.util.Uri; 022 import com.thaiopensource.util.Localizer; 023 import com.thaiopensource.xml.util.Naming; 024 import com.thaiopensource.xml.util.WellKnownNamespaces; 025 import com.thaiopensource.xml.sax.XmlBaseHandler; 026 import com.thaiopensource.xml.sax.AbstractLexicalHandler; 027 import org.xml.sax.Attributes; 028 import org.xml.sax.ContentHandler; 029 import org.xml.sax.EntityResolver; 030 import org.xml.sax.ErrorHandler; 031 import org.xml.sax.Locator; 032 import org.xml.sax.SAXException; 033 import org.xml.sax.SAXParseException; 034 import org.xml.sax.XMLReader; 035 import org.xml.sax.SAXNotRecognizedException; 036 import org.xml.sax.SAXNotSupportedException; 037 import org.xml.sax.helpers.DefaultHandler; 038 039 import java.util.Hashtable; 040 import java.util.Enumeration; 041 import java.util.Vector; 042 import java.util.Stack; 043 044 class SchemaParser implements ParsedPatternFuture { 045 046 private static final String relaxngURIPrefix = 047 WellKnownNamespaces.RELAX_NG.substring(0, WellKnownNamespaces.RELAX_NG.lastIndexOf('/') + 1); 048 static final String relaxng10URI = WellKnownNamespaces.RELAX_NG; 049 private static final Localizer localizer = new Localizer(SchemaParser.class); 050 051 private String relaxngURI; 052 private final XMLReader xr; 053 private final ErrorHandler eh; 054 private final SchemaBuilder schemaBuilder; 055 private ParsedPattern startPattern; 056 private Locator locator; 057 private final XmlBaseHandler xmlBaseHandler = new XmlBaseHandler(); 058 private final ContextImpl context = new ContextImpl(); 059 060 private boolean hadError = false; 061 062 private Hashtable patternTable; 063 private Hashtable nameClassTable; 064 065 static class PrefixMapping { 066 final String prefix; 067 final String uri; 068 final PrefixMapping next; 069 070 PrefixMapping(String prefix, String uri, PrefixMapping next) { 071 this.prefix = prefix; 072 this.uri = uri; 073 this.next = next; 074 } 075 } 076 077 static abstract class AbstractContext extends DtdContext implements Context { 078 PrefixMapping prefixMapping; 079 080 AbstractContext() { 081 prefixMapping = new PrefixMapping("xml", WellKnownNamespaces.XML, null); 082 } 083 084 AbstractContext(AbstractContext context) { 085 super(context); 086 prefixMapping = context.prefixMapping; 087 } 088 089 public String resolveNamespacePrefix(String prefix) { 090 for (PrefixMapping p = prefixMapping; p != null; p = p.next) 091 if (p.prefix.equals(prefix)) 092 return p.uri; 093 return null; 094 } 095 096 public Enumeration prefixes() { 097 Vector v = new Vector(); 098 for (PrefixMapping p = prefixMapping; p != null; p = p.next) { 099 if (!v.contains(p.prefix)) 100 v.addElement(p.prefix); 101 } 102 return v.elements(); 103 } 104 105 public Context copy() { 106 return new SavedContext(this); 107 } 108 } 109 110 static class SavedContext extends AbstractContext { 111 private final String baseUri; 112 SavedContext(AbstractContext context) { 113 super(context); 114 this.baseUri = context.getBaseUri(); 115 } 116 117 public String getBaseUri() { 118 return baseUri; 119 } 120 } 121 122 class ContextImpl extends AbstractContext { 123 public String getBaseUri() { 124 return xmlBaseHandler.getBaseUri(); 125 } 126 } 127 128 static interface CommentHandler { 129 void comment(String value); 130 } 131 132 abstract class Handler implements ContentHandler, CommentHandler { 133 CommentList comments; 134 135 CommentList getComments() { 136 CommentList tem = comments; 137 comments = null; 138 return tem; 139 } 140 141 public void comment(String value) { 142 if (comments == null) 143 comments = schemaBuilder.makeCommentList(); 144 comments.addComment(value, makeLocation()); 145 } 146 public void processingInstruction(String target, String date) { } 147 public void skippedEntity(String name) { } 148 public void ignorableWhitespace(char[] ch, int start, int len) { } 149 public void startDocument() { } 150 public void endDocument() { } 151 public void startPrefixMapping(String prefix, String uri) { 152 context.prefixMapping = new PrefixMapping(prefix, uri, context.prefixMapping); 153 } 154 155 public void endPrefixMapping(String prefix) { 156 context.prefixMapping = context.prefixMapping.next; 157 } 158 159 public void setDocumentLocator(Locator loc) { 160 locator = loc; 161 xmlBaseHandler.setLocator(loc); 162 } 163 } 164 165 abstract class State extends Handler { 166 State parent; 167 String nsInherit; 168 String ns; 169 String datatypeLibrary; 170 Scope scope; 171 Location startLocation; 172 Annotations annotations; 173 174 void set() { 175 xr.setContentHandler(this); 176 } 177 178 abstract State create(); 179 abstract State createChildState(String localName) throws SAXException; 180 181 182 void setParent(State parent) { 183 this.parent = parent; 184 this.nsInherit = parent.getNs(); 185 this.datatypeLibrary = parent.datatypeLibrary; 186 this.scope = parent.scope; 187 this.startLocation = makeLocation(); 188 if (parent.comments != null) { 189 annotations = schemaBuilder.makeAnnotations(parent.comments, getContext()); 190 parent.comments = null; 191 } 192 else if (parent instanceof RootState) 193 annotations = schemaBuilder.makeAnnotations(null, getContext()); 194 } 195 196 String getNs() { 197 return ns == null ? nsInherit : ns; 198 } 199 200 boolean isRelaxNGElement(String uri) throws SAXException { 201 return uri.equals(relaxngURI); 202 } 203 204 public void startElement(String namespaceURI, 205 String localName, 206 String qName, 207 Attributes atts) throws SAXException { 208 xmlBaseHandler.startElement(); 209 if (isRelaxNGElement(namespaceURI)) { 210 State state = createChildState(localName); 211 if (state == null) { 212 xr.setContentHandler(new Skipper(this)); 213 return; 214 } 215 state.setParent(this); 216 state.set(); 217 state.attributes(atts); 218 } 219 else { 220 checkForeignElement(); 221 ForeignElementHandler feh = new ForeignElementHandler(this, getComments()); 222 feh.startElement(namespaceURI, localName, qName, atts); 223 xr.setContentHandler(feh); 224 } 225 } 226 227 public void endElement(String namespaceURI, 228 String localName, 229 String qName) throws SAXException { 230 xmlBaseHandler.endElement(); 231 parent.set(); 232 end(); 233 } 234 235 void setName(String name) throws SAXException { 236 error("illegal_name_attribute"); 237 } 238 239 void setOtherAttribute(String name, String value) throws SAXException { 240 error("illegal_attribute_ignored", name); 241 } 242 243 void endAttributes() throws SAXException { 244 } 245 246 void checkForeignElement() throws SAXException { 247 } 248 249 void attributes(Attributes atts) throws SAXException { 250 int len = atts.getLength(); 251 for (int i = 0; i < len; i++) { 252 String uri = atts.getURI(i); 253 if (uri.length() == 0) { 254 String name = atts.getLocalName(i); 255 if (name.equals("name")) 256 setName(atts.getValue(i).trim()); 257 else if (name.equals("ns")) 258 ns = atts.getValue(i); 259 else if (name.equals("datatypeLibrary")) { 260 datatypeLibrary = atts.getValue(i); 261 checkUri(datatypeLibrary); 262 if (!datatypeLibrary.equals("") 263 && !Uri.isAbsolute(datatypeLibrary)) 264 error("relative_datatype_library"); 265 if (Uri.hasFragmentId(datatypeLibrary)) 266 error("fragment_identifier_datatype_library"); 267 datatypeLibrary = Uri.escapeDisallowedChars(datatypeLibrary); 268 } 269 else 270 setOtherAttribute(name, atts.getValue(i)); 271 } 272 else if (uri.equals(relaxngURI)) 273 error("qualified_attribute", atts.getLocalName(i)); 274 else if (uri.equals(WellKnownNamespaces.XML) 275 && atts.getLocalName(i).equals("base")) 276 xmlBaseHandler.xmlBaseAttribute(atts.getValue(i)); 277 else { 278 if (annotations == null) 279 annotations = schemaBuilder.makeAnnotations(null, getContext()); 280 annotations.addAttribute(uri, atts.getLocalName(i), findPrefix(atts.getQName(i), uri), 281 atts.getValue(i), startLocation); 282 } 283 } 284 endAttributes(); 285 } 286 287 abstract void end() throws SAXException; 288 289 void endChild(ParsedPattern pattern) { 290 // XXX cannot happen; throw exception 291 } 292 293 void endChild(ParsedNameClass nc) { 294 // XXX cannot happen; throw exception 295 } 296 297 public void startDocument() { } 298 public void endDocument() { 299 if (comments != null && startPattern != null) { 300 startPattern = schemaBuilder.commentAfter(startPattern, comments); 301 comments = null; 302 } 303 } 304 305 public void characters(char[] ch, int start, int len) throws SAXException { 306 for (int i = 0; i < len; i++) { 307 switch(ch[start + i]) { 308 case ' ': 309 case '\r': 310 case '\n': 311 case '\t': 312 break; 313 default: 314 error("illegal_characters_ignored"); 315 break; 316 } 317 } 318 } 319 320 boolean isPatternNamespaceURI(String s) { 321 return s.equals(relaxngURI); 322 } 323 324 void endForeignChild(ParsedElementAnnotation ea) { 325 if (annotations == null) 326 annotations = schemaBuilder.makeAnnotations(null, getContext()); 327 annotations.addElement(ea); 328 } 329 330 void mergeLeadingComments() { 331 if (comments != null) { 332 if (annotations == null) 333 annotations = schemaBuilder.makeAnnotations(comments, getContext()); 334 else 335 annotations.addLeadingComment(comments); 336 comments = null; 337 } 338 } 339 } 340 341 class ForeignElementHandler extends Handler { 342 final State nextState; 343 ElementAnnotationBuilder builder; 344 final Stack builderStack = new Stack(); 345 StringBuffer textBuf; 346 Location textLoc; 347 348 ForeignElementHandler(State nextState, CommentList comments) { 349 this.nextState = nextState; 350 this.comments = comments; 351 } 352 353 public void startElement(String namespaceURI, String localName, 354 String qName, Attributes atts) { 355 flushText(); 356 if (builder != null) 357 builderStack.push(builder); 358 Location loc = makeLocation(); 359 builder = schemaBuilder.makeElementAnnotationBuilder(namespaceURI, 360 localName, 361 findPrefix(qName, namespaceURI), 362 loc, 363 getComments(), 364 getContext()); 365 int len = atts.getLength(); 366 for (int i = 0; i < len; i++) { 367 String uri = atts.getURI(i); 368 builder.addAttribute(uri, atts.getLocalName(i), findPrefix(atts.getQName(i), uri), 369 atts.getValue(i), loc); 370 } 371 } 372 373 public void endElement(String namespaceURI, String localName, 374 String qName) { 375 flushText(); 376 if (comments != null) 377 builder.addComment(getComments()); 378 ParsedElementAnnotation ea = builder.makeElementAnnotation(); 379 if (builderStack.empty()) { 380 nextState.endForeignChild(ea); 381 nextState.set(); 382 } 383 else { 384 builder = (ElementAnnotationBuilder)builderStack.pop(); 385 builder.addElement(ea); 386 } 387 } 388 389 public void characters(char ch[], int start, int length) { 390 if (textBuf == null) 391 textBuf = new StringBuffer(); 392 textBuf.append(ch, start, length); 393 if (textLoc == null) 394 textLoc = makeLocation(); 395 } 396 397 public void comment(String value) { 398 flushText(); 399 super.comment(value); 400 } 401 402 void flushText() { 403 if (textBuf != null && textBuf.length() != 0) { 404 builder.addText(textBuf.toString(), textLoc, getComments()); 405 textBuf.setLength(0); 406 } 407 textLoc = null; 408 } 409 } 410 411 class Skipper extends DefaultHandler implements CommentHandler { 412 int level = 1; 413 final State nextState; 414 415 Skipper(State nextState) { 416 this.nextState = nextState; 417 } 418 419 public void startElement(String namespaceURI, 420 String localName, 421 String qName, 422 Attributes atts) throws SAXException { 423 ++level; 424 } 425 426 public void endElement(String namespaceURI, 427 String localName, 428 String qName) throws SAXException { 429 if (--level == 0) 430 nextState.set(); 431 } 432 433 public void comment(String value) { 434 } 435 } 436 437 abstract class EmptyContentState extends State { 438 439 State createChildState(String localName) throws SAXException { 440 error("expected_empty", localName); 441 return null; 442 } 443 444 abstract ParsedPattern makePattern() throws SAXException; 445 446 void end() throws SAXException { 447 if (comments != null) { 448 if (annotations == null) 449 annotations = schemaBuilder.makeAnnotations(null, getContext()); 450 annotations.addComment(comments); 451 comments = null; 452 } 453 parent.endChild(makePattern()); 454 } 455 } 456 457 static private final int INIT_CHILD_ALLOC = 5; 458 459 abstract class PatternContainerState extends State { 460 ParsedPattern[] childPatterns; 461 int nChildPatterns = 0; 462 463 State createChildState(String localName) throws SAXException { 464 State state = (State)patternTable.get(localName); 465 if (state == null) { 466 error("expected_pattern", localName); 467 return null; 468 } 469 return state.create(); 470 } 471 472 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) throws SAXException { 473 if (nPatterns == 1 && anno == null) 474 return patterns[0]; 475 return schemaBuilder.makeGroup(patterns, nPatterns, loc, anno); 476 } 477 478 void endChild(ParsedPattern pattern) { 479 if (childPatterns == null) 480 childPatterns = new ParsedPattern[INIT_CHILD_ALLOC]; 481 else if (nChildPatterns >= childPatterns.length) { 482 ParsedPattern[] newChildPatterns = new ParsedPattern[childPatterns.length * 2]; 483 System.arraycopy(childPatterns, 0, newChildPatterns, 0, childPatterns.length); 484 childPatterns = newChildPatterns; 485 } 486 childPatterns[nChildPatterns++] = pattern; 487 } 488 489 void endForeignChild(ParsedElementAnnotation ea) { 490 if (nChildPatterns == 0) 491 super.endForeignChild(ea); 492 else 493 childPatterns[nChildPatterns - 1] = schemaBuilder.annotateAfter(childPatterns[nChildPatterns - 1], ea); 494 } 495 496 void end() throws SAXException { 497 if (nChildPatterns == 0) { 498 error("missing_children"); 499 endChild(schemaBuilder.makeErrorPattern()); 500 } 501 if (comments != null) { 502 childPatterns[nChildPatterns - 1] = schemaBuilder.commentAfter(childPatterns[nChildPatterns - 1], comments); 503 comments = null; 504 } 505 sendPatternToParent(buildPattern(childPatterns, nChildPatterns, startLocation, annotations)); 506 } 507 508 void sendPatternToParent(ParsedPattern p) { 509 parent.endChild(p); 510 } 511 } 512 513 class GroupState extends PatternContainerState { 514 State create() { 515 return new GroupState(); 516 } 517 } 518 519 class ZeroOrMoreState extends PatternContainerState { 520 State create() { 521 return new ZeroOrMoreState(); 522 } 523 524 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) throws SAXException { 525 return schemaBuilder.makeZeroOrMore(super.buildPattern(patterns, nPatterns, loc, null), loc, anno); 526 } 527 } 528 529 class OneOrMoreState extends PatternContainerState { 530 State create() { 531 return new OneOrMoreState(); 532 } 533 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) throws SAXException { 534 return schemaBuilder.makeOneOrMore(super.buildPattern(patterns, nPatterns, loc, null), loc, anno); 535 } 536 } 537 538 class OptionalState extends PatternContainerState { 539 State create() { 540 return new OptionalState(); 541 } 542 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) throws SAXException { 543 return schemaBuilder.makeOptional(super.buildPattern(patterns, nPatterns, loc, null), loc, anno); 544 } 545 } 546 547 class ListState extends PatternContainerState { 548 State create() { 549 return new ListState(); 550 } 551 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) throws SAXException { 552 return schemaBuilder.makeList(super.buildPattern(patterns, nPatterns, loc, null), loc, anno); 553 } 554 } 555 556 class ChoiceState extends PatternContainerState { 557 State create() { 558 return new ChoiceState(); 559 } 560 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) throws SAXException { 561 return schemaBuilder.makeChoice(patterns, nPatterns, loc, anno); 562 } 563 } 564 565 class InterleaveState extends PatternContainerState { 566 State create() { 567 return new InterleaveState(); 568 } 569 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) { 570 return schemaBuilder.makeInterleave(patterns, nPatterns, loc, anno); 571 } 572 } 573 574 class MixedState extends PatternContainerState { 575 State create() { 576 return new MixedState(); 577 } 578 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) throws SAXException { 579 return schemaBuilder.makeMixed(super.buildPattern(patterns, nPatterns, loc, null), loc, anno); 580 } 581 } 582 583 static interface NameClassRef { 584 void setNameClass(ParsedNameClass nc); 585 } 586 587 class ElementState extends PatternContainerState implements NameClassRef { 588 ParsedNameClass nameClass; 589 boolean nameClassWasAttribute; 590 String name; 591 592 void setName(String name) { 593 this.name = name; 594 } 595 596 public void setNameClass(ParsedNameClass nc) { 597 nameClass = nc; 598 } 599 600 void endAttributes() throws SAXException { 601 if (name != null) { 602 nameClass = expandName(name, getNs(), null); 603 nameClassWasAttribute = true; 604 } 605 else 606 new NameClassChildState(this, this).set(); 607 } 608 609 State create() { 610 return new ElementState(); 611 } 612 613 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) throws SAXException { 614 return schemaBuilder.makeElement(nameClass, super.buildPattern(patterns, nPatterns, loc, null), loc, anno); 615 } 616 617 void endForeignChild(ParsedElementAnnotation ea) { 618 if (nameClassWasAttribute || nChildPatterns > 0 || nameClass == null) 619 super.endForeignChild(ea); 620 else 621 nameClass = schemaBuilder.annotateAfter(nameClass, ea); 622 } 623 } 624 625 class RootState extends PatternContainerState { 626 IncludedGrammar grammar; 627 628 RootState() { 629 } 630 631 RootState(IncludedGrammar grammar, Scope scope, String ns) { 632 this.grammar = grammar; 633 this.scope = scope; 634 this.nsInherit = ns; 635 this.datatypeLibrary = ""; 636 } 637 638 State create() { 639 return new RootState(); 640 } 641 642 State createChildState(String localName) throws SAXException { 643 if (grammar == null) 644 return super.createChildState(localName); 645 if (localName.equals("grammar")) 646 return new MergeGrammarState(grammar); 647 error("expected_grammar", localName); 648 return null; 649 } 650 651 void checkForeignElement() throws SAXException { 652 error("root_bad_namespace_uri", WellKnownNamespaces.RELAX_NG); 653 } 654 655 void endChild(ParsedPattern pattern) { 656 startPattern = pattern; 657 } 658 659 boolean isRelaxNGElement(String uri) throws SAXException { 660 if (!uri.startsWith(relaxngURIPrefix)) 661 return false; 662 if (!uri.equals(WellKnownNamespaces.RELAX_NG)) 663 warning("wrong_uri_version", 664 WellKnownNamespaces.RELAX_NG.substring(relaxngURIPrefix.length()), 665 uri.substring(relaxngURIPrefix.length())); 666 relaxngURI = uri; 667 return true; 668 } 669 670 } 671 672 class NotAllowedState extends EmptyContentState { 673 State create() { 674 return new NotAllowedState(); 675 } 676 677 ParsedPattern makePattern() { 678 return schemaBuilder.makeNotAllowed(startLocation, annotations); 679 } 680 } 681 682 class EmptyState extends EmptyContentState { 683 State create() { 684 return new EmptyState(); 685 } 686 687 ParsedPattern makePattern() { 688 return schemaBuilder.makeEmpty(startLocation, annotations); 689 } 690 } 691 692 class TextState extends EmptyContentState { 693 State create() { 694 return new TextState(); 695 } 696 697 ParsedPattern makePattern() { 698 return schemaBuilder.makeText(startLocation, annotations); 699 } 700 } 701 702 class ValueState extends EmptyContentState { 703 final StringBuffer buf = new StringBuffer(); 704 String type; 705 706 State create() { 707 return new ValueState(); 708 } 709 710 void setOtherAttribute(String name, String value) throws SAXException { 711 if (name.equals("type")) 712 type = checkNCName(value.trim()); 713 else 714 super.setOtherAttribute(name, value); 715 } 716 717 public void characters(char[] ch, int start, int len) { 718 buf.append(ch, start, len); 719 } 720 721 void checkForeignElement() throws SAXException { 722 error("value_contains_foreign_element"); 723 } 724 725 ParsedPattern makePattern() throws SAXException { 726 if (type == null) 727 return makePattern("", "token"); 728 else 729 return makePattern(datatypeLibrary, type); 730 } 731 732 void end() throws SAXException { 733 mergeLeadingComments(); 734 super.end(); 735 } 736 737 ParsedPattern makePattern(String datatypeLibrary, String type) { 738 return schemaBuilder.makeValue(datatypeLibrary, 739 type, 740 buf.toString(), 741 getContext(), 742 getNs(), 743 startLocation, 744 annotations); 745 } 746 747 } 748 749 class DataState extends State { 750 String type; 751 ParsedPattern except = null; 752 DataPatternBuilder dpb = null; 753 754 State create() { 755 return new DataState(); 756 } 757 758 State createChildState(String localName) throws SAXException { 759 if (localName.equals("param")) { 760 if (except != null) 761 error("param_after_except"); 762 return new ParamState(dpb); 763 } 764 if (localName.equals("except")) { 765 if (except != null) 766 error("multiple_except"); 767 return new ChoiceState(); 768 } 769 error("expected_param_except", localName); 770 return null; 771 } 772 773 void setOtherAttribute(String name, String value) throws SAXException { 774 if (name.equals("type")) 775 type = checkNCName(value.trim()); 776 else 777 super.setOtherAttribute(name, value); 778 } 779 780 void endAttributes() throws SAXException { 781 if (type == null) 782 error("missing_type_attribute"); 783 else 784 dpb = schemaBuilder.makeDataPatternBuilder(datatypeLibrary, type, startLocation); 785 } 786 787 void endForeignChild(ParsedElementAnnotation ea) { 788 dpb.annotation(ea); 789 } 790 791 void end() throws SAXException { 792 ParsedPattern p; 793 if (dpb != null) { 794 if (except != null) 795 p = dpb.makePattern(except, startLocation, annotations); 796 else 797 p = dpb.makePattern(startLocation, annotations); 798 } 799 else 800 p = schemaBuilder.makeErrorPattern(); 801 // XXX need to capture comments 802 parent.endChild(p); 803 } 804 805 void endChild(ParsedPattern pattern) { 806 except = pattern; 807 } 808 809 } 810 811 class ParamState extends State { 812 private final StringBuffer buf = new StringBuffer(); 813 private final DataPatternBuilder dpb; 814 private String name; 815 816 ParamState(DataPatternBuilder dpb) { 817 this.dpb = dpb; 818 } 819 820 State create() { 821 return new ParamState(null); 822 } 823 824 void setName(String name) throws SAXException { 825 this.name = checkNCName(name); 826 } 827 828 void endAttributes() throws SAXException { 829 if (name == null) 830 error("missing_name_attribute"); 831 } 832 833 State createChildState(String localName) throws SAXException { 834 error("expected_empty", localName); 835 return null; 836 } 837 838 public void characters(char[] ch, int start, int len) { 839 buf.append(ch, start, len); 840 } 841 842 void checkForeignElement() throws SAXException { 843 error("param_contains_foreign_element"); 844 } 845 846 void end() throws SAXException { 847 if (name == null) 848 return; 849 if (dpb == null) 850 return; 851 mergeLeadingComments(); 852 dpb.addParam(name, buf.toString(), getContext(), getNs(), startLocation, annotations); 853 } 854 } 855 856 class AttributeState extends PatternContainerState implements NameClassRef { 857 ParsedNameClass nameClass; 858 boolean nameClassWasAttribute; 859 String name; 860 861 State create() { 862 return new AttributeState(); 863 } 864 865 void setName(String name) { 866 this.name = name; 867 } 868 869 public void setNameClass(ParsedNameClass nc) { 870 nameClass = nc; 871 } 872 873 void endAttributes() throws SAXException { 874 if (name != null) { 875 String nsUse; 876 if (ns != null) 877 nsUse = ns; 878 else 879 nsUse = ""; 880 nameClass = expandName(name, nsUse, null); 881 nameClassWasAttribute = true; 882 } 883 else 884 new NameClassChildState(this, this).set(); 885 } 886 887 void endForeignChild(ParsedElementAnnotation ea) { 888 if (nameClassWasAttribute || nChildPatterns > 0 || nameClass == null) 889 super.endForeignChild(ea); 890 else 891 nameClass = schemaBuilder.annotateAfter(nameClass, ea); 892 } 893 894 void end() throws SAXException { 895 if (nChildPatterns == 0) 896 endChild(schemaBuilder.makeText(startLocation, null)); 897 super.end(); 898 } 899 900 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) throws SAXException { 901 return schemaBuilder.makeAttribute(nameClass, super.buildPattern(patterns, nPatterns, loc, null), loc, anno); 902 } 903 904 State createChildState(String localName) throws SAXException { 905 State tem = super.createChildState(localName); 906 if (tem != null && nChildPatterns != 0) 907 error("attribute_multi_pattern"); 908 return tem; 909 } 910 911 } 912 913 abstract class SinglePatternContainerState extends PatternContainerState { 914 State createChildState(String localName) throws SAXException { 915 if (nChildPatterns == 0) 916 return super.createChildState(localName); 917 error("too_many_children"); 918 return null; 919 } 920 } 921 922 class GrammarSectionState extends State { 923 GrammarSection section; 924 925 GrammarSectionState() { } 926 927 GrammarSectionState(GrammarSection section) { 928 this.section = section; 929 } 930 931 State create() { 932 return new GrammarSectionState(null); 933 } 934 935 State createChildState(String localName) throws SAXException { 936 if (localName.equals("define")) 937 return new DefineState(section); 938 if (localName.equals("start")) 939 return new StartState(section); 940 if (localName.equals("include")) { 941 Include include = section.makeInclude(); 942 if (include != null) 943 return new IncludeState(include); 944 } 945 if (localName.equals("div")) 946 return new DivState(section.makeDiv()); 947 error("expected_define", localName); 948 // XXX better errors 949 return null; 950 } 951 952 void end() throws SAXException { 953 if (comments != null) { 954 section.topLevelComment(comments); 955 comments = null; 956 } 957 } 958 959 void endForeignChild(ParsedElementAnnotation ea) { 960 section.topLevelAnnotation(ea); 961 } 962 } 963 964 class DivState extends GrammarSectionState { 965 final Div div; 966 DivState(Div div) { 967 super(div); 968 this.div = div; 969 } 970 971 void end() throws SAXException { 972 super.end(); 973 div.endDiv(startLocation, annotations); 974 } 975 } 976 977 class IncludeState extends GrammarSectionState { 978 String href; 979 final Include include; 980 981 IncludeState(Include include) { 982 super(include); 983 this.include = include; 984 } 985 986 void setOtherAttribute(String name, String value) throws SAXException { 987 if (name.equals("href")) { 988 href = value; 989 checkUri(href); 990 } 991 else 992 super.setOtherAttribute(name, value); 993 } 994 995 void endAttributes() throws SAXException { 996 if (href == null) 997 error("missing_href_attribute"); 998 else 999 href = resolve(href); 1000 } 1001 1002 void end() throws SAXException { 1003 super.end(); 1004 if (href != null) { 1005 try { 1006 include.endInclude(href, getNs(), startLocation, annotations); 1007 } 1008 catch (IllegalSchemaException e) { 1009 } 1010 } 1011 } 1012 } 1013 1014 class MergeGrammarState extends GrammarSectionState { 1015 final IncludedGrammar grammar; 1016 MergeGrammarState(IncludedGrammar grammar) { 1017 super(grammar); 1018 this.grammar = grammar; 1019 } 1020 1021 void end() throws SAXException { 1022 super.end(); 1023 parent.endChild(grammar.endIncludedGrammar(startLocation, annotations)); 1024 } 1025 } 1026 1027 class GrammarState extends GrammarSectionState { 1028 Grammar grammar; 1029 1030 void setParent(State parent) { 1031 super.setParent(parent); 1032 grammar = schemaBuilder.makeGrammar(scope); 1033 section = grammar; 1034 scope = grammar; 1035 } 1036 1037 State create() { 1038 return new GrammarState(); 1039 } 1040 1041 void end() throws SAXException { 1042 super.end(); 1043 parent.endChild(grammar.endGrammar(startLocation, annotations)); 1044 } 1045 } 1046 1047 class RefState extends EmptyContentState { 1048 String name; 1049 1050 State create() { 1051 return new RefState(); 1052 } 1053 1054 void endAttributes() throws SAXException { 1055 if (name == null) 1056 error("missing_name_attribute"); 1057 } 1058 1059 void setName(String name) throws SAXException { 1060 this.name = checkNCName(name); 1061 } 1062 1063 ParsedPattern makePattern() { 1064 if (name == null) 1065 return schemaBuilder.makeErrorPattern(); 1066 return scope.makeRef(name, startLocation, annotations); 1067 } 1068 } 1069 1070 class ParentRefState extends RefState { 1071 State create() { 1072 return new ParentRefState(); 1073 } 1074 1075 ParsedPattern makePattern() { 1076 if (name == null) 1077 return schemaBuilder.makeErrorPattern(); 1078 return scope.makeParentRef(name, startLocation, annotations); 1079 } 1080 } 1081 1082 class ExternalRefState extends EmptyContentState { 1083 String href; 1084 ParsedPattern includedPattern; 1085 1086 State create() { 1087 return new ExternalRefState(); 1088 } 1089 1090 void setOtherAttribute(String name, String value) throws SAXException { 1091 if (name.equals("href")) { 1092 href = value; 1093 checkUri(href); 1094 } 1095 else 1096 super.setOtherAttribute(name, value); 1097 } 1098 1099 void endAttributes() throws SAXException { 1100 if (href == null) 1101 error("missing_href_attribute"); 1102 else 1103 href = resolve(href); 1104 } 1105 1106 ParsedPattern makePattern() { 1107 if (href != null) { 1108 try { 1109 return schemaBuilder.makeExternalRef(href, 1110 getNs(), 1111 scope, 1112 startLocation, 1113 annotations); 1114 } 1115 catch (IllegalSchemaException e) { } 1116 } 1117 return schemaBuilder.makeErrorPattern(); 1118 } 1119 } 1120 1121 abstract class DefinitionState extends PatternContainerState { 1122 GrammarSection.Combine combine = null; 1123 final GrammarSection section; 1124 1125 DefinitionState(GrammarSection section) { 1126 this.section = section; 1127 } 1128 1129 void setOtherAttribute(String name, String value) throws SAXException { 1130 if (name.equals("combine")) { 1131 value = value.trim(); 1132 if (value.equals("choice")) 1133 combine = GrammarSection.COMBINE_CHOICE; 1134 else if (value.equals("interleave")) 1135 combine = GrammarSection.COMBINE_INTERLEAVE; 1136 else 1137 error("combine_attribute_bad_value", value); 1138 } 1139 else 1140 super.setOtherAttribute(name, value); 1141 } 1142 1143 ParsedPattern buildPattern(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno) throws SAXException { 1144 return super.buildPattern(patterns, nPatterns, loc, null); 1145 } 1146 } 1147 1148 class DefineState extends DefinitionState { 1149 String name; 1150 1151 DefineState(GrammarSection section) { 1152 super(section); 1153 } 1154 1155 State create() { 1156 return new DefineState(null); 1157 } 1158 1159 void setName(String name) throws SAXException { 1160 this.name = checkNCName(name); 1161 } 1162 1163 void endAttributes() throws SAXException { 1164 if (name == null) 1165 error("missing_name_attribute"); 1166 } 1167 1168 void sendPatternToParent(ParsedPattern p) { 1169 if (name != null) 1170 section.define(name, combine, p, startLocation, annotations); 1171 } 1172 1173 } 1174 1175 class StartState extends DefinitionState { 1176 1177 StartState(GrammarSection section) { 1178 super(section); 1179 } 1180 1181 State create() { 1182 return new StartState(null); 1183 } 1184 1185 void sendPatternToParent(ParsedPattern p) { 1186 section.define(GrammarSection.START, combine, p, startLocation, annotations); 1187 } 1188 1189 State createChildState(String localName) throws SAXException { 1190 State tem = super.createChildState(localName); 1191 if (tem != null && nChildPatterns != 0) 1192 error("start_multi_pattern"); 1193 return tem; 1194 } 1195 1196 } 1197 1198 abstract class NameClassContainerState extends State { 1199 State createChildState(String localName) throws SAXException { 1200 State state = (State)nameClassTable.get(localName); 1201 if (state == null) { 1202 error("expected_name_class", localName); 1203 return null; 1204 } 1205 return state.create(); 1206 } 1207 } 1208 1209 class NameClassChildState extends NameClassContainerState { 1210 final State prevState; 1211 final NameClassRef nameClassRef; 1212 1213 State create() { 1214 return null; 1215 } 1216 1217 NameClassChildState(State prevState, NameClassRef nameClassRef) { 1218 this.prevState = prevState; 1219 this.nameClassRef = nameClassRef; 1220 setParent(prevState.parent); 1221 this.ns = prevState.ns; 1222 } 1223 1224 void endChild(ParsedNameClass nameClass) { 1225 nameClassRef.setNameClass(nameClass); 1226 prevState.set(); 1227 } 1228 1229 void endForeignChild(ParsedElementAnnotation ea) { 1230 prevState.endForeignChild(ea); 1231 } 1232 1233 void end() throws SAXException { 1234 nameClassRef.setNameClass(schemaBuilder.makeErrorNameClass()); 1235 error("missing_name_class"); 1236 prevState.set(); 1237 prevState.end(); 1238 } 1239 } 1240 1241 abstract class NameClassBaseState extends State { 1242 1243 abstract ParsedNameClass makeNameClass() throws SAXException; 1244 1245 void end() throws SAXException { 1246 parent.endChild(makeNameClass()); 1247 } 1248 } 1249 1250 class NameState extends NameClassBaseState { 1251 final StringBuffer buf = new StringBuffer(); 1252 1253 State createChildState(String localName) throws SAXException { 1254 error("expected_name", localName); 1255 return null; 1256 } 1257 1258 State create() { 1259 return new NameState(); 1260 } 1261 1262 public void characters(char[] ch, int start, int len) { 1263 buf.append(ch, start, len); 1264 } 1265 1266 void checkForeignElement() throws SAXException { 1267 error("name_contains_foreign_element"); 1268 } 1269 1270 ParsedNameClass makeNameClass() throws SAXException { 1271 mergeLeadingComments(); 1272 return expandName(buf.toString().trim(), getNs(), annotations); 1273 } 1274 1275 } 1276 1277 private static final int PATTERN_CONTEXT = 0; 1278 private static final int ANY_NAME_CONTEXT = 1; 1279 private static final int NS_NAME_CONTEXT = 2; 1280 1281 class AnyNameState extends NameClassBaseState { 1282 ParsedNameClass except = null; 1283 1284 State create() { 1285 return new AnyNameState(); 1286 } 1287 1288 State createChildState(String localName) throws SAXException { 1289 if (localName.equals("except")) { 1290 if (except != null) 1291 error("multiple_except"); 1292 return new NameClassChoiceState(getContext()); 1293 } 1294 error("expected_except", localName); 1295 return null; 1296 } 1297 1298 int getContext() { 1299 return ANY_NAME_CONTEXT; 1300 } 1301 1302 ParsedNameClass makeNameClass() { 1303 if (except == null) 1304 return makeNameClassNoExcept(); 1305 else 1306 return makeNameClassExcept(except); 1307 } 1308 1309 ParsedNameClass makeNameClassNoExcept() { 1310 return schemaBuilder.makeAnyName(startLocation, annotations); 1311 } 1312 1313 ParsedNameClass makeNameClassExcept(ParsedNameClass except) { 1314 return schemaBuilder.makeAnyName(except, startLocation, annotations); 1315 } 1316 1317 void endChild(ParsedNameClass nameClass) { 1318 except = nameClass; 1319 } 1320 1321 } 1322 1323 class NsNameState extends AnyNameState { 1324 State create() { 1325 return new NsNameState(); 1326 } 1327 1328 ParsedNameClass makeNameClassNoExcept() { 1329 return schemaBuilder.makeNsName(getNs(), null, null); 1330 } 1331 1332 ParsedNameClass makeNameClassExcept(ParsedNameClass except) { 1333 return schemaBuilder.makeNsName(getNs(), except, null, null); 1334 } 1335 1336 int getContext() { 1337 return NS_NAME_CONTEXT; 1338 } 1339 1340 } 1341 1342 class NameClassChoiceState extends NameClassContainerState { 1343 private ParsedNameClass[] nameClasses; 1344 private int nNameClasses; 1345 private int context; 1346 1347 NameClassChoiceState() { 1348 this.context = PATTERN_CONTEXT; 1349 } 1350 1351 NameClassChoiceState(int context) { 1352 this.context = context; 1353 } 1354 1355 void setParent(State parent) { 1356 super.setParent(parent); 1357 if (parent instanceof NameClassChoiceState) 1358 this.context = ((NameClassChoiceState)parent).context; 1359 } 1360 1361 State create() { 1362 return new NameClassChoiceState(); 1363 } 1364 1365 State createChildState(String localName) throws SAXException { 1366 if (localName.equals("anyName")) { 1367 if (context >= ANY_NAME_CONTEXT) { 1368 error(context == ANY_NAME_CONTEXT 1369 ? "any_name_except_contains_any_name" 1370 : "ns_name_except_contains_any_name"); 1371 return null; 1372 } 1373 } 1374 else if (localName.equals("nsName")) { 1375 if (context == NS_NAME_CONTEXT) { 1376 error("ns_name_except_contains_ns_name"); 1377 return null; 1378 } 1379 } 1380 return super.createChildState(localName); 1381 } 1382 1383 void endChild(ParsedNameClass nc) { 1384 if (nameClasses == null) 1385 nameClasses = new ParsedNameClass[INIT_CHILD_ALLOC]; 1386 else if (nNameClasses >= nameClasses.length) { 1387 ParsedNameClass[] newNameClasses = new ParsedNameClass[nameClasses.length * 2]; 1388 System.arraycopy(nameClasses, 0, newNameClasses, 0, nameClasses.length); 1389 nameClasses = newNameClasses; 1390 } 1391 nameClasses[nNameClasses++] = nc; 1392 } 1393 1394 void endForeignChild(ParsedElementAnnotation ea) { 1395 if (nNameClasses == 0) 1396 super.endForeignChild(ea); 1397 else 1398 nameClasses[nNameClasses - 1] = schemaBuilder.annotateAfter(nameClasses[nNameClasses - 1], ea); 1399 } 1400 1401 void end() throws SAXException { 1402 if (nNameClasses == 0) { 1403 error("missing_name_class"); 1404 parent.endChild(schemaBuilder.makeErrorNameClass()); 1405 return; 1406 } 1407 if (comments != null) { 1408 nameClasses[nNameClasses - 1] = schemaBuilder.commentAfter(nameClasses[nNameClasses - 1], comments); 1409 comments = null; 1410 } 1411 parent.endChild(schemaBuilder.makeChoice(nameClasses, nNameClasses, startLocation, annotations)); 1412 } 1413 } 1414 1415 private void initPatternTable() { 1416 patternTable = new Hashtable(); 1417 patternTable.put("zeroOrMore", new ZeroOrMoreState()); 1418 patternTable.put("oneOrMore", new OneOrMoreState()); 1419 patternTable.put("optional", new OptionalState()); 1420 patternTable.put("list", new ListState()); 1421 patternTable.put("choice", new ChoiceState()); 1422 patternTable.put("interleave", new InterleaveState()); 1423 patternTable.put("group", new GroupState()); 1424 patternTable.put("mixed", new MixedState()); 1425 patternTable.put("element", new ElementState()); 1426 patternTable.put("attribute", new AttributeState()); 1427 patternTable.put("empty", new EmptyState()); 1428 patternTable.put("text", new TextState()); 1429 patternTable.put("value", new ValueState()); 1430 patternTable.put("data", new DataState()); 1431 patternTable.put("notAllowed", new NotAllowedState()); 1432 patternTable.put("grammar", new GrammarState()); 1433 patternTable.put("ref", new RefState()); 1434 patternTable.put("parentRef", new ParentRefState()); 1435 patternTable.put("externalRef", new ExternalRefState()); 1436 } 1437 1438 private void initNameClassTable() { 1439 nameClassTable = new Hashtable(); 1440 nameClassTable.put("name", new NameState()); 1441 nameClassTable.put("anyName", new AnyNameState()); 1442 nameClassTable.put("nsName", new NsNameState()); 1443 nameClassTable.put("choice", new NameClassChoiceState()); 1444 } 1445 1446 public ParsedPattern getParsedPattern() throws IllegalSchemaException { 1447 if (hadError) 1448 throw new IllegalSchemaException(); 1449 return startPattern; 1450 } 1451 1452 private void error(String key) throws SAXException { 1453 error(key, locator); 1454 } 1455 1456 private void error(String key, String arg) throws SAXException { 1457 error(key, arg, locator); 1458 } 1459 1460 void error(String key, String arg1, String arg2) throws SAXException { 1461 error(key, arg1, arg2, locator); 1462 } 1463 1464 private void error(String key, Locator loc) throws SAXException { 1465 error(new SAXParseException(localizer.message(key), loc)); 1466 } 1467 1468 private void error(String key, String arg, Locator loc) throws SAXException { 1469 error(new SAXParseException(localizer.message(key, arg), loc)); 1470 } 1471 1472 private void error(String key, String arg1, String arg2, Locator loc) 1473 throws SAXException { 1474 error(new SAXParseException(localizer.message(key, arg1, arg2), loc)); 1475 } 1476 1477 private void error(SAXParseException e) throws SAXException { 1478 hadError = true; 1479 if (eh != null) 1480 eh.error(e); 1481 } 1482 1483 void warning(String key) throws SAXException { 1484 warning(key, locator); 1485 } 1486 1487 private void warning(String key, String arg) throws SAXException { 1488 warning(key, arg, locator); 1489 } 1490 1491 private void warning(String key, String arg1, String arg2) throws SAXException { 1492 warning(key, arg1, arg2, locator); 1493 } 1494 1495 private void warning(String key, Locator loc) throws SAXException { 1496 warning(new SAXParseException(localizer.message(key), loc)); 1497 } 1498 1499 private void warning(String key, String arg, Locator loc) throws SAXException { 1500 warning(new SAXParseException(localizer.message(key, arg), loc)); 1501 } 1502 1503 private void warning(String key, String arg1, String arg2, Locator loc) 1504 throws SAXException { 1505 warning(new SAXParseException(localizer.message(key, arg1, arg2), loc)); 1506 } 1507 1508 private void warning(SAXParseException e) throws SAXException { 1509 if (eh != null) 1510 eh.warning(e); 1511 } 1512 1513 SchemaParser(XMLReader xr, 1514 ErrorHandler eh, 1515 EntityResolver er, 1516 SchemaBuilder schemaBuilder, 1517 IncludedGrammar grammar, 1518 Scope scope) throws SAXException { 1519 this.xr = xr; 1520 this.eh = eh; 1521 this.schemaBuilder = schemaBuilder; 1522 if (eh != null) 1523 xr.setErrorHandler(eh); 1524 if (er != null) 1525 xr.setEntityResolver(er); 1526 xr.setDTDHandler(context); 1527 if (schemaBuilder.usesComments()) { 1528 try { 1529 xr.setProperty("http://xml.org/sax/properties/lexical-handler", new LexicalHandlerImpl()); 1530 } 1531 catch (SAXNotRecognizedException e) { 1532 warning("no_comment_support", xr.getClass().getName()); 1533 } 1534 catch (SAXNotSupportedException e) { 1535 warning("no_comment_support", xr.getClass().getName()); 1536 } 1537 } 1538 initPatternTable(); 1539 initNameClassTable(); 1540 new RootState(grammar, scope, SchemaBuilder.INHERIT_NS).set(); 1541 } 1542 1543 1544 private Context getContext() { 1545 return context; 1546 } 1547 1548 class LexicalHandlerImpl extends AbstractLexicalHandler { 1549 private boolean inDtd = false; 1550 1551 public void startDTD(String s, String s1, String s2) throws SAXException { 1552 inDtd = true; 1553 } 1554 1555 public void endDTD() throws SAXException { 1556 inDtd = false; 1557 } 1558 1559 public void comment(char[] chars, int start, int length) throws SAXException { 1560 if (!inDtd) 1561 ((CommentHandler)xr.getContentHandler()).comment(new String(chars, start, length)); 1562 } 1563 } 1564 1565 private ParsedNameClass expandName(String name, String ns, Annotations anno) throws SAXException { 1566 int ic = name.indexOf(':'); 1567 if (ic == -1) 1568 return schemaBuilder.makeName(ns, checkNCName(name), null, null, anno); 1569 String prefix = checkNCName(name.substring(0, ic)); 1570 String localName = checkNCName(name.substring(ic + 1)); 1571 for (PrefixMapping tem = context.prefixMapping; tem != null; tem = tem.next) 1572 if (tem.prefix.equals(prefix)) 1573 return schemaBuilder.makeName(tem.uri, localName, prefix, null, anno); 1574 error("undefined_prefix", prefix); 1575 return schemaBuilder.makeName("", localName, null, null, anno); 1576 } 1577 1578 private String findPrefix(String qName, String uri) { 1579 String prefix = null; 1580 if (qName == null || qName.equals("")) { 1581 for (PrefixMapping p = context.prefixMapping; p != null; p = p.next) 1582 if (p.uri.equals(uri)) { 1583 prefix = p.prefix; 1584 break; 1585 } 1586 } 1587 else { 1588 int off = qName.indexOf(':'); 1589 if (off > 0) 1590 prefix = qName.substring(0, off); 1591 } 1592 return prefix; 1593 } 1594 private String checkNCName(String str) throws SAXException { 1595 if (!Naming.isNcname(str)) 1596 error("invalid_ncname", str); 1597 return str; 1598 } 1599 1600 private String resolve(String systemId) throws SAXException { 1601 if (Uri.hasFragmentId(systemId)) 1602 error("href_fragment_id"); 1603 systemId = Uri.escapeDisallowedChars(systemId); 1604 return Uri.resolve(xmlBaseHandler.getBaseUri(), systemId); 1605 } 1606 1607 private Location makeLocation() { 1608 if (locator == null) 1609 return null; 1610 return schemaBuilder.makeLocation(locator.getSystemId(), 1611 locator.getLineNumber(), 1612 locator.getColumnNumber()); 1613 } 1614 1615 private void checkUri(String s) throws SAXException { 1616 if (!Uri.isValid(s)) 1617 error("invalid_uri", s); 1618 } 1619 }