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 grammarForRelaxng;
\r
44 static XmlReader relaxngXmlReader;
\r
45 static RelaxngReader ()
\r
47 relaxngXmlReader = new XmlTextReader (typeof (RelaxngReader).Assembly.GetManifestResourceStream ("relaxng.rng"));
\r
49 RelaxngPattern.Read (relaxngXmlReader);
\r
52 [Obsolete] // incorrectly introduced
\r
53 public static string RelaxngNS = "http://relaxng.org/ns/structure/1.0";
\r
54 public static RelaxngPattern GrammarForRelaxng {
\r
55 get { return grammarForRelaxng; }
\r
60 Stack nsStack = new Stack ();
\r
61 Stack datatypeLibraryStack = new Stack ();
\r
62 XmlResolver resolver = new XmlUrlResolver ();
\r
63 // ArrayList annotationNamespaces = new ArrayList ();
\r
66 public RelaxngReader (XmlReader reader)
\r
67 : this (reader, null)
\r
71 public RelaxngReader (XmlReader reader, string ns)
\r
72 // : base (grammarForRelaxng == null ? reader : new RelaxngValidatingReader (reader, grammarForRelaxng))
\r
75 if (Reader.ReadState == ReadState.Initial)
\r
78 string nsval = GetSpaceStrippedAttribute ("ns", String.Empty);
\r
81 nsStack.Push (nsval == null ? String.Empty : nsval);
\r
82 string dtlib = GetSpaceStrippedAttribute ("datatypeLibrary", String.Empty);
\r
83 datatypeLibraryStack.Push (dtlib != null ?
\r
84 dtlib : String.Empty);
\r
87 public XmlResolver XmlResolver {
\r
88 set { resolver = value; }
\r
91 internal XmlResolver Resolver {
\r
92 get { return resolver; }
\r
95 private void FillLocation (RelaxngElementBase el)
\r
97 el.BaseUri = BaseURI;
\r
98 IXmlLineInfo li = this as IXmlLineInfo;
\r
99 el.LineNumber = li != null ? li.LineNumber : 0;
\r
100 el.LinePosition = li != null ? li.LinePosition : 0;
\r
104 public void AddAnnotationNamespace (string ns)
\r
106 if (!annotationNamespaces.Contains (ns))
\r
107 annotationNamespaces.Add (ns);
\r
112 public override bool Read ()
\r
114 bool skipRead = false;
\r
117 if (IsEmptyElement) { // this should be done here
\r
119 datatypeLibraryStack.Pop ();
\r
123 b = Reader.Read ();
\r
126 switch (NodeType) {
\r
127 case XmlNodeType.ProcessingInstruction:
\r
128 case XmlNodeType.Comment:
\r
129 case XmlNodeType.EntityReference:
\r
131 case XmlNodeType.Whitespace:
\r
132 // Skip whitespaces except for data and param.
\r
133 case XmlNodeType.SignificantWhitespace:
\r
134 if (LocalName != "value" && LocalName != "param") {
\r
141 if (NamespaceURI != RelaxngGrammar.NamespaceURI) {
\r
149 } while (b && loop);
\r
151 switch (NodeType) {
\r
152 case XmlNodeType.Element:
\r
153 if (MoveToAttribute ("ns")) {
\r
154 nsStack.Push (Value.Trim ());
\r
158 nsStack.Push (ContextNamespace);
\r
160 if (MoveToAttribute ("datatypeLibrary")) {
\r
161 string uriString = Value.Trim ();
\r
162 if (uriString.Length == 0)
\r
163 datatypeLibraryStack.Push (String.Empty);
\r
166 Uri uri = new Uri (uriString);
\r
167 // MS.NET Uri is too lamespec
\r
168 datatypeLibraryStack.Push (uri.ToString ());
\r
169 } catch (UriFormatException ex) {
\r
170 throw new RelaxngException (ex.Message, ex);
\r
176 datatypeLibraryStack.Push (DatatypeLibrary);
\r
179 case XmlNodeType.EndElement:
\r
181 datatypeLibraryStack.Pop ();
\r
190 public string ContextNamespace {
\r
192 if (nsStack.Count == 0)
\r
193 // It happens only on initialization.
\r
194 return String.Empty;
\r
195 return nsStack.Peek () as string;
\r
199 public string DatatypeLibrary {
\r
201 if (datatypeLibraryStack.Count == 0)
\r
202 // It happens only on initialization.
\r
203 return String.Empty;
\r
204 return datatypeLibraryStack.Peek () as string;
\r
208 // Utility methods.
\r
209 private void expect (string name)
\r
211 if (NamespaceURI != RelaxngGrammar.NamespaceURI)
\r
212 throw new RelaxngException (String.Format ("Invalid document: expected namespace {0} but found {1}", RelaxngGrammar.NamespaceURI, NamespaceURI));
\r
213 else if (LocalName != name)
\r
214 throw new RelaxngException (String.Format ("Invalid document: expected local name {0} but found {1}", name, LocalName));
\r
217 private void expectEnd (string name)
\r
219 if (NodeType != XmlNodeType.EndElement)
\r
220 throw new RelaxngException (String.Format ("Expected EndElement but found {0}.", NodeType));
\r
226 // Other than name class and pattern.
\r
227 private RelaxngStart ReadStart ()
\r
229 RelaxngStart s = new RelaxngStart ();
\r
233 if (MoveToFirstAttribute ()) {
\r
235 if (NamespaceURI != String.Empty)
\r
237 switch (LocalName) {
\r
238 case "datatypeLibrary":
\r
242 throw new RelaxngException ("Invalid attribute.");
\r
244 } while (MoveToNextAttribute ());
\r
248 if (MoveToAttribute ("combine")) {
\r
249 s.Combine = Value.Trim ();
\r
250 if (s.Combine != "choice" && s.Combine != "interleave")
\r
251 throw new RelaxngException ("Invalid combine attribute: " + s.Combine);
\r
256 s.Pattern = ReadPattern ();
\r
257 expectEnd ("start");
\r
261 private string GetNameAttribute ()
\r
263 string name = GetSpaceStrippedAttribute ("name", String.Empty);
\r
265 throw new RelaxngException ("Required attribute name is not found.");
\r
266 return XmlConvert.VerifyNCName (name);
\r
269 private string GetSpaceStrippedAttribute (string name, string ns)
\r
271 string v = GetAttribute (name, ns);
\r
272 return v != null ? v.Trim () : null;
\r
275 private RelaxngDefine ReadDefine ()
\r
277 RelaxngDefine def = new RelaxngDefine ();
\r
278 FillLocation (def);
\r
280 def.Name = GetNameAttribute ();
\r
281 def.Combine = GetSpaceStrippedAttribute ("combine", String.Empty);
\r
284 while (NodeType == XmlNodeType.Element)
\r
285 def.Patterns.Add (ReadPattern ());
\r
286 expectEnd ("define");
\r
290 private RelaxngParam ReadParam ()
\r
292 RelaxngParam p = new RelaxngParam ();
\r
295 p.Name = GetNameAttribute ();
\r
296 p.Value = ReadString ().Trim ();
\r
297 expectEnd ("param");
\r
301 // NameClass reader (only if it is element-style.)
\r
302 private RelaxngNameClass ReadNameClass ()
\r
304 switch (LocalName) {
\r
306 return ReadNameClassName ();
\r
308 return ReadNameClassAnyName ();
\r
310 return ReadNameClassNsName ();
\r
312 return ReadNameClassChoice ();
\r
314 throw new RelaxngException ("Invalid name class: " + LocalName);
\r
317 private RelaxngName ReadNameClassName ()
\r
319 string name = ReadString ().Trim ();
\r
320 RelaxngName rName = resolvedName (name);
\r
321 expectEnd ("name");
\r
325 private RelaxngAnyName ReadNameClassAnyName ()
\r
327 RelaxngAnyName an = new RelaxngAnyName ();
\r
329 if (!IsEmptyElement) {
\r
331 if (NodeType == XmlNodeType.EndElement) {
\r
336 an.Except = new RelaxngExceptNameClass ();
\r
337 FillLocation (an.Except);
\r
338 while (NodeType == XmlNodeType.Element)
\r
339 an.Except.Names.Add (
\r
341 expectEnd ("except");
\r
343 expectEnd ("anyName");
\r
349 private RelaxngNsName ReadNameClassNsName ()
\r
351 RelaxngNsName nn = new RelaxngNsName ();
\r
353 nn.Namespace = this.ContextNamespace;
\r
354 if (!IsEmptyElement) {
\r
356 if (NodeType == XmlNodeType.EndElement) {
\r
361 nn.Except = ReadNameClassExcept ();//new RelaxngExceptNameClass ();
\r
362 FillLocation (nn.Except);
\r
364 expectEnd ("nsName");
\r
370 private RelaxngNameChoice ReadNameClassChoice ()
\r
372 RelaxngNameChoice nc = new RelaxngNameChoice ();
\r
374 if (IsEmptyElement)
\r
375 throw new RelaxngException ("Name choice must have at least one name class.");
\r
378 while (NodeType != XmlNodeType.EndElement) {
\r
379 nc.Children.Add (ReadNameClass ());
\r
381 if (nc.Children.Count == 0)
\r
382 throw new RelaxngException ("Name choice must have at least one name class.");
\r
384 expectEnd ("choice");
\r
388 private RelaxngExceptNameClass ReadNameClassExcept ()
\r
390 RelaxngExceptNameClass x = new RelaxngExceptNameClass ();
\r
392 if (IsEmptyElement)
\r
393 throw new RelaxngException ("Name choice must have at least one name class.");
\r
396 while (NodeType != XmlNodeType.EndElement)
\r
397 x.Names.Add (ReadNameClass ());
\r
398 if (x.Names.Count == 0)
\r
399 throw new RelaxngException ("Name choice must have at least one name class.");
\r
401 expectEnd ("except");
\r
407 public RelaxngPattern ReadPattern ()
\r
409 while (NodeType != XmlNodeType.Element)
\r
413 switch (LocalName) {
\r
415 return ReadElementPattern ();
\r
417 return ReadAttributePattern ();
\r
419 return ReadGroupPattern ();
\r
421 return ReadInterleavePattern ();
\r
423 return ReadChoicePattern ();
\r
425 return ReadOptionalPattern ();
\r
427 return ReadZeroOrMorePattern ();
\r
429 return ReadOneOrMorePattern ();
\r
431 return ReadListPattern ();
\r
433 return ReadMixedPattern ();
\r
435 return ReadRefPattern ();
\r
437 return ReadParentRefPattern ();
\r
439 return ReadEmptyPattern ();
\r
441 return ReadTextPattern ();
\r
443 return ReadDataPattern ();
\r
445 return ReadValuePattern ();
\r
447 return ReadNotAllowedPattern ();
\r
448 case "externalRef":
\r
449 return ReadExternalRefPattern ();
\r
451 return ReadGrammarPattern ();
\r
453 throw new RelaxngException ("Non-supported pattern specification: " + LocalName);
\r
456 private void ReadPatterns (RelaxngSingleContentPattern el)
\r
459 el.Patterns.Add (ReadPattern ());
\r
460 } while (NodeType == XmlNodeType.Element);
\r
463 private void ReadPatterns (RelaxngBinaryContentPattern el)
\r
466 el.Patterns.Add (ReadPattern ());
\r
467 } while (NodeType == XmlNodeType.Element);
\r
470 private RelaxngExcept ReadPatternExcept ()
\r
472 RelaxngExcept x = new RelaxngExcept ();
\r
474 if (IsEmptyElement)
\r
475 throw new RelaxngException ("'except' must have at least one pattern.");
\r
477 while (NodeType != XmlNodeType.EndElement)
\r
478 x.Patterns.Add (ReadPattern ());
\r
479 if (x.Patterns.Count == 0)
\r
480 throw new RelaxngException ("'except' must have at least one pattern.");
\r
482 expectEnd ("except");
\r
486 private RelaxngInclude ReadInclude ()
\r
488 RelaxngInclude i = new RelaxngInclude ();
\r
490 expect ("include");
\r
491 i.NSContext = ContextNamespace;
\r
492 string href = GetSpaceStrippedAttribute ("href", String.Empty);
\r
494 throw new RelaxngException ("Required attribute href was not found.");
\r
495 XmlResolver res = resolver != null ? resolver : new XmlUrlResolver ();
\r
496 i.Href = res.ResolveUri (BaseURI != null ? new Uri (BaseURI) : null, href).AbsoluteUri;
\r
497 if (!IsEmptyElement) {
\r
499 this.readGrammarIncludeContent (i.Starts, i.Defines, i.Divs, null);
\r
500 expectEnd ("include");
\r
507 private void readGrammarIncludeContent (IList starts, IList defines, IList divs, IList includes)
\r
509 while (NodeType == XmlNodeType.Element) {
\r
510 switch (LocalName) {
\r
512 starts.Add (ReadStart ());
\r
515 defines.Add (ReadDefine ());
\r
518 divs.Add (ReadDiv (includes != null));
\r
521 if (includes != null)
\r
522 includes.Add (ReadInclude ());
\r
524 throw new RelaxngException ("Unexpected content: " + Name);
\r
527 throw new RelaxngException ("Unexpected content: " + Name);
\r
532 private RelaxngDiv ReadDiv (bool allowIncludes)
\r
535 RelaxngDiv div = new RelaxngDiv ();
\r
536 FillLocation (div);
\r
537 if (!IsEmptyElement) {
\r
539 readGrammarIncludeContent (div.Starts, div.Defines, div.Divs, div.Includes);
\r
547 private RelaxngName resolvedName (string nameSpec)
\r
549 int colonAt = nameSpec.IndexOf (':');
\r
550 string prefix = (colonAt < 0) ? "" : nameSpec.Substring (0, colonAt);
\r
551 string local = (colonAt < 0) ? nameSpec : nameSpec.Substring (colonAt + 1, nameSpec.Length - colonAt - 1);
\r
552 string uri = ContextNamespace;
\r
554 if (prefix != "") {
\r
555 uri = LookupNamespace (prefix);
\r
557 throw new RelaxngException ("Undeclared prefix in name component: " + nameSpec);
\r
559 RelaxngName n = new RelaxngName (local, uri);
\r
564 private RelaxngElement ReadElementPattern ()
\r
566 RelaxngElement el = new RelaxngElement ();
\r
569 if (MoveToFirstAttribute ()) {
\r
571 if (NamespaceURI != String.Empty)
\r
573 switch (LocalName) {
\r
574 case "datatypeLibrary":
\r
579 throw new RelaxngException ("Invalid attribute.");
\r
581 } while (MoveToNextAttribute ());
\r
585 // try to get name from attribute.
\r
586 if (MoveToAttribute ("name"))
\r
587 el.NameClass = resolvedName (XmlConvert.VerifyName (Value.Trim ()));
\r
591 // read nameClass from content.
\r
592 if (el.NameClass == null)
\r
593 el.NameClass = ReadNameClass ();
\r
596 this.ReadPatterns (el);
\r
598 expectEnd ("element");
\r
600 if (el.NameClass == null)
\r
601 throw new RelaxngException ("Name class was not specified.");
\r
605 private RelaxngAttribute ReadAttributePattern ()
\r
607 RelaxngAttribute attr = new RelaxngAttribute ();
\r
608 FillLocation (attr);
\r
610 if (MoveToFirstAttribute ()) {
\r
612 if (NamespaceURI != String.Empty)
\r
614 switch (LocalName) {
\r
615 case "datatypeLibrary":
\r
620 throw new RelaxngException ("Invalid attribute.");
\r
622 } while (MoveToNextAttribute ());
\r
626 string ns = GetSpaceStrippedAttribute ("ns", String.Empty);
\r
628 // try to get name from attribute.
\r
629 if (MoveToAttribute ("name", String.Empty)) {
\r
630 // attr.NameClass = resolvedName (XmlConvert.VerifyName (Value.Trim ()), false);
\r
631 RelaxngName nc = new RelaxngName ();
\r
632 string name = XmlConvert.VerifyName (Value.Trim ());
\r
633 if (name.IndexOf (':') > 0)
\r
634 nc = resolvedName (name);
\r
636 nc.LocalName = name;
\r
637 nc.Namespace = ns == null ? String.Empty : ns;
\r
639 attr.NameClass = nc;
\r
643 if (!IsEmptyElement) {
\r
645 // read nameClass from content.
\r
646 if (attr.NameClass == null)
\r
647 attr.NameClass = ReadNameClass ();
\r
649 if (NodeType == XmlNodeType.Element)
\r
650 attr.Pattern = ReadPattern ();
\r
652 expectEnd ("attribute");
\r
656 if (attr.NameClass == null)
\r
657 throw new RelaxngException ("Name class was not specified.");
\r
661 private RelaxngGrammar ReadGrammarPattern ()
\r
663 RelaxngGrammar grammar = new RelaxngGrammar ();
\r
664 FillLocation (grammar);
\r
665 grammar.DefaultNamespace = Reader.GetAttribute ("ns");
\r
667 this.readGrammarIncludeContent (grammar.Starts, grammar.Defines, grammar.Divs, grammar.Includes);
\r
668 expectEnd ("grammar");
\r
673 private RelaxngRef ReadRefPattern ()
\r
675 RelaxngRef r = new RelaxngRef ();
\r
678 r.Name = GetNameAttribute ();
\r
679 if (!IsEmptyElement) {
\r
688 private RelaxngExternalRef ReadExternalRefPattern ()
\r
690 RelaxngExternalRef r = new RelaxngExternalRef ();
\r
692 expect ("externalRef");
\r
693 string href = GetSpaceStrippedAttribute ("href", String.Empty);
\r
695 throw new RelaxngException ("Required attribute href was not found.");
\r
696 XmlResolver res = resolver != null ? resolver : new XmlUrlResolver ();
\r
697 r.Href = res.ResolveUri (BaseURI != null ? new Uri (BaseURI) : null, href).AbsoluteUri;
\r
698 r.NSContext = ContextNamespace;
\r
699 if (!IsEmptyElement) {
\r
701 expectEnd ("externalRef");
\r
708 private RelaxngParentRef ReadParentRefPattern ()
\r
710 RelaxngParentRef r = new RelaxngParentRef ();
\r
712 expect ("parentRef");
\r
713 r.Name = GetNameAttribute ();
\r
714 if (!IsEmptyElement) {
\r
716 expectEnd ("parentRef");
\r
723 private RelaxngEmpty ReadEmptyPattern ()
\r
727 if (MoveToFirstAttribute ()) {
\r
729 if (NamespaceURI == String.Empty && LocalName != "datatypeLibrary")
\r
730 throw new RelaxngException ("Invalid attribute.");
\r
731 } while (MoveToNextAttribute ());
\r
735 if (!IsEmptyElement) {
\r
737 expectEnd ("empty");
\r
742 RelaxngEmpty empty = new RelaxngEmpty ();
\r
743 FillLocation (empty);
\r
747 private RelaxngText ReadTextPattern ()
\r
751 if (MoveToFirstAttribute ()) {
\r
753 if (NamespaceURI == String.Empty && LocalName != "datatypeLibrary")
\r
754 throw new RelaxngException ("Invalid attribute.");
\r
755 } while (MoveToNextAttribute ());
\r
759 if (!IsEmptyElement) {
\r
761 expectEnd ("text");
\r
766 RelaxngText t = new RelaxngText ();
\r
771 private RelaxngData ReadDataPattern ()
\r
773 RelaxngData data = new RelaxngData ();
\r
774 FillLocation (data);
\r
777 data.Type = GetSpaceStrippedAttribute ("type", String.Empty);
\r
778 if (data.Type == null)
\r
779 throw new RelaxngException ("Attribute type is required.");
\r
780 data.DatatypeLibrary = DatatypeLibrary;
\r
782 if (MoveToFirstAttribute ()) {
\r
784 if (NamespaceURI != String.Empty)
\r
786 switch (LocalName) {
\r
787 case "datatypeLibrary":
\r
791 throw new RelaxngException ("Invalid attribute.");
\r
793 } while (MoveToNextAttribute ());
\r
797 if (!IsEmptyElement) {
\r
799 while (Name == "param") {
\r
800 data.ParamList.Add (ReadParam ());
\r
802 if (LocalName == "except")
\r
803 data.Except = ReadPatternExcept ();
\r
804 expectEnd ("data");
\r
811 private RelaxngValue ReadValuePattern ()
\r
813 RelaxngValue v = new RelaxngValue ();
\r
817 if (MoveToFirstAttribute ()) {
\r
819 if (NamespaceURI != String.Empty)
\r
821 switch (LocalName) {
\r
822 case "datatypeLibrary":
\r
827 throw new RelaxngException ("Invalid attribute.");
\r
829 } while (MoveToNextAttribute ());
\r
833 if (MoveToAttribute ("type")) {
\r
834 v.Type = Value.Trim ();
\r
835 v.DatatypeLibrary = DatatypeLibrary;
\r
838 v.DatatypeLibrary = "";
\r
840 // v.Namespace = GetSpaceStrippedAttribute ("ns", String.Empty);
\r
842 if (IsEmptyElement) {
\r
843 v.Value = String.Empty;
\r
846 v.Value = ReadString ();
\r
847 expectEnd ("value");
\r
853 private RelaxngList ReadListPattern ()
\r
855 RelaxngList list = new RelaxngList ();
\r
856 FillLocation (list);
\r
859 ReadPatterns (list);
\r
860 expectEnd ("list");
\r
864 private RelaxngOneOrMore ReadOneOrMorePattern ()
\r
866 RelaxngOneOrMore o = new RelaxngOneOrMore ();
\r
868 expect ("oneOrMore");
\r
871 expectEnd ("oneOrMore");
\r
875 private RelaxngZeroOrMore ReadZeroOrMorePattern ()
\r
877 RelaxngZeroOrMore o = new RelaxngZeroOrMore ();
\r
879 expect ("zeroOrMore");
\r
882 expectEnd ("zeroOrMore");
\r
886 private RelaxngOptional ReadOptionalPattern ()
\r
888 RelaxngOptional o = new RelaxngOptional ();
\r
890 expect ("optional");
\r
893 expectEnd ("optional");
\r
897 private RelaxngMixed ReadMixedPattern ()
\r
899 RelaxngMixed o = new RelaxngMixed ();
\r
904 expectEnd ("mixed");
\r
908 private RelaxngGroup ReadGroupPattern ()
\r
910 RelaxngGroup g = new RelaxngGroup ();
\r
915 expectEnd ("group");
\r
919 private RelaxngInterleave ReadInterleavePattern ()
\r
921 RelaxngInterleave i = new RelaxngInterleave ();
\r
923 expect ("interleave");
\r
926 expectEnd ("interleave");
\r
930 private RelaxngChoice ReadChoicePattern ()
\r
932 RelaxngChoice c = new RelaxngChoice ();
\r
937 expectEnd ("choice");
\r
941 private RelaxngNotAllowed ReadNotAllowedPattern ()
\r
943 expect ("notAllowed");
\r
944 if (!IsEmptyElement) {
\r
946 expectEnd ("notAllowed");
\r
950 RelaxngNotAllowed na = new RelaxngNotAllowed ();
\r