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 class XsdParticleStateManager
41 XmlSchemaContentProcessing processContents;
43 public XsdParticleStateManager ()
45 table = new Hashtable ();
46 processContents = XmlSchemaContentProcessing.Strict; // not Lax
49 public XmlSchemaElement CurrentElement;
51 public Stack ContextStack = new Stack ();
53 public XsdValidationContext Context
54 = new XsdValidationContext ();
56 public XmlSchemaContentProcessing ProcessContents {
57 get { return processContents; }
60 public void PushContext ()
62 ContextStack.Push (Context.Clone ());
65 public void PopContext ()
67 Context = (XsdValidationContext) ContextStack.Pop ();
70 internal void SetProcessContents (XmlSchemaContentProcessing value)
72 this.processContents = value;
75 public XsdValidationState Get (XmlSchemaParticle xsobj)
77 XsdValidationState got = table [xsobj] as XsdValidationState;
83 public XsdValidationState Create (XmlSchemaObject xsobj)
85 string typeName = xsobj.GetType ().Name;
87 case "XmlSchemaElement":
88 return AddElement ((XmlSchemaElement) xsobj);
89 case "XmlSchemaSequence":
90 return AddSequence ((XmlSchemaSequence) xsobj);
91 case "XmlSchemaChoice":
92 return AddChoice ((XmlSchemaChoice) xsobj);
94 return AddAll ((XmlSchemaAll) xsobj);
96 return AddAny ((XmlSchemaAny) xsobj);
100 // GroupRef should not appear
101 throw new InvalidOperationException ("Should not occur.");
105 internal XsdValidationState MakeSequence (XsdValidationState head, XsdValidationState rest)
107 if (head is XsdEmptyValidationState)
110 return new XsdAppendedValidationState (this, head, rest);
113 private XsdElementValidationState AddElement (XmlSchemaElement element)
115 XsdElementValidationState got = new XsdElementValidationState (element, this);
119 private XsdSequenceValidationState AddSequence (XmlSchemaSequence sequence)
121 XsdSequenceValidationState got = new XsdSequenceValidationState (sequence, this);
125 private XsdChoiceValidationState AddChoice (XmlSchemaChoice choice)
127 XsdChoiceValidationState got = new XsdChoiceValidationState (choice, this);
131 private XsdAllValidationState AddAll (XmlSchemaAll all)
133 XsdAllValidationState got = new XsdAllValidationState (all, this);
137 private XsdAnyValidationState AddAny (XmlSchemaAny any)
139 XsdAnyValidationState got = new XsdAnyValidationState (any, this);
143 private XsdEmptyValidationState AddEmpty ()
145 return new XsdEmptyValidationState (this);
149 internal abstract class XsdValidationState
153 static XsdInvalidValidationState invalid;
155 static XsdValidationState ()
157 invalid = new XsdInvalidValidationState (null);
160 public static XsdInvalidValidationState Invalid {
161 get { return invalid; }
167 readonly XsdParticleStateManager manager;
169 public XsdValidationState (XsdParticleStateManager manager)
171 this.manager = manager;
174 // Normally checks Max Occurs boundary
175 public abstract XsdValidationState EvaluateStartElement (string localName, string ns);
177 // Normally checks Min Occurs boundary
178 public abstract bool EvaluateEndElement ();
180 internal abstract bool EvaluateIsEmptiable ();
182 public abstract void GetExpectedParticles (ArrayList al);
184 public XsdParticleStateManager Manager {
185 get { return manager; }
189 get { return occured; }
192 internal int OccuredInternal {
193 get { return occured; }
194 set { occured = value; }
198 internal class XsdElementValidationState : XsdValidationState
200 public XsdElementValidationState (XmlSchemaElement element, XsdParticleStateManager manager)
203 this.element = element;
207 readonly XmlSchemaElement element;
210 get { return element.QualifiedName.Name; }
214 get { return element.QualifiedName.Namespace; }
218 public override void GetExpectedParticles (ArrayList al)
220 XmlSchemaElement copy = (XmlSchemaElement) MemberwiseClone ();
221 decimal mo = element.ValidatedMinOccurs - Occured;
222 copy.MinOccurs = mo > 0 ? mo : 0;
223 if (element.ValidatedMaxOccurs == decimal.MaxValue)
224 copy.MaxOccursString = "unbounded";
226 copy.MaxOccurs = element.ValidatedMaxOccurs - Occured;
230 public override XsdValidationState EvaluateStartElement (string name, string ns)
232 if (this.Name == name && this.NS == ns && !element.IsAbstract) {
233 return this.CheckOccurence (element);
235 for (int i = 0; i < element.SubstitutingElements.Count; i++) {
236 XmlSchemaElement subst = (XmlSchemaElement) element.SubstitutingElements [i];
237 if (subst.QualifiedName.Name == name &&
238 subst.QualifiedName.Namespace == ns) {
239 return this.CheckOccurence (subst);
242 return XsdValidationState.Invalid;
246 private XsdValidationState CheckOccurence (XmlSchemaElement maybeSubstituted)
249 Manager.CurrentElement = maybeSubstituted;
250 if (Occured > element.ValidatedMaxOccurs) {
251 return XsdValidationState.Invalid;
252 } else if (Occured == element.ValidatedMaxOccurs) {
253 return Manager.Create (XmlSchemaParticle.Empty);
259 public override bool EvaluateEndElement ()
261 return EvaluateIsEmptiable ();
264 internal override bool EvaluateIsEmptiable ()
266 return (element.ValidatedMinOccurs <= Occured &&
267 element.ValidatedMaxOccurs >= Occured);
271 internal class XsdSequenceValidationState : XsdValidationState
273 readonly XmlSchemaSequence seq;
275 XsdValidationState currentAutomata;
278 public XsdSequenceValidationState (XmlSchemaSequence sequence, XsdParticleStateManager manager)
285 public override void GetExpectedParticles (ArrayList al)
287 // if not started, then just collect all items from seq.
288 if (currentAutomata == null) {
289 foreach (XmlSchemaParticle p in seq.CompiledItems) {
291 if (!p.ValidateIsEmptiable ())
297 // automata for ongoing iteration
298 if (currentAutomata != null) {
299 currentAutomata.GetExpectedParticles (al);
300 if (!currentAutomata.EvaluateIsEmptiable ())
303 // remaining items after currentAutomata
304 for (int i = current + 1; i < seq.CompiledItems.Count; i++) {
305 XmlSchemaParticle p = seq.CompiledItems [i] as XmlSchemaParticle;
307 if (!p.ValidateIsEmptiable ())
313 if (Occured + 1 == seq.ValidatedMaxOccurs)
317 for (int i = 0; i <= current; i++)
318 al.Add (seq.CompiledItems [i]);
322 public override XsdValidationState EvaluateStartElement (string name, string ns)
324 if (seq.CompiledItems.Count == 0)
325 return XsdValidationState.Invalid;
327 int idx = current < 0 ? 0 : current;
328 XsdValidationState xa = currentAutomata;
329 // If it is true and when matching particle was found, then
330 // it will increment occurence.
331 bool increment = false;
334 // if (current < 0 || current == seq.CompiledItems.Count) {
335 // idx = current = 0;
338 if (xa == null) { // This code runs in case of a newiteration.
339 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
342 if (xa is XsdEmptyValidationState &&
343 seq.CompiledItems.Count == idx + 1 &&
344 Occured == seq.ValidatedMaxOccurs) {
345 return XsdValidationState.Invalid;
347 XsdValidationState result = xa.EvaluateStartElement (name, ns);
348 if (result == XsdValidationState.Invalid) {
349 if (!xa.EvaluateIsEmptiable ()) {
351 return XsdValidationState.Invalid;
355 currentAutomata = result;
358 if (Occured > seq.ValidatedMaxOccurs)
359 return XsdValidationState.Invalid;
362 // return Manager.MakeSequence (result, this);
364 // skip in other cases.
368 if (idx > current && increment && current >= 0) {
369 return XsdValidationState.Invalid;
371 if (seq.CompiledItems.Count > idx) {
372 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
374 else if (current < 0) { // started from top
375 return XsdValidationState.Invalid;
377 else { // started from middle
384 public override bool EvaluateEndElement ()
386 if (seq.ValidatedMinOccurs > Occured + 1)
388 if (seq.CompiledItems.Count == 0)
390 if (currentAutomata == null && seq.ValidatedMinOccurs <= Occured)
393 int idx = current < 0 ? 0 : current;
394 XsdValidationState xa = currentAutomata;
396 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
398 if (!xa.EvaluateEndElement ())
399 if (!xa.EvaluateIsEmptiable ())
400 return false; // cannot omit following items.
402 if (seq.CompiledItems.Count > idx)
403 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
410 return seq.ValidatedMinOccurs <= Occured && seq.ValidatedMaxOccurs >= Occured;
413 internal override bool EvaluateIsEmptiable ()
\r
415 if (seq.ValidatedMinOccurs > Occured + 1)
\r
417 if (seq.ValidatedMinOccurs == 0 && currentAutomata == null)
\r
422 if (seq.CompiledItems.Count == 0)
\r
425 int idx = current < 0 ? 0 : current;
426 XsdValidationState xa = currentAutomata;
\r
428 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
430 if (!xa.EvaluateIsEmptiable ())
433 if (seq.CompiledItems.Count > idx)
434 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
444 internal class XsdChoiceValidationState : XsdValidationState
446 readonly XmlSchemaChoice choice;
448 bool emptiableComputed;
450 public XsdChoiceValidationState (XmlSchemaChoice choice, XsdParticleStateManager manager)
453 this.choice = choice;
456 public override void GetExpectedParticles (ArrayList al)
458 if (Occured < choice.ValidatedMaxOccurs)
459 foreach (XmlSchemaParticle p in choice.CompiledItems)
463 public override XsdValidationState EvaluateStartElement (string localName, string ns)
465 emptiableComputed = false;
467 for (int i = 0; i < choice.CompiledItems.Count; i++) {
468 XmlSchemaParticle xsobj = (XmlSchemaParticle) choice.CompiledItems [i];
469 XsdValidationState xa = Manager.Create (xsobj);
470 XsdValidationState result = xa.EvaluateStartElement (localName, ns);
471 if (result != XsdValidationState.Invalid) {
473 if (Occured > choice.ValidatedMaxOccurs)
474 return XsdValidationState.Invalid;
475 else if (Occured == choice.ValidatedMaxOccurs)
478 return Manager.MakeSequence (result, this);
481 emptiable = choice.ValidatedMinOccurs <= Occured;
482 emptiableComputed = true;
483 return XsdValidationState.Invalid;
486 public override bool EvaluateEndElement ()
488 emptiableComputed = false;
490 if (choice.ValidatedMinOccurs > Occured + 1)
493 else if (choice.ValidatedMinOccurs <= Occured)
496 for (int i = 0; i < choice.CompiledItems.Count; i++) {
497 XmlSchemaParticle p = (XmlSchemaParticle) choice.CompiledItems [i];
498 if (Manager.Create (p).EvaluateIsEmptiable ())
504 internal override bool EvaluateIsEmptiable ()
\r
506 if (emptiableComputed)
509 if (choice.ValidatedMaxOccurs < Occured)
\r
511 else if (choice.ValidatedMinOccurs > Occured + 1)
\r
514 for (int i = Occured; i < choice.ValidatedMinOccurs; i++) {
\r
516 for (int pi = 0; pi < choice.CompiledItems.Count; pi++) {
517 XmlSchemaParticle p = (XmlSchemaParticle) choice.CompiledItems [pi];
\r
518 if (Manager.Create (p).EvaluateIsEmptiable ()) {
530 internal class XsdAllValidationState : XsdValidationState
532 readonly XmlSchemaAll all;
533 ArrayList consumed = new ArrayList ();
535 public XsdAllValidationState (XmlSchemaAll all, XsdParticleStateManager manager)
541 public override void GetExpectedParticles (ArrayList al)
543 foreach (XmlSchemaParticle p in all.CompiledItems)
544 if (!consumed.Contains (p))
548 public override XsdValidationState EvaluateStartElement (string localName, string ns)
550 if (all.CompiledItems.Count == 0)
551 return XsdValidationState.Invalid;
553 // We don't have to keep element validation state, since
554 // it must occur only 0 or 1.
555 for (int i = 0; i < all.CompiledItems.Count; i++) {
556 XmlSchemaElement xsElem = (XmlSchemaElement) all.CompiledItems [i];
557 if (xsElem.QualifiedName.Name == localName &&
558 xsElem.QualifiedName.Namespace == ns) {
559 if (consumed.Contains (xsElem))
560 return XsdValidationState.Invalid;
561 consumed.Add (xsElem);
562 Manager.CurrentElement = xsElem;
563 OccuredInternal = 1; // xs:all also occurs 0 or 1 always.
567 return XsdValidationState.Invalid;
570 public override bool EvaluateEndElement ()
572 if (all.Emptiable || all.ValidatedMinOccurs == 0)
574 if (all.ValidatedMinOccurs > 0 && consumed.Count == 0)
576 if (all.CompiledItems.Count == consumed.Count)
578 for (int i = 0; i < all.CompiledItems.Count; i++) {
579 XmlSchemaElement el = (XmlSchemaElement) all.CompiledItems [i];
580 if (el.ValidatedMinOccurs > 0 && !consumed.Contains (el))
586 internal override bool EvaluateIsEmptiable ()
\r
588 if (all.Emptiable || all.ValidatedMinOccurs == 0)
590 for (int i = 0; i < all.CompiledItems.Count; i++) {
591 XmlSchemaElement el = (XmlSchemaElement) all.CompiledItems [i];
592 if (el.ValidatedMinOccurs > 0 && !consumed.Contains (el))
599 internal class XsdAnyValidationState : XsdValidationState
601 readonly XmlSchemaAny any;
603 public XsdAnyValidationState (XmlSchemaAny any, XsdParticleStateManager manager)
610 public override void GetExpectedParticles (ArrayList al)
615 public override XsdValidationState EvaluateStartElement (string name, string ns)
617 if (!MatchesNamespace (ns))
618 return XsdValidationState.Invalid;
621 Manager.SetProcessContents (any.ResolvedProcessContents);
622 if (Occured > any.ValidatedMaxOccurs)
623 return XsdValidationState.Invalid;
624 else if (Occured == any.ValidatedMaxOccurs)
625 return Manager.Create (XmlSchemaParticle.Empty);
630 private bool MatchesNamespace (string ns)
634 if (any.HasValueLocal && ns == String.Empty)
636 if (any.HasValueOther && (any.TargetNamespace == "" || any.TargetNamespace != ns))
638 if (any.HasValueTargetNamespace && any.TargetNamespace == ns)
640 for (int i = 0; i < any.ResolvedNamespaces.Count; i++)
641 if (any.ResolvedNamespaces [i] == ns)
646 public override bool EvaluateEndElement ()
648 return EvaluateIsEmptiable ();
651 internal override bool EvaluateIsEmptiable ()
\r
653 return any.ValidatedMinOccurs <= Occured &&
654 any.ValidatedMaxOccurs >= Occured;
658 internal class XsdAppendedValidationState : XsdValidationState
660 public XsdAppendedValidationState (XsdParticleStateManager manager,
661 XsdValidationState head, XsdValidationState rest)
668 XsdValidationState head;
669 XsdValidationState rest;
672 public override void GetExpectedParticles (ArrayList al)
674 head.GetExpectedParticles (al);
675 rest.GetExpectedParticles (al);
678 public override XsdValidationState EvaluateStartElement (string name, string ns)
680 XsdValidationState afterHead = head.EvaluateStartElement (name, ns);
681 if (afterHead != XsdValidationState.Invalid) {
683 return afterHead is XsdEmptyValidationState ? rest : this;
684 } else if (!head.EvaluateIsEmptiable ()) {
685 return XsdValidationState.Invalid;
688 return rest.EvaluateStartElement (name, ns);
691 public override bool EvaluateEndElement ()
693 if (head.EvaluateEndElement ())
695 return rest.EvaluateIsEmptiable ();
696 if (!head.EvaluateIsEmptiable ())
698 return rest.EvaluateEndElement ();
701 internal override bool EvaluateIsEmptiable ()
\r
703 if (head.EvaluateIsEmptiable ())
\r
704 return rest.EvaluateIsEmptiable ();
\r
710 internal class XsdEmptyValidationState : XsdValidationState
712 public XsdEmptyValidationState (XsdParticleStateManager manager)
719 public override void GetExpectedParticles (ArrayList al)
724 public override XsdValidationState EvaluateStartElement (string name, string ns)
726 return XsdValidationState.Invalid;
729 public override bool EvaluateEndElement ()
734 internal override bool EvaluateIsEmptiable ()
\r
741 internal class XsdInvalidValidationState : XsdValidationState
743 internal XsdInvalidValidationState (XsdParticleStateManager manager)
750 public override void GetExpectedParticles (ArrayList al)
755 public override XsdValidationState EvaluateStartElement (string name, string ns)
760 public override bool EvaluateEndElement ()
765 internal override bool EvaluateIsEmptiable ()
\r