**** Merged from MCS ****
[mono-project.git] / mcs / class / System.XML / Mono.Xml.Schema / XsdParticleValidationState.cs
blob38a1929510d63bb1862bd7be013f39b98d2733c9
1 //
2 // Mono.Xml.Schema.XsdParticleValidationState.cs
3 //
4 // Author:
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 // (C)2003 Atsushi Enomoto
8 //
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:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
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.
30 using System;
31 using System.Collections;
32 using System.Xml;
33 using System.Xml.Schema;
34 using Mono.Xml;
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
48 Hashtable table;
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;
80 if (got == null)
81 got = Create (xsobj);
82 return got;
85 public XsdValidationState Create (XmlSchemaObject xsobj)
87 string typeName = xsobj.GetType ().Name;
88 switch (typeName) {
89 case "XmlSchemaElement":
90 return AddElement ((XmlSchemaElement) xsobj);
91 case "XmlSchemaSequence":
92 return AddSequence ((XmlSchemaSequence) xsobj);
93 case "XmlSchemaChoice":
94 return AddChoice ((XmlSchemaChoice) xsobj);
95 case "XmlSchemaAll":
96 return AddAll ((XmlSchemaAll) xsobj);
97 case "XmlSchemaAny":
98 return AddAny ((XmlSchemaAny) xsobj);
99 case "EmptyParticle":
100 return AddEmpty ();
101 default:
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)
110 return rest;
111 else
112 return new XsdAppendedValidationState (this, head, rest);
115 private XsdElementValidationState AddElement (XmlSchemaElement element)
117 XsdElementValidationState got = new XsdElementValidationState (element, this);
118 return got;
121 private XsdSequenceValidationState AddSequence (XmlSchemaSequence sequence)
123 XsdSequenceValidationState got = new XsdSequenceValidationState (sequence, this);
124 return got;
127 private XsdChoiceValidationState AddChoice (XmlSchemaChoice choice)
129 XsdChoiceValidationState got = new XsdChoiceValidationState (choice, this);
130 return got;
133 private XsdAllValidationState AddAll (XmlSchemaAll all)
135 XsdAllValidationState got = new XsdAllValidationState (all, this);
136 return got;
139 private XsdAnyValidationState AddAny (XmlSchemaAny any)
141 XsdAnyValidationState got = new XsdAnyValidationState (any, this);
142 return got;
145 private XsdEmptyValidationState AddEmpty ()
147 return new XsdEmptyValidationState (this);
151 internal abstract class XsdValidationState
153 // Static members
155 static XsdInvalidValidationState invalid;
157 static XsdValidationState ()
159 invalid = new XsdInvalidValidationState (null);
162 public static XsdInvalidValidationState Invalid {
163 get { return invalid; }
166 // Dynamic members
168 int occured;
169 string message;
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; }
198 public int Occured {
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)
211 : base (manager)
213 this.element = element;
214 name = element.QualifiedName.Name;
215 ns = element.QualifiedName.Namespace;
218 // final fields
219 XmlSchemaElement element;
220 string name;
221 string ns;
223 // Methods
225 public override XsdValidationState EvaluateStartElement (string name, string ns)
227 if (this.name == name && this.ns == ns && !element.IsAbstract) {
228 return this.CheckOccurence (element);
229 } else {
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)
243 OccuredInternal++;
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);
250 } else {
251 return this;
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;
270 int current;
271 XsdValidationState currentAutomata;
272 bool emptiable;
273 decimal minOccurs;
274 decimal maxOccurs;
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)
283 : base (manager)
285 seq = sequence;
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;
302 while (true) {
303 // if (current < 0 || current == seq.CompiledItems.Count) {
304 // idx = current = 0;
305 // increment = true;
306 // }
307 if (xa == null) { // This code runs in case of a newiteration.
308 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
309 increment = true;
311 if (xa is XsdEmptyValidationState &&
312 seq.CompiledItems.Count == idx + 1 &&
313 Occured == maxOccurs) {
314 return XsdValidationState.Invalid;
315 } else {
316 XsdValidationState result = xa.EvaluateStartElement (name, ns);
317 if (result == XsdValidationState.Invalid) {
318 if (!xa.EvaluateIsEmptiable ()) {
319 emptiable = false;
320 return XsdValidationState.Invalid;
322 } else {
323 current = idx;
324 currentAutomata = result;
325 if (increment) {
326 OccuredInternal++;
327 if (Occured > maxOccurs)
328 return XsdValidationState.Invalid;
330 // current++;
331 // return Manager.MakeSequence (result, this);
332 return this;
333 // skip in other cases.
336 idx++;
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
347 idx = 0;
348 xa = null;
353 public override bool EvaluateEndElement ()
355 if (minOccurs > Occured + 1)
356 return false;
357 if (seq.CompiledItems.Count == 0)
358 return true;
359 if (currentAutomata == null && minOccurs <= Occured)
360 return true;
362 int idx = current < 0 ? 0 : current;
363 XsdValidationState xa = currentAutomata;
364 if (xa == null)
365 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
366 while (xa != null) {
367 if (!xa.EvaluateEndElement ())
368 if (!xa.EvaluateIsEmptiable ())
369 return false; // cannot omit following items.
370 idx++;
371 if (seq.CompiledItems.Count > idx)
372 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
373 else
374 xa = null;
376 if (current < 0)
377 OccuredInternal++;
379 return minOccurs <= Occured && maxOccurs >= Occured;
382 internal override bool EvaluateIsEmptiable ()
384 if (minOccurs > Occured + 1)
385 return false;
386 if (minOccurs == 0 && currentAutomata == null)
387 return true;
389 if (emptiable)
390 return true;
391 if (seq.CompiledItems.Count == 0)
392 return true;
394 int idx = current < 0 ? 0 : current;
395 XsdValidationState xa = currentAutomata;
396 if (xa == null)
397 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
398 while (xa != null) {
399 if (!xa.EvaluateIsEmptiable ())
400 return false;
401 idx++;
402 if (seq.CompiledItems.Count > idx)
403 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
404 else
405 xa = null;
407 emptiable = true;
408 return true;
413 internal class XsdChoiceValidationState : XsdValidationState
415 XmlSchemaChoice choice;
416 bool emptiable;
417 bool emptiableComputed;
419 public XsdChoiceValidationState (XmlSchemaChoice choice, XsdValidationStateManager manager)
420 : base (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) {
434 OccuredInternal++;
435 if (Occured > choice.ValidatedMaxOccurs)
436 return XsdValidationState.Invalid;
437 else if (Occured == choice.ValidatedMaxOccurs)
438 return result;
439 else
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)
453 return false;
455 else if (choice.ValidatedMinOccurs <= Occured)
456 return true;
458 for (int i = 0; i < choice.CompiledItems.Count; i++) {
459 XmlSchemaParticle p = (XmlSchemaParticle) choice.CompiledItems [i];
460 if (Manager.Create (p).EvaluateIsEmptiable ())
461 return true;
463 return false;
466 internal override bool EvaluateIsEmptiable ()
468 if (emptiableComputed)
469 return emptiable;
471 if (choice.ValidatedMaxOccurs < Occured)
472 return false;
473 else if (choice.ValidatedMinOccurs > Occured + 1)
474 return false;
476 for (int i = Occured; i < choice.ValidatedMinOccurs; i++) {
477 bool next = false;
478 for (int pi = 0; pi < choice.CompiledItems.Count; pi++) {
479 XmlSchemaParticle p = (XmlSchemaParticle) choice.CompiledItems [pi];
480 if (Manager.Create (p).EvaluateIsEmptiable ()) {
481 next = true;
482 break;
485 if (!next)
486 return false;
488 return true;
492 internal class XsdAllValidationState : XsdValidationState
494 XmlSchemaAll all;
495 ArrayList consumed = new ArrayList ();
497 public XsdAllValidationState (XmlSchemaAll all, XsdValidationStateManager manager)
498 : base (manager)
500 this.all = all;
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.
519 return this;
522 return XsdValidationState.Invalid;
525 public override bool EvaluateEndElement ()
527 if (all.Emptiable || all.ValidatedMinOccurs == 0)
528 return true;
529 if (all.ValidatedMinOccurs > 0 && consumed.Count == 0)
530 return false;
531 if (all.CompiledItems.Count == consumed.Count)
532 return true;
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))
536 return false;
538 return true;
541 internal override bool EvaluateIsEmptiable ()
543 if (all.Emptiable || all.ValidatedMinOccurs == 0)
544 return true;
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))
548 return false;
550 return true;
554 internal class XsdAnyValidationState : XsdValidationState
556 XmlSchemaAny any;
558 public XsdAnyValidationState (XmlSchemaAny any, XsdValidationStateManager manager)
559 : base (manager)
561 this.any = any;
564 // Methods
565 public override XsdValidationState EvaluateStartElement (string name, string ns)
567 if (!MatchesNamespace (ns))
568 return XsdValidationState.Invalid;
570 OccuredInternal++;
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);
576 else
577 return this;
580 private bool MatchesNamespace (string ns)
582 if (any.HasValueAny)
583 return true;
584 if (any.HasValueLocal && ns == String.Empty)
585 return true;
586 if (any.HasValueOther && (any.TargetNamespace == "" || any.TargetNamespace != ns))
587 return true;
588 if (any.HasValueTargetNamespace && any.TargetNamespace == ns)
589 return true;
590 for (int i = 0; i < any.ResolvedNamespaces.Count; i++)
591 if (any.ResolvedNamespaces [i] == ns)
592 return true;
593 return false;
596 public override bool EvaluateEndElement ()
598 return EvaluateIsEmptiable ();
601 internal override bool EvaluateIsEmptiable ()
603 return any.ValidatedMinOccurs <= Occured &&
604 any.ValidatedMaxOccurs >= Occured;
608 internal class XsdAppendedValidationState : XsdValidationState
610 public XsdAppendedValidationState (XsdValidationStateManager manager,
611 XsdValidationState head, XsdValidationState rest)
612 : base (manager)
614 this.head = head;
615 this.rest = rest;
618 XsdValidationState head;
619 XsdValidationState rest;
621 // Methods
622 public override XsdValidationState EvaluateStartElement (string name, string ns)
624 XsdValidationState afterHead = head.EvaluateStartElement (name, ns);
625 if (afterHead != XsdValidationState.Invalid) {
626 head = afterHead;
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 ())
638 // return true;
639 return rest.EvaluateIsEmptiable ();
640 if (!head.EvaluateIsEmptiable ())
641 return false;
642 return rest.EvaluateEndElement ();
645 internal override bool EvaluateIsEmptiable ()
647 if (head.EvaluateIsEmptiable ())
648 return rest.EvaluateIsEmptiable ();
649 else
650 return false;
654 internal class XsdEmptyValidationState : XsdValidationState
656 public XsdEmptyValidationState (XsdValidationStateManager manager)
657 : base (manager)
661 // Methods
662 public override XsdValidationState EvaluateStartElement (string name, string ns)
664 return XsdValidationState.Invalid;
667 public override bool EvaluateEndElement ()
669 return true;
672 internal override bool EvaluateIsEmptiable ()
674 return true;
679 internal class XsdInvalidValidationState : XsdValidationState
681 internal XsdInvalidValidationState (XsdValidationStateManager manager)
682 : base (manager)
686 // Methods
687 public override XsdValidationState EvaluateStartElement (string name, string ns)
689 return this;
692 public override bool EvaluateEndElement ()
694 return false;
697 internal override bool EvaluateIsEmptiable ()
699 return false;