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 }