2 // Commons.Xml.Relaxng.RelaxngReader.cs
\r
5 // Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
\r
7 // 2003 Atsushi Enomoto "No rights reserved."
\r
9 // Copyright (c) 2004 Novell Inc.
\r
10 // All rights reserved
\r
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
\r
38 namespace Commons.Xml.Relaxng
\r
40 public class RelaxngReader : XmlDefaultReader
\r
43 static RelaxngPattern relaxngGrammar;
\r
44 static XmlReader relaxngXmlReader;
\r
45 static RelaxngReader ()
\r
47 // relaxngXmlReader = new XmlTextReader ("relaxng.rng");
\r
48 // relaxngGrammar = RelaxngPattern.Read (relaxngXmlReader);
\r
51 public static string RelaxngNS = "http://relaxng.org/ns/structure/1.0";
\r
52 // public static RelaxngPattern RelaxngGrammar {
\r
53 // get { return relaxngGrammar; }
\r
58 Stack nsStack = new Stack ();
\r
59 Stack datatypeLibraryStack = new Stack ();
\r
60 XmlResolver resolver = new XmlUrlResolver ();
\r
63 public RelaxngReader (XmlReader reader)
\r
64 : this (reader, null)
\r
68 public RelaxngReader (XmlReader reader, string ns)
\r
69 // : base (reader == relaxngXmlReader ? reader : new RelaxngValidatingReader (reader, relaxngGrammar))
\r
72 nsStack.Push (ns == null ? "" : ns);
\r
73 datatypeLibraryStack.Push ("");
\r
75 if (Reader.ReadState == ReadState.Initial)
\r
80 public XmlResolver XmlResolver {
\r
81 set { resolver = value; }
\r
84 internal XmlResolver Resolver {
\r
85 get { return resolver; }
\r
88 private void FillLocation (RelaxngElementBase el)
\r
90 el.BaseUri = BaseURI;
\r
91 IXmlLineInfo li = this as IXmlLineInfo;
\r
92 el.LineNumber = li != null ? li.LineNumber : 0;
\r
93 el.LinePosition = li != null ? li.LinePosition : 0;
\r
97 public override bool Read ()
\r
99 bool skipRead = false;
\r
102 if (IsEmptyElement) { // this should be done here
\r
104 datatypeLibraryStack.Pop ();
\r
108 b = Reader.Read ();
\r
111 switch (NodeType) {
\r
112 case XmlNodeType.ProcessingInstruction:
\r
113 case XmlNodeType.Comment:
\r
114 case XmlNodeType.EntityReference:
\r
116 case XmlNodeType.Whitespace:
\r
117 // Skip whitespaces except for data and param.
\r
118 case XmlNodeType.SignificantWhitespace:
\r
119 if (LocalName != "value" && LocalName != "param") {
\r
126 if (NamespaceURI != RelaxngNS) {
\r
134 } while (b && loop);
\r
136 switch (NodeType) {
\r
137 case XmlNodeType.Element:
\r
138 if (MoveToAttribute ("ns"))
\r
139 nsStack.Push (Value.Trim ());
\r
141 nsStack.Push (nsStack.Peek ());
\r
143 if (MoveToAttribute ("datatypeLibrary")) {
\r
144 string uriString = Value.Trim ();
\r
145 if (uriString.Length == 0)
\r
146 datatypeLibraryStack.Push (String.Empty);
\r
149 Uri uri = new Uri (uriString);
\r
150 // MS.NET Uri is too lamespec
\r
151 datatypeLibraryStack.Push (uri.ToString ());
\r
152 } catch (UriFormatException ex) {
\r
153 throw new RelaxngException (ex.Message, ex);
\r
158 datatypeLibraryStack.Push (datatypeLibraryStack.Peek ());
\r
162 case XmlNodeType.EndElement:
\r
164 datatypeLibraryStack.Pop ();
\r
173 public string ContextNamespace {
\r
174 get { return nsStack.Peek () as string; }
\r
177 public string DatatypeLibrary {
\r
178 get { return datatypeLibraryStack.Peek () as string; }
\r
181 // Utility methods.
\r
182 private void expect (string name)
\r
184 if (NamespaceURI != RelaxngGrammar.NamespaceURI)
\r
185 throw new RelaxngException (String.Format ("Invalid document: expected namespace {0} but found {1}", RelaxngGrammar.NamespaceURI, NamespaceURI));
\r
186 else if (LocalName != name)
\r
187 throw new RelaxngException (String.Format ("Invalid document: expected local name {0} but found {1}", name, LocalName));
\r
190 private void expectEnd (string name)
\r
192 if (NodeType != XmlNodeType.EndElement)
\r
193 throw new RelaxngException (String.Format ("Expected EndElement but found {0}.", NodeType));
\r
199 // Other than name class and pattern.
\r
200 private RelaxngStart ReadStart ()
\r
202 RelaxngStart s = new RelaxngStart ();
\r
206 if (MoveToFirstAttribute ()) {
\r
208 if (NamespaceURI != String.Empty)
\r
210 switch (LocalName) {
\r
211 case "datatypeLibrary":
\r
215 throw new RelaxngException ("Invalid attribute.");
\r
217 } while (MoveToNextAttribute ());
\r
221 if (MoveToAttribute ("combine")) {
\r
222 s.Combine = Value.Trim ();
\r
223 if (s.Combine != "choice" && s.Combine != "interleave")
\r
224 throw new RelaxngException ("Invalid combine attribute: " + s.Combine);
\r
229 s.Pattern = ReadPattern ();
\r
230 expectEnd ("start");
\r
234 private string GetNameAttribute ()
\r
236 string name = GetSpaceStrippedAttribute ("name");
\r
238 throw new RelaxngException ("Required attribute name is not found.");
\r
239 return XmlConvert.VerifyNCName (name);
\r
242 private string GetSpaceStrippedAttribute (string name)
\r
244 string v = GetAttribute (name);
\r
245 return v != null ? v.Trim () : null;
\r
248 private RelaxngDefine ReadDefine ()
\r
250 RelaxngDefine def = new RelaxngDefine ();
\r
251 FillLocation (def);
\r
253 def.Name = GetNameAttribute ();
\r
254 def.Combine = GetSpaceStrippedAttribute ("combine");
\r
257 while (NodeType == XmlNodeType.Element)
\r
258 def.Patterns.Add (ReadPattern ());
\r
259 expectEnd ("define");
\r
263 private RelaxngParam ReadParam ()
\r
265 RelaxngParam p = new RelaxngParam ();
\r
268 p.Name = GetNameAttribute ();
\r
269 p.Value = ReadString ().Trim ();
\r
270 expectEnd ("param");
\r
274 // NameClass reader (only if it is element-style.)
\r
275 private RelaxngNameClass ReadNameClass ()
\r
277 switch (LocalName) {
\r
279 return ReadNameClassName ();
\r
281 return ReadNameClassAnyName ();
\r
283 return ReadNameClassNsName ();
\r
285 return ReadNameClassChoice ();
\r
287 throw new RelaxngException ("Invalid name class: " + LocalName);
\r
290 private RelaxngName ReadNameClassName ()
\r
292 string name = ReadString ().Trim ();
\r
293 RelaxngName rName = resolvedName (name);
\r
294 expectEnd ("name");
\r
298 private RelaxngAnyName ReadNameClassAnyName ()
\r
300 RelaxngAnyName an = new RelaxngAnyName ();
\r
302 if (!IsEmptyElement) {
\r
304 if (NodeType == XmlNodeType.EndElement) {
\r
309 an.Except = new RelaxngExceptNameClass ();
\r
310 FillLocation (an.Except);
\r
311 while (NodeType == XmlNodeType.Element)
\r
312 an.Except.Names.Add (
\r
314 expectEnd ("except");
\r
316 expectEnd ("anyName");
\r
322 private RelaxngNsName ReadNameClassNsName ()
\r
324 RelaxngNsName nn = new RelaxngNsName ();
\r
326 nn.Namespace = this.ContextNamespace;
\r
327 if (!IsEmptyElement) {
\r
329 if (NodeType == XmlNodeType.EndElement) {
\r
334 nn.Except = ReadNameClassExcept ();//new RelaxngExceptNameClass ();
\r
335 FillLocation (nn.Except);
\r
337 expectEnd ("nsName");
\r
343 private RelaxngNameChoice ReadNameClassChoice ()
\r
345 RelaxngNameChoice nc = new RelaxngNameChoice ();
\r
347 if (IsEmptyElement)
\r
348 throw new RelaxngException ("Name choice must have at least one name class.");
\r
351 while (NodeType != XmlNodeType.EndElement) {
\r
352 nc.Children.Add (ReadNameClass ());
\r
354 if (nc.Children.Count == 0)
\r
355 throw new RelaxngException ("Name choice must have at least one name class.");
\r
357 expectEnd ("choice");
\r
361 private RelaxngExceptNameClass ReadNameClassExcept ()
\r
363 RelaxngExceptNameClass x = new RelaxngExceptNameClass ();
\r
365 if (IsEmptyElement)
\r
366 throw new RelaxngException ("Name choice must have at least one name class.");
\r
369 while (NodeType != XmlNodeType.EndElement)
\r
370 x.Names.Add (ReadNameClass ());
\r
371 if (x.Names.Count == 0)
\r
372 throw new RelaxngException ("Name choice must have at least one name class.");
\r
374 expectEnd ("except");
\r
380 public RelaxngPattern ReadPattern ()
\r
382 while (NodeType != XmlNodeType.Element)
\r
386 switch (LocalName) {
\r
388 return ReadElementPattern ();
\r
390 return ReadAttributePattern ();
\r
392 return ReadGroupPattern ();
\r
394 return ReadInterleavePattern ();
\r
396 return ReadChoicePattern ();
\r
398 return ReadOptionalPattern ();
\r
400 return ReadZeroOrMorePattern ();
\r
402 return ReadOneOrMorePattern ();
\r
404 return ReadListPattern ();
\r
406 return ReadMixedPattern ();
\r
408 return ReadRefPattern ();
\r
410 return ReadParentRefPattern ();
\r
412 return ReadEmptyPattern ();
\r
414 return ReadTextPattern ();
\r
416 return ReadDataPattern ();
\r
418 return ReadValuePattern ();
\r
420 return ReadNotAllowedPattern ();
\r
421 case "externalRef":
\r
422 return ReadExternalRefPattern ();
\r
424 return ReadGrammarPattern ();
\r
426 throw new RelaxngException ("Non-supported pattern specification: " + LocalName);
\r
429 private void ReadPatterns (RelaxngSingleContentPattern el)
\r
432 el.Patterns.Add (ReadPattern ());
\r
433 } while (NodeType == XmlNodeType.Element);
\r
436 private void ReadPatterns (RelaxngBinaryContentPattern el)
\r
439 el.Patterns.Add (ReadPattern ());
\r
440 } while (NodeType == XmlNodeType.Element);
\r
443 private RelaxngExcept ReadPatternExcept ()
\r
445 RelaxngExcept x = new RelaxngExcept ();
\r
447 if (IsEmptyElement)
\r
448 throw new RelaxngException ("'except' must have at least one pattern.");
\r
450 while (NodeType != XmlNodeType.EndElement)
\r
451 x.Patterns.Add (ReadPattern ());
\r
452 if (x.Patterns.Count == 0)
\r
453 throw new RelaxngException ("'except' must have at least one pattern.");
\r
455 expectEnd ("except");
\r
459 private RelaxngInclude ReadInclude ()
\r
461 RelaxngInclude i = new RelaxngInclude ();
\r
463 expect ("include");
\r
464 i.NSContext = ContextNamespace;
\r
465 string href = GetSpaceStrippedAttribute ("href");
\r
467 throw new RelaxngException ("Required attribute href was not found.");
\r
468 XmlResolver res = resolver != null ? resolver : new XmlUrlResolver ();
\r
469 i.Href = res.ResolveUri (BaseURI != null ? new Uri (BaseURI) : null, href).AbsoluteUri;
\r
470 if (!IsEmptyElement) {
\r
472 this.readGrammarIncludeContent (i.Starts, i.Defines, i.Divs, null);
\r
473 expectEnd ("include");
\r
480 private void readGrammarIncludeContent (IList starts, IList defines, IList divs, IList includes)
\r
482 while (NodeType == XmlNodeType.Element) {
\r
483 switch (LocalName) {
\r
485 starts.Add (ReadStart ());
\r
488 defines.Add (ReadDefine ());
\r
491 divs.Add (ReadDiv (includes != null));
\r
494 if (includes != null)
\r
495 includes.Add (ReadInclude ());
\r
497 throw new RelaxngException ("Unexpected content: " + Name);
\r
500 throw new RelaxngException ("Unexpected content: " + Name);
\r
505 private RelaxngDiv ReadDiv (bool allowIncludes)
\r
508 RelaxngDiv div = new RelaxngDiv ();
\r
509 FillLocation (div);
\r
510 if (!IsEmptyElement) {
\r
512 readGrammarIncludeContent (div.Starts, div.Defines, div.Divs, div.Includes);
\r
520 private RelaxngName resolvedName (string nameSpec)
\r
522 int colonAt = nameSpec.IndexOf (':');
\r
523 string prefix = (colonAt < 0) ? "" : nameSpec.Substring (0, colonAt);
\r
524 string local = (colonAt < 0) ? nameSpec : nameSpec.Substring (colonAt + 1, nameSpec.Length - colonAt - 1);
\r
525 string uri = ContextNamespace;
\r
527 if (prefix != "") {
\r
528 uri = LookupNamespace (prefix);
\r
530 throw new RelaxngException ("Undeclared prefix in name component: " + nameSpec);
\r
532 RelaxngName n = new RelaxngName (local, uri);
\r
537 private RelaxngElement ReadElementPattern ()
\r
539 RelaxngElement el = new RelaxngElement ();
\r
542 if (MoveToFirstAttribute ()) {
\r
544 if (NamespaceURI != String.Empty)
\r
546 switch (LocalName) {
\r
547 case "datatypeLibrary":
\r
552 throw new RelaxngException ("Invalid attribute.");
\r
554 } while (MoveToNextAttribute ());
\r
558 // try to get name from attribute.
\r
559 if (MoveToAttribute ("name"))
\r
560 el.NameClass = resolvedName (XmlConvert.VerifyName (Value.Trim ()));
\r
564 // read nameClass from content.
\r
565 if (el.NameClass == null)
\r
566 el.NameClass = ReadNameClass ();
\r
569 this.ReadPatterns (el);
\r
571 expectEnd ("element");
\r
573 if (el.NameClass == null)
\r
574 throw new RelaxngException ("Name class was not specified.");
\r
578 private RelaxngAttribute ReadAttributePattern ()
\r
580 RelaxngAttribute attr = new RelaxngAttribute ();
\r
581 FillLocation (attr);
\r
583 if (MoveToFirstAttribute ()) {
\r
585 if (NamespaceURI != String.Empty)
\r
587 switch (LocalName) {
\r
588 case "datatypeLibrary":
\r
593 throw new RelaxngException ("Invalid attribute.");
\r
595 } while (MoveToNextAttribute ());
\r
599 string ns = GetSpaceStrippedAttribute ("ns");
\r
601 // try to get name from attribute.
\r
602 if (MoveToAttribute ("name")) {
\r
603 // attr.NameClass = resolvedName (XmlConvert.VerifyName (Value.Trim ()), false);
\r
604 RelaxngName nc = new RelaxngName ();
\r
605 string name = XmlConvert.VerifyName (Value.Trim ());
\r
606 if (name.IndexOf (':') > 0)
\r
607 nc = resolvedName (name);
\r
609 nc.LocalName = name;
\r
610 nc.Namespace = ns == null ? String.Empty : ns;
\r
612 attr.NameClass = nc;
\r
616 if (!IsEmptyElement) {
\r
618 // read nameClass from content.
\r
619 if (attr.NameClass == null)
\r
620 attr.NameClass = ReadNameClass ();
\r
622 if (NodeType == XmlNodeType.Element)
\r
623 attr.Pattern = ReadPattern ();
\r
625 expectEnd ("attribute");
\r
629 if (attr.NameClass == null)
\r
630 throw new RelaxngException ("Name class was not specified.");
\r
634 private RelaxngGrammar ReadGrammarPattern ()
\r
636 RelaxngGrammar grammar = new RelaxngGrammar ();
\r
637 FillLocation (grammar);
\r
639 this.readGrammarIncludeContent (grammar.Starts, grammar.Defines, grammar.Divs, grammar.Includes);
\r
640 expectEnd ("grammar");
\r
645 private RelaxngRef ReadRefPattern ()
\r
647 RelaxngRef r = new RelaxngRef ();
\r
650 r.Name = GetNameAttribute ();
\r
651 if (!IsEmptyElement) {
\r
660 private RelaxngExternalRef ReadExternalRefPattern ()
\r
662 RelaxngExternalRef r = new RelaxngExternalRef ();
\r
664 expect ("externalRef");
\r
665 string href = GetSpaceStrippedAttribute ("href");
\r
667 throw new RelaxngException ("Required attribute href was not found.");
\r
668 XmlResolver res = resolver != null ? resolver : new XmlUrlResolver ();
\r
669 r.Href = res.ResolveUri (BaseURI != null ? new Uri (BaseURI) : null, href).AbsoluteUri;
\r
670 r.NSContext = ContextNamespace;
\r
671 if (!IsEmptyElement) {
\r
673 expectEnd ("externalRef");
\r
680 private RelaxngParentRef ReadParentRefPattern ()
\r
682 RelaxngParentRef r = new RelaxngParentRef ();
\r
684 expect ("parentRef");
\r
685 r.Name = GetNameAttribute ();
\r
686 if (!IsEmptyElement) {
\r
688 expectEnd ("parentRef");
\r
695 private RelaxngEmpty ReadEmptyPattern ()
\r
699 if (MoveToFirstAttribute ()) {
\r
701 if (NamespaceURI == String.Empty && LocalName != "datatypeLibrary")
\r
702 throw new RelaxngException ("Invalid attribute.");
\r
703 } while (MoveToNextAttribute ());
\r
707 if (!IsEmptyElement) {
\r
709 expectEnd ("empty");
\r
714 RelaxngEmpty empty = new RelaxngEmpty ();
\r
715 FillLocation (empty);
\r
719 private RelaxngText ReadTextPattern ()
\r
723 if (MoveToFirstAttribute ()) {
\r
725 if (NamespaceURI == String.Empty && LocalName != "datatypeLibrary")
\r
726 throw new RelaxngException ("Invalid attribute.");
\r
727 } while (MoveToNextAttribute ());
\r
731 if (!IsEmptyElement) {
\r
733 expectEnd ("text");
\r
738 RelaxngText t = new RelaxngText ();
\r
743 private RelaxngData ReadDataPattern ()
\r
745 RelaxngData data = new RelaxngData ();
\r
746 FillLocation (data);
\r
749 data.Type = GetSpaceStrippedAttribute ("type");
\r
750 if (data.Type == null)
\r
751 throw new RelaxngException ("Attribute type is required.");
\r
752 data.DatatypeLibrary = DatatypeLibrary;
\r
754 if (MoveToFirstAttribute ()) {
\r
756 if (NamespaceURI != String.Empty)
\r
758 switch (LocalName) {
\r
759 case "datatypeLibrary":
\r
763 throw new RelaxngException ("Invalid attribute.");
\r
765 } while (MoveToNextAttribute ());
\r
769 if (!IsEmptyElement) {
\r
771 while (Name == "param") {
\r
772 data.ParamList.Add (ReadParam ());
\r
774 if (LocalName == "except")
\r
775 data.Except = ReadPatternExcept ();
\r
776 expectEnd ("data");
\r
783 private RelaxngValue ReadValuePattern ()
\r
785 RelaxngValue v = new RelaxngValue ();
\r
789 if (MoveToFirstAttribute ()) {
\r
791 if (NamespaceURI != String.Empty)
\r
793 switch (LocalName) {
\r
794 case "datatypeLibrary":
\r
799 throw new RelaxngException ("Invalid attribute.");
\r
801 } while (MoveToNextAttribute ());
\r
805 if (MoveToAttribute ("type")) {
\r
806 v.Type = Value.Trim ();
\r
807 v.DatatypeLibrary = DatatypeLibrary;
\r
810 v.DatatypeLibrary = "";
\r
812 // v.Namespace = GetSpaceStrippedAttribute ("ns");
\r
814 if (IsEmptyElement) {
\r
815 v.Value = String.Empty;
\r
818 v.Value = ReadString ();
\r
819 expectEnd ("value");
\r
825 private RelaxngList ReadListPattern ()
\r
827 RelaxngList list = new RelaxngList ();
\r
828 FillLocation (list);
\r
831 ReadPatterns (list);
\r
832 expectEnd ("list");
\r
836 private RelaxngOneOrMore ReadOneOrMorePattern ()
\r
838 RelaxngOneOrMore o = new RelaxngOneOrMore ();
\r
840 expect ("oneOrMore");
\r
843 expectEnd ("oneOrMore");
\r
847 private RelaxngZeroOrMore ReadZeroOrMorePattern ()
\r
849 RelaxngZeroOrMore o = new RelaxngZeroOrMore ();
\r
851 expect ("zeroOrMore");
\r
854 expectEnd ("zeroOrMore");
\r
858 private RelaxngOptional ReadOptionalPattern ()
\r
860 RelaxngOptional o = new RelaxngOptional ();
\r
862 expect ("optional");
\r
865 expectEnd ("optional");
\r
869 private RelaxngMixed ReadMixedPattern ()
\r
871 RelaxngMixed o = new RelaxngMixed ();
\r
876 expectEnd ("mixed");
\r
880 private RelaxngGroup ReadGroupPattern ()
\r
882 RelaxngGroup g = new RelaxngGroup ();
\r
887 expectEnd ("group");
\r
891 private RelaxngInterleave ReadInterleavePattern ()
\r
893 RelaxngInterleave i = new RelaxngInterleave ();
\r
895 expect ("interleave");
\r
898 expectEnd ("interleave");
\r
902 private RelaxngChoice ReadChoicePattern ()
\r
904 RelaxngChoice c = new RelaxngChoice ();
\r
909 expectEnd ("choice");
\r
913 private RelaxngNotAllowed ReadNotAllowedPattern ()
\r
915 expect ("notAllowed");
\r
916 if (!IsEmptyElement) {
\r
918 expectEnd ("notAllowed");
\r
922 RelaxngNotAllowed na = new RelaxngNotAllowed ();
\r