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    }