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 public static string RelaxngNS = "http://relaxng.org/ns/structure/1.0";
\r
53 public static RelaxngPattern GrammarForRelaxng {
\r
54 get { return grammarForRelaxng; }
\r
59 Stack nsStack = new Stack ();
\r
60 Stack datatypeLibraryStack = new Stack ();
\r
61 XmlResolver resolver = new XmlUrlResolver ();
\r
64 public RelaxngReader (XmlReader reader)
\r
65 : this (reader, null)
\r
69 public RelaxngReader (XmlReader reader, string ns)
\r
70 // : base (grammarForRelaxng == null ? reader : new RelaxngValidatingReader (reader, grammarForRelaxng))
\r
73 nsStack.Push (ns == null ? "" : ns);
\r
74 datatypeLibraryStack.Push ("");
\r
76 if (Reader.ReadState == ReadState.Initial)
\r
81 public XmlResolver XmlResolver {
\r
82 set { resolver = value; }
\r
85 internal XmlResolver Resolver {
\r
86 get { return resolver; }
\r
89 private void FillLocation (RelaxngElementBase el)
\r
91 el.BaseUri = BaseURI;
\r
92 IXmlLineInfo li = this as IXmlLineInfo;
\r
93 el.LineNumber = li != null ? li.LineNumber : 0;
\r
94 el.LinePosition = li != null ? li.LinePosition : 0;
\r
98 public override bool Read ()
\r
100 bool skipRead = false;
\r
103 if (IsEmptyElement) { // this should be done here
\r
105 datatypeLibraryStack.Pop ();
\r
109 b = Reader.Read ();
\r
112 switch (NodeType) {
\r
113 case XmlNodeType.ProcessingInstruction:
\r
114 case XmlNodeType.Comment:
\r
115 case XmlNodeType.EntityReference:
\r
117 case XmlNodeType.Whitespace:
\r
118 // Skip whitespaces except for data and param.
\r
119 case XmlNodeType.SignificantWhitespace:
\r
120 if (LocalName != "value" && LocalName != "param") {
\r
127 if (NamespaceURI != RelaxngNS) {
\r
135 } while (b && loop);
\r
137 switch (NodeType) {
\r
138 case XmlNodeType.Element:
\r
139 if (MoveToAttribute ("ns"))
\r
140 nsStack.Push (Value.Trim ());
\r
142 nsStack.Push (nsStack.Peek ());
\r
144 if (MoveToAttribute ("datatypeLibrary")) {
\r
145 string uriString = Value.Trim ();
\r
146 if (uriString.Length == 0)
\r
147 datatypeLibraryStack.Push (String.Empty);
\r
150 Uri uri = new Uri (uriString);
\r
151 // MS.NET Uri is too lamespec
\r
152 datatypeLibraryStack.Push (uri.ToString ());
\r
153 } catch (UriFormatException ex) {
\r
154 throw new RelaxngException (ex.Message, ex);
\r
159 datatypeLibraryStack.Push (datatypeLibraryStack.Peek ());
\r
163 case XmlNodeType.EndElement:
\r
165 datatypeLibraryStack.Pop ();
\r
174 public string ContextNamespace {
\r
175 get { return nsStack.Peek () as string; }
\r
178 public string DatatypeLibrary {
\r
179 get { return datatypeLibraryStack.Peek () as string; }
\r
182 // Utility methods.
\r
183 private void expect (string name)
\r
185 if (NamespaceURI != RelaxngGrammar.NamespaceURI)
\r
186 throw new RelaxngException (String.Format ("Invalid document: expected namespace {0} but found {1}", RelaxngGrammar.NamespaceURI, NamespaceURI));
\r
187 else if (LocalName != name)
\r
188 throw new RelaxngException (String.Format ("Invalid document: expected local name {0} but found {1}", name, LocalName));
\r
191 private void expectEnd (string name)
\r
193 if (NodeType != XmlNodeType.EndElement)
\r
194 throw new RelaxngException (String.Format ("Expected EndElement but found {0}.", NodeType));
\r
200 // Other than name class and pattern.
\r
201 private RelaxngStart ReadStart ()
\r
203 RelaxngStart s = new RelaxngStart ();
\r
207 if (MoveToFirstAttribute ()) {
\r
209 if (NamespaceURI != String.Empty)
\r
211 switch (LocalName) {
\r
212 case "datatypeLibrary":
\r
216 throw new RelaxngException ("Invalid attribute.");
\r
218 } while (MoveToNextAttribute ());
\r
222 if (MoveToAttribute ("combine")) {
\r
223 s.Combine = Value.Trim ();
\r
224 if (s.Combine != "choice" && s.Combine != "interleave")
\r
225 throw new RelaxngException ("Invalid combine attribute: " + s.Combine);
\r
230 s.Pattern = ReadPattern ();
\r
231 expectEnd ("start");
\r
235 private string GetNameAttribute ()
\r
237 string name = GetSpaceStrippedAttribute ("name", String.Empty);
\r
239 throw new RelaxngException ("Required attribute name is not found.");
\r
240 return XmlConvert.VerifyNCName (name);
\r
243 private string GetSpaceStrippedAttribute (string name, string ns)
\r
245 string v = GetAttribute (name, ns);
\r
246 return v != null ? v.Trim () : null;
\r
249 private RelaxngDefine ReadDefine ()
\r
251 RelaxngDefine def = new RelaxngDefine ();
\r
252 FillLocation (def);
\r
254 def.Name = GetNameAttribute ();
\r
255 def.Combine = GetSpaceStrippedAttribute ("combine", String.Empty);
\r
258 while (NodeType == XmlNodeType.Element)
\r
259 def.Patterns.Add (ReadPattern ());
\r
260 expectEnd ("define");
\r
264 private RelaxngParam ReadParam ()
\r
266 RelaxngParam p = new RelaxngParam ();
\r
269 p.Name = GetNameAttribute ();
\r
270 p.Value = ReadString ().Trim ();
\r
271 expectEnd ("param");
\r
275 // NameClass reader (only if it is element-style.)
\r
276 private RelaxngNameClass ReadNameClass ()
\r
278 switch (LocalName) {
\r
280 return ReadNameClassName ();
\r
282 return ReadNameClassAnyName ();
\r
284 return ReadNameClassNsName ();
\r
286 return ReadNameClassChoice ();
\r
288 throw new RelaxngException ("Invalid name class: " + LocalName);
\r
291 private RelaxngName ReadNameClassName ()
\r
293 string name = ReadString ().Trim ();
\r
294 RelaxngName rName = resolvedName (name);
\r
295 expectEnd ("name");
\r
299 private RelaxngAnyName ReadNameClassAnyName ()
\r
301 RelaxngAnyName an = new RelaxngAnyName ();
\r
303 if (!IsEmptyElement) {
\r
305 if (NodeType == XmlNodeType.EndElement) {
\r
310 an.Except = new RelaxngExceptNameClass ();
\r
311 FillLocation (an.Except);
\r
312 while (NodeType == XmlNodeType.Element)
\r
313 an.Except.Names.Add (
\r
315 expectEnd ("except");
\r
317 expectEnd ("anyName");
\r
323 private RelaxngNsName ReadNameClassNsName ()
\r
325 RelaxngNsName nn = new RelaxngNsName ();
\r
327 nn.Namespace = this.ContextNamespace;
\r
328 if (!IsEmptyElement) {
\r
330 if (NodeType == XmlNodeType.EndElement) {
\r
335 nn.Except = ReadNameClassExcept ();//new RelaxngExceptNameClass ();
\r
336 FillLocation (nn.Except);
\r
338 expectEnd ("nsName");
\r
344 private RelaxngNameChoice ReadNameClassChoice ()
\r
346 RelaxngNameChoice nc = new RelaxngNameChoice ();
\r
348 if (IsEmptyElement)
\r
349 throw new RelaxngException ("Name choice must have at least one name class.");
\r
352 while (NodeType != XmlNodeType.EndElement) {
\r
353 nc.Children.Add (ReadNameClass ());
\r
355 if (nc.Children.Count == 0)
\r
356 throw new RelaxngException ("Name choice must have at least one name class.");
\r
358 expectEnd ("choice");
\r
362 private RelaxngExceptNameClass ReadNameClassExcept ()
\r
364 RelaxngExceptNameClass x = new RelaxngExceptNameClass ();
\r
366 if (IsEmptyElement)
\r
367 throw new RelaxngException ("Name choice must have at least one name class.");
\r
370 while (NodeType != XmlNodeType.EndElement)
\r
371 x.Names.Add (ReadNameClass ());
\r
372 if (x.Names.Count == 0)
\r
373 throw new RelaxngException ("Name choice must have at least one name class.");
\r
375 expectEnd ("except");
\r
381 public RelaxngPattern ReadPattern ()
\r
383 while (NodeType != XmlNodeType.Element)
\r
387 switch (LocalName) {
\r
389 return ReadElementPattern ();
\r
391 return ReadAttributePattern ();
\r
393 return ReadGroupPattern ();
\r
395 return ReadInterleavePattern ();
\r
397 return ReadChoicePattern ();
\r
399 return ReadOptionalPattern ();
\r
401 return ReadZeroOrMorePattern ();
\r
403 return ReadOneOrMorePattern ();
\r
405 return ReadListPattern ();
\r
407 return ReadMixedPattern ();
\r
409 return ReadRefPattern ();
\r
411 return ReadParentRefPattern ();
\r
413 return ReadEmptyPattern ();
\r
415 return ReadTextPattern ();
\r
417 return ReadDataPattern ();
\r
419 return ReadValuePattern ();
\r
421 return ReadNotAllowedPattern ();
\r
422 case "externalRef":
\r
423 return ReadExternalRefPattern ();
\r
425 return ReadGrammarPattern ();
\r
427 throw new RelaxngException ("Non-supported pattern specification: " + LocalName);
\r
430 private void ReadPatterns (RelaxngSingleContentPattern el)
\r
433 el.Patterns.Add (ReadPattern ());
\r
434 } while (NodeType == XmlNodeType.Element);
\r
437 private void ReadPatterns (RelaxngBinaryContentPattern el)
\r
440 el.Patterns.Add (ReadPattern ());
\r
441 } while (NodeType == XmlNodeType.Element);
\r
444 private RelaxngExcept ReadPatternExcept ()
\r
446 RelaxngExcept x = new RelaxngExcept ();
\r
448 if (IsEmptyElement)
\r
449 throw new RelaxngException ("'except' must have at least one pattern.");
\r
451 while (NodeType != XmlNodeType.EndElement)
\r
452 x.Patterns.Add (ReadPattern ());
\r
453 if (x.Patterns.Count == 0)
\r
454 throw new RelaxngException ("'except' must have at least one pattern.");
\r
456 expectEnd ("except");
\r
460 private RelaxngInclude ReadInclude ()
\r
462 RelaxngInclude i = new RelaxngInclude ();
\r
464 expect ("include");
\r
465 i.NSContext = ContextNamespace;
\r
466 string href = GetSpaceStrippedAttribute ("href", String.Empty);
\r
468 throw new RelaxngException ("Required attribute href was not found.");
\r
469 XmlResolver res = resolver != null ? resolver : new XmlUrlResolver ();
\r
470 i.Href = res.ResolveUri (BaseURI != null ? new Uri (BaseURI) : null, href).AbsoluteUri;
\r
471 if (!IsEmptyElement) {
\r
473 this.readGrammarIncludeContent (i.Starts, i.Defines, i.Divs, null);
\r
474 expectEnd ("include");
\r
481 private void readGrammarIncludeContent (IList starts, IList defines, IList divs, IList includes)
\r
483 while (NodeType == XmlNodeType.Element) {
\r
484 switch (LocalName) {
\r
486 starts.Add (ReadStart ());
\r
489 defines.Add (ReadDefine ());
\r
492 divs.Add (ReadDiv (includes != null));
\r
495 if (includes != null)
\r
496 includes.Add (ReadInclude ());
\r
498 throw new RelaxngException ("Unexpected content: " + Name);
\r
501 throw new RelaxngException ("Unexpected content: " + Name);
\r
506 private RelaxngDiv ReadDiv (bool allowIncludes)
\r
509 RelaxngDiv div = new RelaxngDiv ();
\r
510 FillLocation (div);
\r
511 if (!IsEmptyElement) {
\r
513 readGrammarIncludeContent (div.Starts, div.Defines, div.Divs, div.Includes);
\r
521 private RelaxngName resolvedName (string nameSpec)
\r
523 int colonAt = nameSpec.IndexOf (':');
\r
524 string prefix = (colonAt < 0) ? "" : nameSpec.Substring (0, colonAt);
\r
525 string local = (colonAt < 0) ? nameSpec : nameSpec.Substring (colonAt + 1, nameSpec.Length - colonAt - 1);
\r
526 string uri = ContextNamespace;
\r
528 if (prefix != "") {
\r
529 uri = LookupNamespace (prefix);
\r
531 throw new RelaxngException ("Undeclared prefix in name component: " + nameSpec);
\r
533 RelaxngName n = new RelaxngName (local, uri);
\r
538 private RelaxngElement ReadElementPattern ()
\r
540 RelaxngElement el = new RelaxngElement ();
\r
543 if (MoveToFirstAttribute ()) {
\r
545 if (NamespaceURI != String.Empty)
\r
547 switch (LocalName) {
\r
548 case "datatypeLibrary":
\r
553 throw new RelaxngException ("Invalid attribute.");
\r
555 } while (MoveToNextAttribute ());
\r
559 // try to get name from attribute.
\r
560 if (MoveToAttribute ("name"))
\r
561 el.NameClass = resolvedName (XmlConvert.VerifyName (Value.Trim ()));
\r
565 // read nameClass from content.
\r
566 if (el.NameClass == null)
\r
567 el.NameClass = ReadNameClass ();
\r
570 this.ReadPatterns (el);
\r
572 expectEnd ("element");
\r
574 if (el.NameClass == null)
\r
575 throw new RelaxngException ("Name class was not specified.");
\r
579 private RelaxngAttribute ReadAttributePattern ()
\r
581 RelaxngAttribute attr = new RelaxngAttribute ();
\r
582 FillLocation (attr);
\r
584 if (MoveToFirstAttribute ()) {
\r
586 if (NamespaceURI != String.Empty)
\r
588 switch (LocalName) {
\r
589 case "datatypeLibrary":
\r
594 throw new RelaxngException ("Invalid attribute.");
\r
596 } while (MoveToNextAttribute ());
\r
600 string ns = GetSpaceStrippedAttribute ("ns", String.Empty);
\r
602 // try to get name from attribute.
\r
603 if (MoveToAttribute ("name", String.Empty)) {
\r
604 // attr.NameClass = resolvedName (XmlConvert.VerifyName (Value.Trim ()), false);
\r
605 RelaxngName nc = new RelaxngName ();
\r
606 string name = XmlConvert.VerifyName (Value.Trim ());
\r
607 if (name.IndexOf (':') > 0)
\r
608 nc = resolvedName (name);
\r
610 nc.LocalName = name;
\r
611 nc.Namespace = ns == null ? String.Empty : ns;
\r
613 attr.NameClass = nc;
\r
617 if (!IsEmptyElement) {
\r
619 // read nameClass from content.
\r
620 if (attr.NameClass == null)
\r
621 attr.NameClass = ReadNameClass ();
\r
623 if (NodeType == XmlNodeType.Element)
\r
624 attr.Pattern = ReadPattern ();
\r
626 expectEnd ("attribute");
\r
630 if (attr.NameClass == null)
\r
631 throw new RelaxngException ("Name class was not specified.");
\r
635 private RelaxngGrammar ReadGrammarPattern ()
\r
637 RelaxngGrammar grammar = new RelaxngGrammar ();
\r
638 FillLocation (grammar);
\r
640 this.readGrammarIncludeContent (grammar.Starts, grammar.Defines, grammar.Divs, grammar.Includes);
\r
641 expectEnd ("grammar");
\r
646 private RelaxngRef ReadRefPattern ()
\r
648 RelaxngRef r = new RelaxngRef ();
\r
651 r.Name = GetNameAttribute ();
\r
652 if (!IsEmptyElement) {
\r
661 private RelaxngExternalRef ReadExternalRefPattern ()
\r
663 RelaxngExternalRef r = new RelaxngExternalRef ();
\r
665 expect ("externalRef");
\r
666 string href = GetSpaceStrippedAttribute ("href", String.Empty);
\r
668 throw new RelaxngException ("Required attribute href was not found.");
\r
669 XmlResolver res = resolver != null ? resolver : new XmlUrlResolver ();
\r
670 r.Href = res.ResolveUri (BaseURI != null ? new Uri (BaseURI) : null, href).AbsoluteUri;
\r
671 r.NSContext = ContextNamespace;
\r
672 if (!IsEmptyElement) {
\r
674 expectEnd ("externalRef");
\r
681 private RelaxngParentRef ReadParentRefPattern ()
\r
683 RelaxngParentRef r = new RelaxngParentRef ();
\r
685 expect ("parentRef");
\r
686 r.Name = GetNameAttribute ();
\r
687 if (!IsEmptyElement) {
\r
689 expectEnd ("parentRef");
\r
696 private RelaxngEmpty ReadEmptyPattern ()
\r
700 if (MoveToFirstAttribute ()) {
\r
702 if (NamespaceURI == String.Empty && LocalName != "datatypeLibrary")
\r
703 throw new RelaxngException ("Invalid attribute.");
\r
704 } while (MoveToNextAttribute ());
\r
708 if (!IsEmptyElement) {
\r
710 expectEnd ("empty");
\r
715 RelaxngEmpty empty = new RelaxngEmpty ();
\r
716 FillLocation (empty);
\r
720 private RelaxngText ReadTextPattern ()
\r
724 if (MoveToFirstAttribute ()) {
\r
726 if (NamespaceURI == String.Empty && LocalName != "datatypeLibrary")
\r
727 throw new RelaxngException ("Invalid attribute.");
\r
728 } while (MoveToNextAttribute ());
\r
732 if (!IsEmptyElement) {
\r
734 expectEnd ("text");
\r
739 RelaxngText t = new RelaxngText ();
\r
744 private RelaxngData ReadDataPattern ()
\r
746 RelaxngData data = new RelaxngData ();
\r
747 FillLocation (data);
\r
750 data.Type = GetSpaceStrippedAttribute ("type", String.Empty);
\r
751 if (data.Type == null)
\r
752 throw new RelaxngException ("Attribute type is required.");
\r
753 data.DatatypeLibrary = DatatypeLibrary;
\r
755 if (MoveToFirstAttribute ()) {
\r
757 if (NamespaceURI != String.Empty)
\r
759 switch (LocalName) {
\r
760 case "datatypeLibrary":
\r
764 throw new RelaxngException ("Invalid attribute.");
\r
766 } while (MoveToNextAttribute ());
\r
770 if (!IsEmptyElement) {
\r
772 while (Name == "param") {
\r
773 data.ParamList.Add (ReadParam ());
\r
775 if (LocalName == "except")
\r
776 data.Except = ReadPatternExcept ();
\r
777 expectEnd ("data");
\r
784 private RelaxngValue ReadValuePattern ()
\r
786 RelaxngValue v = new RelaxngValue ();
\r
790 if (MoveToFirstAttribute ()) {
\r
792 if (NamespaceURI != String.Empty)
\r
794 switch (LocalName) {
\r
795 case "datatypeLibrary":
\r
800 throw new RelaxngException ("Invalid attribute.");
\r
802 } while (MoveToNextAttribute ());
\r
806 if (MoveToAttribute ("type")) {
\r
807 v.Type = Value.Trim ();
\r
808 v.DatatypeLibrary = DatatypeLibrary;
\r
811 v.DatatypeLibrary = "";
\r
813 // v.Namespace = GetSpaceStrippedAttribute ("ns", String.Empty);
\r
815 if (IsEmptyElement) {
\r
816 v.Value = String.Empty;
\r
819 v.Value = ReadString ();
\r
820 expectEnd ("value");
\r
826 private RelaxngList ReadListPattern ()
\r
828 RelaxngList list = new RelaxngList ();
\r
829 FillLocation (list);
\r
832 ReadPatterns (list);
\r
833 expectEnd ("list");
\r
837 private RelaxngOneOrMore ReadOneOrMorePattern ()
\r
839 RelaxngOneOrMore o = new RelaxngOneOrMore ();
\r
841 expect ("oneOrMore");
\r
844 expectEnd ("oneOrMore");
\r
848 private RelaxngZeroOrMore ReadZeroOrMorePattern ()
\r
850 RelaxngZeroOrMore o = new RelaxngZeroOrMore ();
\r
852 expect ("zeroOrMore");
\r
855 expectEnd ("zeroOrMore");
\r
859 private RelaxngOptional ReadOptionalPattern ()
\r
861 RelaxngOptional o = new RelaxngOptional ();
\r
863 expect ("optional");
\r
866 expectEnd ("optional");
\r
870 private RelaxngMixed ReadMixedPattern ()
\r
872 RelaxngMixed o = new RelaxngMixed ();
\r
877 expectEnd ("mixed");
\r
881 private RelaxngGroup ReadGroupPattern ()
\r
883 RelaxngGroup g = new RelaxngGroup ();
\r
888 expectEnd ("group");
\r
892 private RelaxngInterleave ReadInterleavePattern ()
\r
894 RelaxngInterleave i = new RelaxngInterleave ();
\r
896 expect ("interleave");
\r
899 expectEnd ("interleave");
\r
903 private RelaxngChoice ReadChoicePattern ()
\r
905 RelaxngChoice c = new RelaxngChoice ();
\r
910 expectEnd ("choice");
\r
914 private RelaxngNotAllowed ReadNotAllowedPattern ()
\r
916 expect ("notAllowed");
\r
917 if (!IsEmptyElement) {
\r
919 expectEnd ("notAllowed");
\r
923 RelaxngNotAllowed na = new RelaxngNotAllowed ();
\r