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