001    package com.thaiopensource.relaxng.impl;
002    
003    import com.thaiopensource.relaxng.parse.Annotations;
004    import com.thaiopensource.relaxng.parse.BuildException;
005    import com.thaiopensource.relaxng.parse.DataPatternBuilder;
006    import com.thaiopensource.relaxng.parse.Div;
007    import com.thaiopensource.relaxng.parse.ParsedElementAnnotation;
008    import com.thaiopensource.relaxng.parse.ElementAnnotationBuilder;
009    import com.thaiopensource.relaxng.parse.Grammar;
010    import com.thaiopensource.relaxng.parse.GrammarSection;
011    import com.thaiopensource.relaxng.parse.IllegalSchemaException;
012    import com.thaiopensource.relaxng.parse.Include;
013    import com.thaiopensource.relaxng.parse.IncludedGrammar;
014    import com.thaiopensource.relaxng.parse.Location;
015    import com.thaiopensource.relaxng.parse.Parseable;
016    import com.thaiopensource.relaxng.parse.ParsedNameClass;
017    import com.thaiopensource.relaxng.parse.ParsedPattern;
018    import com.thaiopensource.relaxng.parse.SchemaBuilder;
019    import com.thaiopensource.relaxng.parse.Scope;
020    import com.thaiopensource.relaxng.parse.Context;
021    import com.thaiopensource.relaxng.parse.CommentList;
022    import com.thaiopensource.relaxng.parse.SubParser;
023    import com.thaiopensource.relaxng.parse.ParseReceiver;
024    import com.thaiopensource.relaxng.parse.ParsedPatternFuture;
025    import com.thaiopensource.validate.IncorrectSchemaException;
026    import com.thaiopensource.util.Localizer;
027    import com.thaiopensource.xml.util.Name;
028    
029    import org.relaxng.datatype.Datatype;
030    import org.relaxng.datatype.DatatypeException;
031    import org.relaxng.datatype.DatatypeLibrary;
032    import org.relaxng.datatype.DatatypeLibraryFactory;
033    import org.relaxng.datatype.ValidationContext;
034    import org.relaxng.datatype.DatatypeBuilder;
035    
036    import org.xml.sax.ErrorHandler;
037    import org.xml.sax.Locator;
038    import org.xml.sax.SAXException;
039    import org.xml.sax.SAXParseException;
040    import org.xml.sax.XMLReader;
041    
042    import java.util.Enumeration;
043    import java.util.Hashtable;
044    import java.io.IOException;
045    
046    public class SchemaBuilderImpl implements SchemaBuilder, ElementAnnotationBuilder, CommentList {
047      private final SchemaBuilderImpl parent;
048      private boolean hadError = false;
049      private final SubParser subParser;
050      private final SchemaPatternBuilder pb;
051      private final DatatypeLibraryFactory datatypeLibraryFactory;
052      private final String inheritNs;
053      private final ErrorHandler eh;
054      private final OpenIncludes openIncludes;
055      private final AttributeNameClassChecker attributeNameClassChecker = new AttributeNameClassChecker();
056      static final Localizer localizer = new Localizer(SchemaBuilderImpl.class);
057    
058      static class OpenIncludes {
059        final String uri;
060        final OpenIncludes parent;
061    
062        OpenIncludes(String uri, OpenIncludes parent) {
063          this.uri = uri;
064          this.parent = parent;
065        }
066      }
067    
068      static public Pattern parse(Parseable parseable,
069                                  ErrorHandler eh,
070                                  DatatypeLibraryFactory datatypeLibraryFactory,
071                                  SchemaPatternBuilder pb,
072                                  boolean isAttributesPattern)
073              throws IncorrectSchemaException, IOException, SAXException {
074        try {
075          SchemaBuilderImpl sb = new SchemaBuilderImpl(parseable,
076                                                       eh,
077                                                       new BuiltinDatatypeLibraryFactory(datatypeLibraryFactory),
078                                                       pb);
079          ParsedPattern pp = parseable.parse(sb, new RootScope(sb));
080          if (isAttributesPattern)
081            pp = sb.wrapAttributesPattern(pp);
082          return sb.expandPattern((Pattern)pp);
083        }
084        catch (IllegalSchemaException e) {
085          throw new IncorrectSchemaException();
086        }
087        catch (BuildException e) {
088          throw unwrapBuildException(e);
089        }
090      }
091    
092    
093      static public PatternFuture installHandlers(ParseReceiver parser, XMLReader xr, ErrorHandler eh, DatatypeLibraryFactory dlf,
094                                                  SchemaPatternBuilder pb)
095              throws SAXException {
096        final SchemaBuilderImpl sb = new SchemaBuilderImpl(parser,
097                                                           eh,
098                                                           new BuiltinDatatypeLibraryFactory(dlf),
099                                                           pb);
100        final ParsedPatternFuture pf = parser.installHandlers(xr, sb, new RootScope(sb));
101        return new PatternFuture() {
102          public Pattern getPattern(boolean isAttributesPattern) throws IncorrectSchemaException, SAXException, IOException {
103            try {
104              ParsedPattern pp = pf.getParsedPattern();
105              if (isAttributesPattern)
106                pp = sb.wrapAttributesPattern(pp);
107              return sb.expandPattern((Pattern)pp);
108            }
109            catch (IllegalSchemaException e) {
110              throw new IncorrectSchemaException();
111            }
112            catch (BuildException e) {
113              throw unwrapBuildException(e);
114            }
115          }
116        };
117      }
118    
119      static RuntimeException unwrapBuildException(BuildException e) throws SAXException, IncorrectSchemaException, IOException {
120        Throwable t = e.getCause();
121        if (t instanceof IOException)
122          throw (IOException)t;
123        if (t instanceof RuntimeException)
124          return (RuntimeException)t;
125        if (t instanceof IllegalSchemaException)
126          throw new IncorrectSchemaException();
127        if (t instanceof SAXException)
128          throw (SAXException)t;
129        if (t instanceof Exception)
130          throw new SAXException((Exception)t);
131        throw new SAXException(t.getClass().getName() + " thrown");
132      }
133    
134      private ParsedPattern wrapAttributesPattern(ParsedPattern pattern) {
135        // XXX where can we get a locator from?
136        return makeElement(makeAnyName(null, null), pattern, null, null);
137      }
138    
139      private Pattern expandPattern(Pattern pattern) throws IllegalSchemaException, BuildException {
140        if (!hadError) {
141          try {
142            pattern.checkRecursion(0);
143            pattern = pattern.expand(pb);
144            pattern.checkRestrictions(Pattern.START_CONTEXT, null, null);
145            if (!hadError)
146              return pattern;
147          }
148          catch (SAXParseException e) {
149            error(e);
150          }
151          catch (SAXException e) {
152            throw new BuildException(e);
153          }
154          catch (RestrictionViolationException e) {
155            if (e.getName() != null)
156              error(e.getMessageId(), NameFormatter.format(e.getName()), e.getLocator());
157            else
158              error(e.getMessageId(), e.getLocator());
159          }
160        }
161        throw new IllegalSchemaException();
162      }
163    
164      private SchemaBuilderImpl(SubParser subParser,
165                                ErrorHandler eh,
166                                DatatypeLibraryFactory datatypeLibraryFactory,
167                                SchemaPatternBuilder pb) {
168        this.parent = null;
169        this.subParser = subParser;
170        this.eh = eh;
171        this.datatypeLibraryFactory = datatypeLibraryFactory;
172        this.pb = pb;
173        this.inheritNs = "";
174        this.openIncludes = null;
175      }
176    
177      private SchemaBuilderImpl(String inheritNs,
178                                String uri,
179                                SchemaBuilderImpl parent) {
180        this.parent = parent;
181        this.subParser = parent.subParser;
182        this.eh = parent.eh;
183        this.datatypeLibraryFactory = parent.datatypeLibraryFactory;
184        this.pb = parent.pb;
185        this.inheritNs = parent.resolveInherit(inheritNs);
186        this.openIncludes = new OpenIncludes(uri, parent.openIncludes);
187      }
188    
189      public ParsedPattern makeChoice(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno)
190              throws BuildException {
191        if (nPatterns <= 0)
192          throw new IllegalArgumentException();
193        Pattern result = (Pattern)patterns[0];
194        for (int i = 1; i < nPatterns; i++)
195          result = pb.makeChoice(result, (Pattern)patterns[i]);
196        return result;
197      }
198    
199      public ParsedPattern makeInterleave(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno)
200              throws BuildException {
201        if (nPatterns <= 0)
202          throw new IllegalArgumentException();
203        Pattern result = (Pattern)patterns[0];
204        for (int i = 1; i < nPatterns; i++)
205          result = pb.makeInterleave(result, (Pattern)patterns[i]);
206        return result;
207      }
208    
209      public ParsedPattern makeGroup(ParsedPattern[] patterns, int nPatterns, Location loc, Annotations anno)
210              throws BuildException {
211        if (nPatterns <= 0)
212          throw new IllegalArgumentException();
213        Pattern result = (Pattern)patterns[0];
214        for (int i = 1; i < nPatterns; i++)
215          result = pb.makeGroup(result, (Pattern)patterns[i]);
216        return result;
217      }
218    
219      public ParsedPattern makeOneOrMore(ParsedPattern p, Location loc, Annotations anno)
220              throws BuildException {
221        return pb.makeOneOrMore((Pattern)p);
222      }
223    
224      public ParsedPattern makeZeroOrMore(ParsedPattern p, Location loc, Annotations anno)
225              throws BuildException {
226        return pb.makeZeroOrMore((Pattern)p);
227      }
228    
229      public ParsedPattern makeOptional(ParsedPattern p, Location loc, Annotations anno)
230              throws BuildException {
231        return pb.makeOptional((Pattern)p);
232      }
233    
234      public ParsedPattern makeList(ParsedPattern p, Location loc, Annotations anno)
235              throws BuildException {
236        return pb.makeList((Pattern)p, (Locator)loc);
237      }
238    
239      public ParsedPattern makeMixed(ParsedPattern p, Location loc, Annotations anno)
240              throws BuildException {
241        return pb.makeMixed((Pattern)p);
242      }
243    
244      public ParsedPattern makeEmpty(Location loc, Annotations anno) {
245        return pb.makeEmpty();
246      }
247    
248      public ParsedPattern makeNotAllowed(Location loc, Annotations anno) {
249        return pb.makeUnexpandedNotAllowed();
250      }
251    
252      public ParsedPattern makeText(Location loc, Annotations anno) {
253        return pb.makeText();
254      }
255    
256      public ParsedPattern makeErrorPattern() {
257        return pb.makeError();
258      }
259    
260      public ParsedNameClass makeErrorNameClass() {
261        return new ErrorNameClass();
262      }
263    
264      public ParsedPattern makeAttribute(ParsedNameClass nc, ParsedPattern p, Location loc, Annotations anno)
265              throws BuildException {
266        String messageId = attributeNameClassChecker.checkNameClass((NameClass)nc);
267        if (messageId != null)
268          error(messageId, (Locator)loc);
269        return pb.makeAttribute((NameClass)nc, (Pattern)p, (Locator)loc);
270      }
271    
272      public ParsedPattern makeElement(ParsedNameClass nc, ParsedPattern p, Location loc, Annotations anno)
273              throws BuildException {
274        return pb.makeElement((NameClass)nc, (Pattern)p, (Locator)loc);
275      }
276    
277      private class DummyDataPatternBuilder implements DataPatternBuilder {
278        public void addParam(String name, String value, Context context, String ns, Location loc, Annotations anno)
279                throws BuildException {
280        }
281    
282        public void annotation(ParsedElementAnnotation ea)
283                throws BuildException {
284        }
285    
286        public ParsedPattern makePattern(Location loc, Annotations anno)
287                throws BuildException {
288          return pb.makeError();
289        }
290    
291        public ParsedPattern makePattern(ParsedPattern except, Location loc, Annotations anno)
292                throws BuildException {
293          return pb.makeError();
294        }
295      }
296    
297      private class ValidationContextImpl implements ValidationContext {
298        private final ValidationContext vc;
299        private final String ns;
300    
301        ValidationContextImpl(ValidationContext vc, String ns) {
302          this.vc = vc;
303          this.ns = ns.length() == 0 ? null : ns;
304        }
305    
306        public String resolveNamespacePrefix(String prefix) {
307          String result = prefix.length() == 0 ? ns : vc.resolveNamespacePrefix(prefix);
308          if (result == INHERIT_NS) {
309            if (inheritNs.length() == 0)
310              return null;
311            return inheritNs;
312          }
313          return result;
314        }
315    
316        public String getBaseUri() {
317          return vc.getBaseUri();
318        }
319    
320        public boolean isUnparsedEntity(String entityName) {
321          return vc.isUnparsedEntity(entityName);
322        }
323    
324        public boolean isNotation(String notationName) {
325          return vc.isNotation(notationName);
326        }
327      }
328    
329      private class DataPatternBuilderImpl implements DataPatternBuilder {
330        private final DatatypeBuilder dtb;
331        DataPatternBuilderImpl(DatatypeBuilder dtb) {
332          this.dtb = dtb;
333        }
334    
335        public void addParam(String name, String value, Context context, String ns, Location loc, Annotations anno)
336                throws BuildException {
337          try {
338            dtb.addParameter(name, value, new ValidationContextImpl(context, ns));
339          }
340          catch (DatatypeException e) {
341            String detail = e.getMessage();
342            int pos = e.getIndex();
343            String displayedParam;
344            if (pos == DatatypeException.UNKNOWN)
345              displayedParam = null;
346            else
347              displayedParam = displayParam(value, pos);
348            if (displayedParam != null) {
349              if (detail != null)
350                error("invalid_param_detail_display", detail, displayedParam, (Locator)loc);
351              else
352                error("invalid_param_display", displayedParam, (Locator)loc);
353            }
354            else if (detail != null)
355              error("invalid_param_detail", detail, (Locator)loc);
356            else
357              error("invalid_param", (Locator)loc);
358          }
359        }
360    
361        public void annotation(ParsedElementAnnotation ea)
362                throws BuildException {
363        }
364    
365        String displayParam(String value, int pos) {
366          if (pos < 0)
367            pos = 0;
368          else if (pos > value.length())
369            pos = value.length();
370          return localizer.message("display_param", value.substring(0, pos), value.substring(pos));
371        }
372    
373        public ParsedPattern makePattern(Location loc, Annotations anno)
374                throws BuildException {
375          try {
376            return pb.makeData(dtb.createDatatype());
377          }
378          catch (DatatypeException e) {
379            String detail = e.getMessage();
380            if (detail != null)
381              error("invalid_params_detail", detail, (Locator)loc);
382            else
383              error("invalid_params", (Locator)loc);
384            return pb.makeError();
385          }
386        }
387    
388        public ParsedPattern makePattern(ParsedPattern except, Location loc, Annotations anno)
389                throws BuildException {
390          try {
391            return pb.makeDataExcept(dtb.createDatatype(), (Pattern)except, (Locator)loc);
392          }
393          catch (DatatypeException e) {
394            String detail = e.getMessage();
395            if (detail != null)
396              error("invalid_params_detail", detail, (Locator)loc);
397            else
398              error("invalid_params", (Locator)loc);
399            return pb.makeError();
400          }
401        }
402      }
403    
404      public DataPatternBuilder makeDataPatternBuilder(String datatypeLibrary, String type, Location loc)
405              throws BuildException {
406        DatatypeLibrary dl = datatypeLibraryFactory.createDatatypeLibrary(datatypeLibrary);
407        if (dl == null)
408          error("unrecognized_datatype_library", datatypeLibrary, (Locator)loc);
409        else {
410          try {
411            return new DataPatternBuilderImpl(dl.createDatatypeBuilder(type));
412          }
413          catch (DatatypeException e) {
414            String detail = e.getMessage();
415            if (detail != null)
416              error("unsupported_datatype_detail", datatypeLibrary, type, detail, (Locator)loc);
417            else
418              error("unrecognized_datatype", datatypeLibrary, type, (Locator)loc);
419          }
420        }
421        return new DummyDataPatternBuilder();
422      }
423    
424      public ParsedPattern makeValue(String datatypeLibrary, String type, String value, Context context, String ns,
425                                     Location loc, Annotations anno) throws BuildException {
426        DatatypeLibrary dl = datatypeLibraryFactory.createDatatypeLibrary(datatypeLibrary);
427        if (dl == null)
428          error("unrecognized_datatype_library", datatypeLibrary, (Locator)loc);
429        else {
430          try {
431            DatatypeBuilder dtb = dl.createDatatypeBuilder(type);
432            try {
433              Datatype dt = dtb.createDatatype();
434              Object obj = dt.createValue(value, new ValidationContextImpl(context, ns));
435              if (obj != null)
436                return pb.makeValue(dt, obj);
437              error("invalid_value", value, (Locator)loc);
438            }
439            catch (DatatypeException e) {
440              String detail = e.getMessage();
441              if (detail != null)
442                error("datatype_requires_param_detail", detail, (Locator)loc);
443              else
444                error("datatype_requires_param", (Locator)loc);
445            }
446          }
447          catch (DatatypeException e) {
448            error("unrecognized_datatype", datatypeLibrary, type, (Locator)loc);
449          }
450        }
451        return pb.makeError();
452      }
453    
454      static class GrammarImpl implements Grammar, Div, IncludedGrammar {
455        private final SchemaBuilderImpl sb;
456        private final Hashtable defines;
457        private final RefPattern startRef;
458        private final Scope parent;
459    
460        private GrammarImpl(SchemaBuilderImpl sb, Scope parent) {
461          this.sb = sb;
462          this.parent = parent;
463          this.defines = new Hashtable();
464          this.startRef = new RefPattern(null);
465        }
466    
467        protected GrammarImpl(SchemaBuilderImpl sb, GrammarImpl g) {
468          this.sb = sb;
469          parent = g.parent;
470          startRef = g.startRef;
471          defines = g.defines;
472        }
473    
474        public ParsedPattern endGrammar(Location loc, Annotations anno) throws BuildException {
475          for (Enumeration en = defines.keys();
476               en.hasMoreElements();) {
477            String name = (String)en.nextElement();
478            RefPattern rp = (RefPattern)defines.get(name);
479            if (rp.getPattern() == null) {
480              sb.error("reference_to_undefined", name, rp.getRefLocator());
481              rp.setPattern(sb.pb.makeError());
482            }
483          }
484          Pattern start = startRef.getPattern();
485          if (start == null) {
486            sb.error("missing_start_element", (Locator)loc);
487            start = sb.pb.makeError();
488          }
489          return start;
490        }
491    
492        public void endDiv(Location loc, Annotations anno) throws BuildException {
493          // nothing to do
494        }
495    
496        public ParsedPattern endIncludedGrammar(Location loc, Annotations anno) throws BuildException {
497          return null;
498        }
499    
500        public void define(String name, GrammarSection.Combine combine, ParsedPattern pattern, Location loc, Annotations anno)
501                throws BuildException {
502          define(lookup(name), combine, pattern, loc);
503        }
504    
505        private void define(RefPattern rp, GrammarSection.Combine combine, ParsedPattern pattern, Location loc)
506                throws BuildException {
507          switch (rp.getReplacementStatus()) {
508          case RefPattern.REPLACEMENT_KEEP:
509            if (combine == null) {
510              if (rp.isCombineImplicit()) {
511                if (rp.getName() == null)
512                  sb.error("duplicate_start", (Locator)loc);
513                else
514                  sb.error("duplicate_define", rp.getName(), (Locator)loc);
515              }
516              else
517                rp.setCombineImplicit();
518            }
519            else {
520              byte combineType = (combine == COMBINE_CHOICE ? RefPattern.COMBINE_CHOICE : RefPattern.COMBINE_INTERLEAVE);
521              if (rp.getCombineType() != RefPattern.COMBINE_NONE
522                  && rp.getCombineType() != combineType) {
523                if (rp.getName() == null)
524                  sb.error("conflict_combine_start", (Locator)loc);
525                else
526                  sb.error("conflict_combine_define", rp.getName(), (Locator)loc);
527              }
528              rp.setCombineType(combineType);
529            }
530            Pattern p = (Pattern)pattern;
531            if (rp.getPattern() == null)
532              rp.setPattern(p);
533            else if (rp.getCombineType() == RefPattern.COMBINE_INTERLEAVE)
534              rp.setPattern(sb.pb.makeInterleave(rp.getPattern(), p));
535            else
536              rp.setPattern(sb.pb.makeChoice(rp.getPattern(), p));
537            break;
538          case RefPattern.REPLACEMENT_REQUIRE:
539            rp.setReplacementStatus(RefPattern.REPLACEMENT_IGNORE);
540            break;
541          case RefPattern.REPLACEMENT_IGNORE:
542            break;
543          }
544        }
545    
546        public void topLevelAnnotation(ParsedElementAnnotation ea) throws BuildException {
547        }
548    
549        public void topLevelComment(CommentList comments) throws BuildException {
550        }
551    
552        private RefPattern lookup(String name) {
553          if (name == START)
554            return startRef;
555          return lookup1(name);
556        }
557    
558        private RefPattern lookup1(String name) {
559          RefPattern p = (RefPattern)defines.get(name);
560          if (p == null) {
561            p = new RefPattern(name);
562            defines.put(name, p);
563          }
564          return p;
565        }
566    
567        public ParsedPattern makeRef(String name, Location loc, Annotations anno) throws BuildException {
568          RefPattern p = lookup1(name);
569          if (p.getRefLocator() == null && loc != null)
570            p.setRefLocator((Locator)loc);
571          return p;
572        }
573    
574        public ParsedPattern makeParentRef(String name, Location loc, Annotations anno) throws BuildException {
575          if (parent == null) {
576            sb.error("parent_ref_outside_grammar", (Locator)loc);
577            return sb.makeErrorPattern();
578          }
579          return parent.makeRef(name, loc, anno);
580        }
581    
582        public Div makeDiv() {
583          return this;
584        }
585    
586        public Include makeInclude() {
587          return new IncludeImpl(sb, this);
588        }
589    
590      }
591    
592      static class RootScope implements Scope {
593        private final SchemaBuilderImpl sb;
594        RootScope(SchemaBuilderImpl sb) {
595          this.sb = sb;
596        }
597    
598        public ParsedPattern makeParentRef(String name, Location loc, Annotations anno) throws BuildException {
599          sb.error("parent_ref_outside_grammar", (Locator)loc);
600          return sb.makeErrorPattern();
601        }
602        public ParsedPattern makeRef(String name, Location loc, Annotations anno) throws BuildException {
603          sb.error("ref_outside_grammar", (Locator)loc);
604          return sb.makeErrorPattern();
605        }
606    
607      }
608    
609      static class Override {
610        Override(RefPattern prp, Override next) {
611          this.prp = prp;
612          this.next = next;
613        }
614    
615        final RefPattern prp;
616        final Override next;
617        byte replacementStatus;
618      }
619    
620    
621      private static class IncludeImpl implements Include, Div {
622        private final SchemaBuilderImpl sb;
623        private Override overrides;
624        private final GrammarImpl grammar;
625    
626        private IncludeImpl(SchemaBuilderImpl sb, GrammarImpl grammar) {
627          this.sb = sb;
628          this.grammar = grammar;
629        }
630    
631        public void define(String name, GrammarSection.Combine combine, ParsedPattern pattern, Location loc, Annotations anno)
632                throws BuildException {
633          RefPattern rp = grammar.lookup(name);
634          overrides = new Override(rp, overrides);
635          grammar.define(rp, combine, pattern, loc);
636        }
637    
638        public void endDiv(Location loc, Annotations anno) throws BuildException {
639          // nothing to do
640        }
641    
642        public void topLevelAnnotation(ParsedElementAnnotation ea) throws BuildException {
643          // nothing to do
644        }
645    
646        public void topLevelComment(CommentList comments) throws BuildException {
647        }
648    
649        public Div makeDiv() {
650          return this;
651        }
652    
653        public void endInclude(String uri, String ns,
654                               Location loc, Annotations anno) throws BuildException {
655          for (OpenIncludes inc = sb.openIncludes;
656               inc != null;
657               inc = inc.parent) {
658            if (inc.uri.equals(uri)) {
659              sb.error("recursive_include", uri, (Locator)loc);
660              return;
661            }
662          }
663    
664          for (Override o = overrides; o != null; o = o.next) {
665            o.replacementStatus = o.prp.getReplacementStatus();
666            o.prp.setReplacementStatus(RefPattern.REPLACEMENT_REQUIRE);
667          }
668          try {
669            SchemaBuilderImpl isb = new SchemaBuilderImpl(ns, uri, sb);
670            sb.subParser.parseInclude(uri, isb, new GrammarImpl(isb, grammar));
671            for (Override o = overrides; o != null; o = o.next) {
672              if (o.prp.getReplacementStatus() == RefPattern.REPLACEMENT_REQUIRE) {
673                if (o.prp.getName() == null)
674                  sb.error("missing_start_replacement", (Locator)loc);
675                else
676                  sb.error("missing_define_replacement", o.prp.getName(), (Locator)loc);
677              }
678            }
679          }
680          catch (IllegalSchemaException e) {
681            sb.noteError();
682          }
683          finally {
684            for (Override o = overrides; o != null; o = o.next)
685              o.prp.setReplacementStatus(o.replacementStatus);
686          }
687        }
688    
689        public Include makeInclude() {
690          return null;
691        }
692      }
693    
694      public Grammar makeGrammar(Scope parent) {
695        return new GrammarImpl(this, parent);
696      }
697    
698      public ParsedPattern annotate(ParsedPattern p, Annotations anno) throws BuildException {
699        return p;
700      }
701    
702      public ParsedNameClass annotate(ParsedNameClass nc, Annotations anno) throws BuildException {
703        return nc;
704      }
705    
706      public ParsedPattern annotateAfter(ParsedPattern p, ParsedElementAnnotation e) throws BuildException {
707        return p;
708      }
709    
710      public ParsedNameClass annotateAfter(ParsedNameClass nc, ParsedElementAnnotation e) throws BuildException {
711        return nc;
712      }
713    
714      public ParsedPattern commentAfter(ParsedPattern p, CommentList comments) throws BuildException {
715        return p;
716      }
717    
718      public ParsedNameClass commentAfter(ParsedNameClass nc, CommentList comments) throws BuildException {
719        return nc;
720      }
721    
722      public ParsedPattern makeExternalRef(String uri, String ns, Scope scope,
723                                           Location loc, Annotations anno)
724              throws BuildException {
725        for (OpenIncludes inc = openIncludes;
726             inc != null;
727             inc = inc.parent) {
728          if (inc.uri.equals(uri)) {
729            error("recursive_include", uri, (Locator)loc);
730            return pb.makeError();
731          }
732        }
733        try {
734          return subParser.parseExternal(uri, new SchemaBuilderImpl(ns, uri, this), scope);
735        }
736        catch (IllegalSchemaException e) {
737          noteError();
738          return pb.makeError();
739        }
740      }
741    
742      public ParsedNameClass makeChoice(ParsedNameClass[] nameClasses, int nNameClasses, Location loc, Annotations anno) {
743        if (nNameClasses <= 0)
744          throw new IllegalArgumentException();
745        NameClass result = (NameClass)nameClasses[0];
746        for (int i = 1; i < nNameClasses; i++)
747          result = new ChoiceNameClass(result, (NameClass)nameClasses[i]);
748        return result;
749      }
750    
751      public ParsedNameClass makeName(String ns, String localName, String prefix, Location loc, Annotations anno) {
752        return new SimpleNameClass(new Name(resolveInherit(ns), localName));
753      }
754    
755      public ParsedNameClass makeNsName(String ns, Location loc, Annotations anno) {
756        return new NsNameClass(resolveInherit(ns));
757      }
758    
759      public ParsedNameClass makeNsName(String ns, ParsedNameClass except, Location loc, Annotations anno) {
760        return new NsNameExceptNameClass(resolveInherit(ns), (NameClass)except);
761      }
762    
763      public ParsedNameClass makeAnyName(Location loc, Annotations anno) {
764        return new AnyNameClass();
765      }
766    
767      public ParsedNameClass makeAnyName(ParsedNameClass except, Location loc, Annotations anno) {
768        return new AnyNameExceptNameClass((NameClass)except);
769      }
770    
771      private final String resolveInherit(String ns) {
772        if (ns == INHERIT_NS)
773          return inheritNs;
774        return ns;
775      }
776    
777      private class LocatorImpl implements Locator, Location {
778        private final String systemId;
779        private final int lineNumber;
780        private final int columnNumber;
781    
782        private LocatorImpl(String systemId, int lineNumber, int columnNumber) {
783          this.systemId = systemId;
784          this.lineNumber = lineNumber;
785          this.columnNumber = columnNumber;
786        }
787    
788        public String getPublicId() {
789          return null;
790        }
791    
792        public String getSystemId() {
793          return systemId;
794        }
795    
796        public int getLineNumber() {
797          return lineNumber;
798        }
799    
800        public int getColumnNumber() {
801          return columnNumber;
802        }
803      }
804    
805      public Location makeLocation(String systemId, int lineNumber, int columnNumber) {
806        return new LocatorImpl(systemId, lineNumber, columnNumber);
807      }
808    
809      public Annotations makeAnnotations(CommentList comments, Context context) {
810        return this;
811      }
812    
813      public ElementAnnotationBuilder makeElementAnnotationBuilder(String ns, String localName, String prefix,
814                                                                   Location loc, CommentList comments, Context context) {
815        return this;
816      }
817    
818      public CommentList makeCommentList() {
819        return this;
820      }
821    
822      public void addComment(String value, Location loc) throws BuildException {
823      }
824    
825      public void addAttribute(String ns, String localName, String prefix, String value, Location loc) {
826        // nothing needed
827      }
828    
829      public void addElement(ParsedElementAnnotation ea) {
830        // nothing needed
831      }
832    
833      public void addComment(CommentList comments) throws BuildException {
834        // nothing needed
835      }
836    
837      public void addLeadingComment(CommentList comments) throws BuildException {
838        // nothing needed
839      }
840    
841      public void addText(String value, Location loc, CommentList comments) {
842        // nothing needed
843      }
844    
845      public ParsedElementAnnotation makeElementAnnotation() {
846        return null;
847      }
848    
849      public boolean usesComments() {
850        return false;
851      }
852    
853      private void error(SAXParseException message) throws BuildException {
854        noteError();
855        try {
856          if (eh != null)
857            eh.error(message);
858        }
859        catch (SAXException e) {
860          throw new BuildException(e);
861        }
862      }
863    
864      /*
865      private void warning(SAXParseException message) throws BuildException {
866        try {
867          if (eh != null)
868            eh.warning(message);
869        }
870        catch (SAXException e) {
871          throw new BuildException(e);
872        }
873      }
874      */
875    
876      private void error(String key, Locator loc) throws BuildException {
877        error(new SAXParseException(localizer.message(key), loc));
878      }
879    
880      private void error(String key, String arg, Locator loc) throws BuildException {
881        error(new SAXParseException(localizer.message(key, arg), loc));
882      }
883    
884      private void error(String key, String arg1, String arg2, Locator loc) throws BuildException {
885        error(new SAXParseException(localizer.message(key, arg1, arg2), loc));
886      }
887    
888      private void error(String key, String arg1, String arg2, String arg3, Locator loc) throws BuildException {
889        error(new SAXParseException(localizer.message(key, new Object[]{arg1, arg2, arg3}), loc));
890      }
891      private void noteError() {
892        if (!hadError && parent != null)
893          parent.noteError();
894        hadError = true;
895      }
896    }