2 // Mono.Xml.Schema.XsdParticleValidationState.cs
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 // (C)2003 Atsushi Enomoto
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Collections;
33 using System.Xml.Schema;
36 namespace Mono.Xml.Schema
38 internal enum XsdParticleEvaluationResult
40 Matched = 1, // Matched one of its components.
41 Passed = 2, // Did not match, but it successfully passed the whole components.
42 InvalidIncomplete = 3, // It had incomplete validation state, and in fact it failed to pass.
43 Mismatched = 4 // Dis not match,
46 internal class XsdValidationStateManager
49 XmlSchemaElement currentElement;
50 XmlSchemaContentProcessing processContents;
52 public XsdValidationStateManager ()
54 table = new Hashtable ();
55 processContents = XmlSchemaContentProcessing.Strict; // not Lax
58 public XmlSchemaElement CurrentElement {
59 get { return currentElement; }
60 set { currentElement = value; }
63 internal void SetCurrentElement (XmlSchemaElement elt)
65 this.currentElement = elt;
68 public XmlSchemaContentProcessing ProcessContents {
69 get { return processContents; }
72 internal void SetProcessContents (XmlSchemaContentProcessing value)
74 this.processContents = value;
77 public XsdValidationState Get (XmlSchemaParticle xsobj)
79 XsdValidationState got = table [xsobj] as XsdValidationState;
85 public XsdValidationState Create (XmlSchemaObject xsobj)
87 string typeName = xsobj.GetType ().Name;
89 case "XmlSchemaElement":
90 return AddElement ((XmlSchemaElement) xsobj);
91 case "XmlSchemaSequence":
92 return AddSequence ((XmlSchemaSequence) xsobj);
93 case "XmlSchemaChoice":
94 return AddChoice ((XmlSchemaChoice) xsobj);
96 return AddAll ((XmlSchemaAll) xsobj);
98 return AddAny ((XmlSchemaAny) xsobj);
102 // GroupRef should not appear
103 throw new InvalidOperationException ("Should not occur.");
107 internal XsdValidationState MakeSequence (XsdValidationState head, XsdValidationState rest)
109 if (head is XsdEmptyValidationState)
112 return new XsdAppendedValidationState (this, head, rest);
115 private XsdElementValidationState AddElement (XmlSchemaElement element)
117 XsdElementValidationState got = new XsdElementValidationState (element, this);
121 private XsdSequenceValidationState AddSequence (XmlSchemaSequence sequence)
123 XsdSequenceValidationState got = new XsdSequenceValidationState (sequence, this);
127 private XsdChoiceValidationState AddChoice (XmlSchemaChoice choice)
129 XsdChoiceValidationState got = new XsdChoiceValidationState (choice, this);
133 private XsdAllValidationState AddAll (XmlSchemaAll all)
135 XsdAllValidationState got = new XsdAllValidationState (all, this);
139 private XsdAnyValidationState AddAny (XmlSchemaAny any)
141 XsdAnyValidationState got = new XsdAnyValidationState (any, this);
145 private XsdEmptyValidationState AddEmpty ()
147 return new XsdEmptyValidationState (this);
151 internal abstract class XsdValidationState
155 static XsdInvalidValidationState invalid;
157 static XsdValidationState ()
159 invalid = new XsdInvalidValidationState (null);
162 public static XsdInvalidValidationState Invalid {
163 get { return invalid; }
170 XsdValidationStateManager manager;
172 public XsdValidationState (XsdValidationStateManager manager)
174 this.manager = manager;
177 // Normally checks Max Occurs boundary
178 public abstract XsdValidationState EvaluateStartElement (string localName, string ns);
180 // Normally checks Min Occurs boundary
181 public abstract bool EvaluateEndElement ();
183 internal abstract bool EvaluateIsEmptiable ();
185 public XsdValidationStateManager Manager {
186 get { return manager; }
189 public string Message {
190 get { return message; }
193 public string MessageInternal {
194 get { return message; }
195 set { message = value; }
199 get { return occured; }
202 internal int OccuredInternal {
203 get { return occured; }
204 set { occured = value; }
208 internal class XsdElementValidationState : XsdValidationState
210 public XsdElementValidationState (XmlSchemaElement element, XsdValidationStateManager manager)
213 this.element = element;
214 name = element.QualifiedName.Name;
215 ns = element.QualifiedName.Namespace;
219 XmlSchemaElement element;
225 public override XsdValidationState EvaluateStartElement (string name, string ns)
227 if (this.name == name && this.ns == ns && !element.IsAbstract) {
228 return this.CheckOccurence (element);
230 for (int i = 0; i < element.SubstitutingElements.Count; i++) {
231 XmlSchemaElement subst = (XmlSchemaElement) element.SubstitutingElements [i];
232 if (subst.QualifiedName.Name == name &&
233 subst.QualifiedName.Namespace == ns) {
234 return this.CheckOccurence (subst);
237 return XsdValidationState.Invalid;
241 private XsdValidationState CheckOccurence (XmlSchemaElement maybeSubstituted)
244 Manager.SetCurrentElement (maybeSubstituted);
245 if (Occured > element.ValidatedMaxOccurs) {
246 MessageInternal = "Element occurence excess.";
247 return XsdValidationState.Invalid;
248 } else if (Occured == element.ValidatedMaxOccurs) {
249 return Manager.Create (XmlSchemaParticle.Empty);
255 public override bool EvaluateEndElement ()
257 return EvaluateIsEmptiable ();
260 internal override bool EvaluateIsEmptiable ()
262 return (element.ValidatedMinOccurs <= Occured &&
263 element.ValidatedMaxOccurs >= Occured);
267 internal class XsdSequenceValidationState : XsdValidationState
269 XmlSchemaSequence seq;
271 XsdValidationState currentAutomata;
276 public XsdSequenceValidationState (XmlSchemaSequence sequence, XsdValidationStateManager manager)
277 : this (sequence, manager, sequence.ValidatedMinOccurs, sequence.ValidatedMaxOccurs, -1)
281 public XsdSequenceValidationState (XmlSchemaSequence sequence, XsdValidationStateManager manager,
282 decimal minOccurs, decimal maxOccurs, int current)
286 this.minOccurs = minOccurs;
287 this.maxOccurs = maxOccurs;
288 this.current = current;
291 public override XsdValidationState EvaluateStartElement (string name, string ns)
293 if (seq.CompiledItems.Count == 0)
294 return XsdValidationState.Invalid;
296 int idx = current < 0 ? 0 : current;
297 XsdValidationState xa = currentAutomata;
298 // If it is true and when matching particle was found, then
299 // it will increment occurence.
300 bool increment = false;
303 // if (current < 0 || current == seq.CompiledItems.Count) {
304 // idx = current = 0;
307 if (xa == null) { // This code runs in case of a newiteration.
308 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
311 if (xa is XsdEmptyValidationState &&
312 seq.CompiledItems.Count == idx + 1 &&
313 Occured == maxOccurs) {
314 return XsdValidationState.Invalid;
316 XsdValidationState result = xa.EvaluateStartElement (name, ns);
317 if (result == XsdValidationState.Invalid) {
318 if (!xa.EvaluateIsEmptiable ()) {
320 return XsdValidationState.Invalid;
324 currentAutomata = result;
327 if (Occured > maxOccurs)
328 return XsdValidationState.Invalid;
331 // return Manager.MakeSequence (result, this);
333 // skip in other cases.
337 if (idx > current && increment && current >= 0) {
338 return XsdValidationState.Invalid;
340 if (seq.CompiledItems.Count > idx) {
341 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
343 else if (current < 0) { // started from top
344 return XsdValidationState.Invalid;
346 else { // started from middle
353 public override bool EvaluateEndElement ()
355 if (minOccurs > Occured + 1)
357 if (seq.CompiledItems.Count == 0)
359 if (currentAutomata == null && minOccurs <= Occured)
362 int idx = current < 0 ? 0 : current;
363 XsdValidationState xa = currentAutomata;
365 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
367 if (!xa.EvaluateEndElement ())
368 if (!xa.EvaluateIsEmptiable ())
369 return false; // cannot omit following items.
371 if (seq.CompiledItems.Count > idx)
372 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
379 return minOccurs <= Occured && maxOccurs >= Occured;
382 internal override bool EvaluateIsEmptiable ()
\r
384 if (minOccurs > Occured + 1)
\r
386 if (minOccurs == 0 && currentAutomata == null)
\r
391 if (seq.CompiledItems.Count == 0)
\r
394 int idx = current < 0 ? 0 : current;
395 XsdValidationState xa = currentAutomata;
\r
397 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
399 if (!xa.EvaluateIsEmptiable ())
402 if (seq.CompiledItems.Count > idx)
403 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
413 internal class XsdChoiceValidationState : XsdValidationState
415 XmlSchemaChoice choice;
417 bool emptiableComputed;
419 public XsdChoiceValidationState (XmlSchemaChoice choice, XsdValidationStateManager manager)
422 this.choice = choice;
425 public override XsdValidationState EvaluateStartElement (string localName, string ns)
427 emptiableComputed = false;
429 for (int i = 0; i < choice.CompiledItems.Count; i++) {
430 XmlSchemaParticle xsobj = (XmlSchemaParticle) choice.CompiledItems [i];
431 XsdValidationState xa = Manager.Create (xsobj);
432 XsdValidationState result = xa.EvaluateStartElement (localName, ns);
433 if (result != XsdValidationState.Invalid) {
435 if (Occured > choice.ValidatedMaxOccurs)
436 return XsdValidationState.Invalid;
437 else if (Occured == choice.ValidatedMaxOccurs)
440 return Manager.MakeSequence (result, this);
443 emptiable = choice.ValidatedMinOccurs <= Occured;
444 emptiableComputed = true;
445 return XsdValidationState.Invalid;
448 public override bool EvaluateEndElement ()
450 emptiableComputed = false;
452 if (choice.ValidatedMinOccurs > Occured + 1)
455 else if (choice.ValidatedMinOccurs <= Occured)
458 for (int i = 0; i < choice.CompiledItems.Count; i++) {
459 XmlSchemaParticle p = (XmlSchemaParticle) choice.CompiledItems [i];
460 if (Manager.Create (p).EvaluateIsEmptiable ())
466 internal override bool EvaluateIsEmptiable ()
\r
468 if (emptiableComputed)
471 if (choice.ValidatedMaxOccurs < Occured)
\r
473 else if (choice.ValidatedMinOccurs > Occured + 1)
\r
476 for (int i = Occured; i < choice.ValidatedMinOccurs; i++) {
\r
478 for (int pi = 0; pi < choice.CompiledItems.Count; pi++) {
479 XmlSchemaParticle p = (XmlSchemaParticle) choice.CompiledItems [pi];
\r
480 if (Manager.Create (p).EvaluateIsEmptiable ()) {
492 internal class XsdAllValidationState : XsdValidationState
495 ArrayList consumed = new ArrayList ();
497 public XsdAllValidationState (XmlSchemaAll all, XsdValidationStateManager manager)
503 public override XsdValidationState EvaluateStartElement (string localName, string ns)
505 if (all.CompiledItems.Count == 0)
506 return XsdValidationState.Invalid;
508 // We don't have to keep element validation state, since
509 // it must occur only 0 or 1.
510 for (int i = 0; i < all.CompiledItems.Count; i++) {
511 XmlSchemaElement xsElem = (XmlSchemaElement) all.CompiledItems [i];
512 if (xsElem.QualifiedName.Name == localName &&
513 xsElem.QualifiedName.Namespace == ns) {
514 if (consumed.Contains (xsElem))
515 return XsdValidationState.Invalid;
516 consumed.Add (xsElem);
517 Manager.SetCurrentElement (xsElem);
518 OccuredInternal = 1; // xs:all also occurs 0 or 1 always.
522 return XsdValidationState.Invalid;
525 public override bool EvaluateEndElement ()
527 if (all.Emptiable || all.ValidatedMinOccurs == 0)
529 if (all.ValidatedMinOccurs > 0 && consumed.Count == 0)
531 if (all.CompiledItems.Count == consumed.Count)
533 for (int i = 0; i < all.CompiledItems.Count; i++) {
534 XmlSchemaElement el = (XmlSchemaElement) all.CompiledItems [i];
535 if (el.ValidatedMinOccurs > 0 && !consumed.Contains (el))
541 internal override bool EvaluateIsEmptiable ()
\r
543 if (all.Emptiable || all.ValidatedMinOccurs == 0)
545 for (int i = 0; i < all.CompiledItems.Count; i++) {
546 XmlSchemaElement el = (XmlSchemaElement) all.CompiledItems [i];
547 if (el.ValidatedMinOccurs > 0 && !consumed.Contains (el))
554 internal class XsdAnyValidationState : XsdValidationState
558 public XsdAnyValidationState (XmlSchemaAny any, XsdValidationStateManager manager)
565 public override XsdValidationState EvaluateStartElement (string name, string ns)
567 if (!MatchesNamespace (ns))
568 return XsdValidationState.Invalid;
571 Manager.SetProcessContents (any.ResolvedProcessContents);
572 if (Occured > any.ValidatedMaxOccurs)
573 return XsdValidationState.Invalid;
574 else if (Occured == any.ValidatedMaxOccurs)
575 return Manager.Create (XmlSchemaParticle.Empty);
580 private bool MatchesNamespace (string ns)
584 if (any.HasValueLocal && ns == String.Empty)
586 if (any.HasValueOther && (any.TargetNamespace == "" || any.TargetNamespace != ns))
588 if (any.HasValueTargetNamespace && any.TargetNamespace == ns)
590 for (int i = 0; i < any.ResolvedNamespaces.Count; i++)
591 if (any.ResolvedNamespaces [i] == ns)
596 public override bool EvaluateEndElement ()
598 return EvaluateIsEmptiable ();
601 internal override bool EvaluateIsEmptiable ()
\r
603 return any.ValidatedMinOccurs <= Occured &&
604 any.ValidatedMaxOccurs >= Occured;
608 internal class XsdAppendedValidationState : XsdValidationState
610 public XsdAppendedValidationState (XsdValidationStateManager manager,
611 XsdValidationState head, XsdValidationState rest)
618 XsdValidationState head;
619 XsdValidationState rest;
622 public override XsdValidationState EvaluateStartElement (string name, string ns)
624 XsdValidationState afterHead = head.EvaluateStartElement (name, ns);
625 if (afterHead != XsdValidationState.Invalid) {
627 return afterHead is XsdEmptyValidationState ? rest : this;
628 } else if (!head.EvaluateIsEmptiable ()) {
629 return XsdValidationState.Invalid;
632 return rest.EvaluateStartElement (name, ns);
635 public override bool EvaluateEndElement ()
637 if (head.EvaluateEndElement ())
639 return rest.EvaluateIsEmptiable ();
640 if (!head.EvaluateIsEmptiable ())
642 return rest.EvaluateEndElement ();
645 internal override bool EvaluateIsEmptiable ()
\r
647 if (head.EvaluateIsEmptiable ())
\r
648 return rest.EvaluateIsEmptiable ();
\r
654 internal class XsdEmptyValidationState : XsdValidationState
656 public XsdEmptyValidationState (XsdValidationStateManager manager)
662 public override XsdValidationState EvaluateStartElement (string name, string ns)
664 return XsdValidationState.Invalid;
667 public override bool EvaluateEndElement ()
672 internal override bool EvaluateIsEmptiable ()
\r
679 internal class XsdInvalidValidationState : XsdValidationState
681 internal XsdInvalidValidationState (XsdValidationStateManager manager)
687 public override XsdValidationState EvaluateStartElement (string name, string ns)
692 public override bool EvaluateEndElement ()
697 internal override bool EvaluateIsEmptiable ()
\r