2003-10-26 Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
[mono.git] / mcs / class / System.XML / Mono.Xml.Schema / XsdParticleValidationState.cs
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 //
9 using System;
10 using System.Collections;
11 using System.Xml;
12 using System.Xml.Schema;
13 using Mono.Xml;
14
15 namespace Mono.Xml.Schema
16 {
17         public enum XsdParticleEvaluationResult
18         {
19                 Matched = 1,    // Matched one of its components.
20                 Passed = 2,     // Did not match, but it successfully passed the whole components.
21                 InvalidIncomplete = 3,  // It had incomplete validation state, and in fact it failed to pass.
22                 Mismatched = 4  // Dis not match, 
23         }
24
25         public class XsdValidationStateManager
26         {
27                 Hashtable table;
28                 XmlSchemaElement currentElement;
29                 XmlSchemaContentProcessing processContents;
30
31                 public XsdValidationStateManager ()
32                 {
33                         table = new Hashtable ();
34                         processContents = XmlSchemaContentProcessing.Strict; // not Lax
35                 }
36
37                 public XmlSchemaElement CurrentElement {
38                         get { return currentElement; }
39                         set { currentElement = value; }
40                 }
41
42                 internal void SetCurrentElement (XmlSchemaElement elt)
43                 {
44                         this.currentElement = elt;
45                 }
46
47                 public XmlSchemaContentProcessing ProcessContents {
48                         get { return processContents; }
49                 }
50
51                 internal void SetProcessContents (XmlSchemaContentProcessing value)
52                 {
53                         this.processContents = value;
54                 }
55
56                 public XsdValidationState Get (XmlSchemaParticle xsobj)
57                 {
58                         XsdValidationState got = table [xsobj] as XsdValidationState;
59                         if (got == null)
60                                 got = Create (xsobj);
61                         return got;
62                 }
63
64                 public XsdValidationState Create (XmlSchemaObject xsobj)
65                 {
66                         string typeName = xsobj.GetType ().Name;
67                         switch (typeName) {
68                         case "XmlSchemaElement":
69                                 return AddElement ((XmlSchemaElement) xsobj);
70                         case "XmlSchemaGroupRef":
71                                 return AddGroup ((XmlSchemaGroupRef) xsobj);
72                         case "XmlSchemaSequence":
73                                 return AddSequence ((XmlSchemaSequence) xsobj);
74                         case "XmlSchemaChoice":
75                                 return AddChoice ((XmlSchemaChoice) xsobj);
76                         case "XmlSchemaAll":
77                                 return AddAll ((XmlSchemaAll) xsobj);
78                         case "XmlSchemaAny":
79                                 return AddAny ((XmlSchemaAny) xsobj);
80                         case "EmptyParticle":   // Microsoft.NET
81                         case "XmlSchemaParticleEmpty":  // Mono
82                                 return AddEmpty ();
83                         default:
84                                 throw new InvalidOperationException (); // Should not occur.
85                         }
86                 }
87
88                 internal XsdValidationState MakeSequence (XsdValidationState head, XsdValidationState rest)
89                 {
90                         if (head is XsdEmptyValidationState)
91                                 return rest;
92                         else
93                                 return new XsdAppendedValidationState (this, head, rest);
94                 }
95
96                 private XsdElementValidationState AddElement (XmlSchemaElement element)
97                 {
98                         XsdElementValidationState got = new XsdElementValidationState (element, this);
99 //                      table [element] = got;
100                         return got;
101                 }
102
103                 private XsdGroupValidationState AddGroup (XmlSchemaGroupRef groupRef)
104                 {
105                         XsdGroupValidationState got = new XsdGroupValidationState (groupRef, this);
106 //                      table [groupRef] = got;
107                         return got;
108                 }
109
110                 private XsdSequenceValidationState AddSequence (XmlSchemaSequence sequence)
111                 {
112                         XsdSequenceValidationState got = new XsdSequenceValidationState (sequence, this);
113 //                      table [sequence] = got;
114                         return got;
115                 }
116
117                 private XsdChoiceValidationState AddChoice (XmlSchemaChoice choice)
118                 {
119                         XsdChoiceValidationState got = new XsdChoiceValidationState (choice, this);
120 //                      table [choice] = got;
121                         return got;
122                 }
123
124                 private XsdAllValidationState AddAll (XmlSchemaAll all)
125                 {
126                         XsdAllValidationState got = new XsdAllValidationState (all, this);
127 //                      table [all] = got;
128                         return got;
129                 }
130
131                 private XsdAnyValidationState AddAny (XmlSchemaAny any)
132                 {
133                         XsdAnyValidationState got = new XsdAnyValidationState (any, this);
134 //                      table [any] = got;
135                         return got;
136                 }
137
138                 private XsdEmptyValidationState AddEmpty ()
139                 {
140                         return new XsdEmptyValidationState (this);
141                 }
142         }
143
144         public abstract class XsdValidationState
145         {
146                 // Static members
147
148                 static XsdInvalidValidationState invalid;
149
150                 static XsdValidationState ()
151                 {
152                         invalid = new XsdInvalidValidationState (null);
153                 }
154
155                 public static XsdInvalidValidationState Invalid {
156                         get { return invalid; }
157                 }
158
159                 // Dynamic members
160
161                 int occured;
162                 string message;
163                 XsdValidationStateManager manager;
164
165                 public XsdValidationState (XsdValidationStateManager manager)
166                 {
167                         this.manager = manager;
168                 }
169
170                 // Normally checks MaxOccurs boundary
171                 public abstract XsdValidationState EvaluateStartElement (string localName, string ns);
172
173                 // Normally checks MinOccurs boundary
174                 public abstract bool EvaluateEndElement ();
175
176                 internal abstract bool EvaluateIsEmptiable ();
177
178                 public XsdValidationStateManager Manager {
179                         get { return manager; }
180                 }
181
182                 public string Message {
183                         get { return message; }
184                 }
185
186                 public string MessageInternal {
187                         get { return message; }
188                         set { message = value; }
189                 }
190
191                 public int Occured {
192                         get { return occured; }
193                 }
194
195                 internal int OccuredInternal {
196                         get { return occured; }
197                         set { occured = value; }
198                 }
199         }
200
201         public class XsdElementValidationState : XsdValidationState
202         {
203                 public XsdElementValidationState (XmlSchemaElement element, XsdValidationStateManager manager)
204                         : base (manager)
205                 {
206                         this.element = element;
207                         name = element.QualifiedName.Name;
208                         ns = element.QualifiedName.Namespace;
209                 }
210
211                 // final fields
212                 XmlSchemaElement element;
213                 string name;
214                 string ns;
215
216                 // Methods
217                 
218                 public override XsdValidationState EvaluateStartElement (string name, string ns)
219                 {
220                         if (this.name == name && this.ns == ns && !element.IsAbstract) {
221                                 return this.CheckOccurence (element);
222                         } else {
223                                 foreach (XmlSchemaElement subst in element.SubstitutingElements) {
224                                         if (subst.QualifiedName.Name == name &&
225                                                 subst.QualifiedName.Namespace == ns) {
226                                                 return this.CheckOccurence (subst);
227                                         }
228                                 }
229                                 return XsdValidationState.Invalid;
230                         }
231                 }
232
233                 private XsdValidationState CheckOccurence (XmlSchemaElement maybeSubstituted)
234                 {
235                         OccuredInternal++;
236                         Manager.SetCurrentElement (maybeSubstituted);
237                         if (Occured > element.ValidatedMaxOccurs) {
238                                 MessageInternal = "Element occurence excess.";
239                                 return XsdValidationState.Invalid;
240                         } else if (Occured == element.ValidatedMaxOccurs) {
241                                 return Manager.Create (XmlSchemaParticle.Empty);
242                         } else {
243                                 return this;
244                         }
245                 }
246
247                 public override bool EvaluateEndElement ()
248                 {
249                         return EvaluateIsEmptiable ();
250                 }
251
252                 internal override bool EvaluateIsEmptiable ()
253                 {
254                         return (element.ValidatedMinOccurs <= Occured &&
255                                 element.ValidatedMaxOccurs >= Occured);
256                 }
257         }
258
259         public class XsdGroupValidationState : XsdValidationState
260         {
261                 public XsdGroupValidationState (XmlSchemaGroupRef groupRef, XsdValidationStateManager manager)
262                         : base (manager)
263                 {
264                         this.groupRef = groupRef;
265                 }
266
267                 XmlSchemaGroupRef groupRef;
268
269                 // Methods
270
271                 public override XsdValidationState EvaluateStartElement (string name, string ns)
272                 {
273                         XsdValidationState xa = Manager.Create (groupRef.Particle);
274                         XsdValidationState result = xa.EvaluateStartElement (name, ns);
275                         if (result == XsdValidationState.Invalid)
276                                 return result;
277
278                         OccuredInternal++;
279                         if (OccuredInternal > groupRef.MaxOccurs)
280                                 return XsdValidationState.Invalid;
281                         return Manager.MakeSequence (result, this);
282                 }
283
284                 public override bool EvaluateEndElement ()
285                 {
286                         if (groupRef.ValidatedMinOccurs > Occured + 1)
287                                 return false;
288                         else if (groupRef.ValidatedMinOccurs <= Occured)
289                                 return true;
290                         return Manager.Create (groupRef.Particle).EvaluateIsEmptiable ();
291                 }
292
293                 internal override bool EvaluateIsEmptiable ()\r
294                 {\r
295                         return (groupRef.ValidatedMinOccurs <= Occured);\r
296                 }\r
297         }
298
299         public class XsdSequenceValidationState : XsdValidationState
300         {
301                 XmlSchemaSequence seq;
302                 int current;
303                 XsdValidationState currentAutomata;
304                 bool emptiable;
305                 decimal minOccurs;
306                 decimal maxOccurs;
307
308                 public XsdSequenceValidationState (XmlSchemaSequence sequence, XsdValidationStateManager manager)
309                         : this (sequence, manager, sequence.ValidatedMinOccurs, sequence.ValidatedMaxOccurs, -1)
310                 {
311                 }
312
313                 public XsdSequenceValidationState (XmlSchemaSequence sequence, XsdValidationStateManager manager,
314                         decimal minOccurs, decimal maxOccurs, int current)
315                         : base (manager)
316                 {
317                         seq = sequence;
318                         this.minOccurs = minOccurs;
319                         this.maxOccurs = maxOccurs;
320                         this.current = current;
321                 }
322
323                 public override XsdValidationState EvaluateStartElement (string name, string ns)
324                 {
325                         if (seq.CompiledItems.Count == 0)
326                                 return XsdValidationState.Invalid;
327
328                         int idx = current < 0 ? 0 : current;
329                         XsdValidationState xa = currentAutomata;
330                         // If it is true and when matching particle was found, then
331                         // it will increment occurence.
332                         bool increment = false;
333
334                         while (true) {
335 //                              if (current < 0 || current == seq.CompiledItems.Count) {
336 //                                      idx = current = 0;
337 //                                      increment = true;
338 //                              }
339                                 if (xa == null) {       // This code runs in case of a newiteration.
340                                         xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
341                                         increment = true;
342                                 }
343                                 if (xa is XsdEmptyValidationState &&
344                                                 seq.CompiledItems.Count == idx + 1 &&
345                                                 Occured == maxOccurs) {
346                                         return XsdValidationState.Invalid;
347                                 } else {
348                                         XsdValidationState result = xa.EvaluateStartElement (name, ns);
349                                         if (result == XsdValidationState.Invalid) {
350                                                 if (!xa.EvaluateIsEmptiable ()) {
351                                                         emptiable = false;
352                                                         return XsdValidationState.Invalid;
353                                                 }
354                                         } else {
355                                                 current = idx;
356                                                 currentAutomata = result;
357                                                 if (increment) {
358                                                         OccuredInternal++;
359                                                         if (Occured > maxOccurs)
360                                                                 return XsdValidationState.Invalid;
361                                                 }
362 //                                              current++;
363 //                                              return Manager.MakeSequence (result, this);
364                                                 return this;
365                                         // skip in other cases.
366                                         }
367                                 }
368                                 idx++;
369                                 if (idx > current && increment && current >= 0) {
370                                         return XsdValidationState.Invalid;
371                                 }
372                                 if (seq.CompiledItems.Count > idx) {
373                                         xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
374                                 }
375                                 else if (current < 0) { // started from top
376                                         return XsdValidationState.Invalid;
377                                 }
378                                 else {          // started from middle
379                                         idx = 0;
380                                         xa = null;
381                                 }
382                         }
383                         return XsdValidationState.Invalid;
384                 }
385
386                 public override bool EvaluateEndElement ()
387                 {
388                         if (minOccurs > Occured + 1)
389                                 return false;
390                         if (seq.CompiledItems.Count == 0)
391                                 return true;
392                         if (currentAutomata == null && minOccurs <= Occured)
393                                 return true;
394
395                         int idx = current < 0 ? 0 : current;
396                         XsdValidationState xa = currentAutomata;
397                         if (xa == null)
398                                 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
399                         while (xa != null) {
400                                 if (!xa.EvaluateEndElement ())
401                                         if (!xa.EvaluateIsEmptiable ())
402                                                 return false;   // cannot omit following items.
403                                 idx++;
404                                 if (seq.CompiledItems.Count > idx)
405                                         xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
406                                 else
407                                         xa = null;
408                         }
409                         if (current < 0)
410                                 OccuredInternal++;
411
412                         return minOccurs <= Occured && maxOccurs >= Occured;
413                 }
414
415                 internal override bool EvaluateIsEmptiable ()\r
416                 {\r
417                         if (minOccurs > Occured + 1)\r
418                                 return false;\r
419                         if (minOccurs == 0 && currentAutomata == null)\r
420                                 return true;\r
421 \r
422                         if (emptiable)\r
423                                 return true;\r
424                         if (seq.CompiledItems.Count == 0)\r
425                                 return true;\r
426 \r
427                         int idx = current < 0 ? 0 : current;
428                         XsdValidationState xa = currentAutomata;\r
429                         if (xa == null)
430                                 xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
431                         while (xa != null) {
432                                 if (!xa.EvaluateIsEmptiable ())
433                                         return false;
434                                 idx++;
435                                 if (seq.CompiledItems.Count > idx)
436                                         xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
437                                 else
438                                         xa = null;
439                         }
440                         emptiable = true;
441                         return true;
442                 }\r
443
444         }
445
446         public class XsdChoiceValidationState : XsdValidationState
447         {
448                 XmlSchemaChoice choice;
449                 bool emptiable;
450                 bool emptiableComputed;
451
452                 public XsdChoiceValidationState (XmlSchemaChoice choice, XsdValidationStateManager manager)
453                         : base (manager)
454                 {
455                         this.choice = choice;
456                 }
457
458                 public override XsdValidationState EvaluateStartElement (string localName, string ns)
459                 {
460                         emptiableComputed = false;
461
462                         foreach (XmlSchemaParticle xsobj in choice.CompiledItems) {
463                                 XsdValidationState xa = Manager.Create (xsobj);
464                                 XsdValidationState result = xa.EvaluateStartElement (localName, ns);
465                                 if (result != XsdValidationState.Invalid) {
466                                         OccuredInternal++;
467                                         if (Occured > choice.ValidatedMaxOccurs)
468                                                 return XsdValidationState.Invalid;
469                                         else if (Occured == choice.ValidatedMaxOccurs)
470                                                 return result;
471                                         else
472                                                 return Manager.MakeSequence (result, this);
473                                 }
474                         }
475                         emptiable = choice.ValidatedMinOccurs <= Occured;
476                         emptiableComputed = true;
477                         return XsdValidationState.Invalid;
478                 }
479
480                 public override bool EvaluateEndElement ()
481                 {
482                         emptiableComputed = false;
483
484                         if (choice.ValidatedMinOccurs > Occured + 1)
485                                 return false;
486
487                         else if (choice.ValidatedMinOccurs <= Occured)
488                                 return true;
489
490                         foreach (XmlSchemaParticle p in choice.CompiledItems)
491                                 if (Manager.Create (p).EvaluateIsEmptiable ())
492                                         return true;
493                         return false;
494                 }
495
496                 internal override bool EvaluateIsEmptiable ()\r
497                 {\r
498                         if (emptiableComputed)
499                                 return emptiable;
500 \r
501                         if (choice.ValidatedMaxOccurs < Occured)\r
502                                 return false;\r
503                         else if (choice.ValidatedMinOccurs > Occured + 1)\r
504                                 return false;\r
505 \r
506                         for (int i = Occured; i < choice.ValidatedMinOccurs; i++) {\r
507                                 bool next = false;\r
508                                 foreach (XmlSchemaParticle p in choice.CompiledItems) {\r
509                                         if (Manager.Create (p).EvaluateIsEmptiable ()) {
510                                                 next = true;
511                                                 break;
512                                         }
513                                 }\r
514                                 if (!next)\r
515                                         return false;\r
516                         }\r
517                         return true;\r
518                 }\r
519         }
520
521         public class XsdAllValidationState : XsdValidationState
522         {
523                 XmlSchemaAll all;
524                 ArrayList consumed = new ArrayList ();
525
526                 public XsdAllValidationState (XmlSchemaAll all, XsdValidationStateManager manager)
527                         : base (manager)
528                 {
529                         this.all = all;
530                 }
531
532                 public override XsdValidationState EvaluateStartElement (string localName, string ns)
533                 {
534                         if (all.CompiledItems.Count == 0)
535                                 return XsdValidationState.Invalid;
536
537                         // We don't have to keep element validation state, since 
538                         // it must occur only 0 or 1.
539                         foreach (XmlSchemaElement xsElem in all.CompiledItems) {
540                                 if (xsElem.QualifiedName.Name == localName &&
541                                         xsElem.QualifiedName.Namespace == ns) {
542                                         if (consumed.Contains (xsElem))
543                                                 return XsdValidationState.Invalid;
544                                         consumed.Add (xsElem);
545                                         Manager.SetCurrentElement (xsElem);
546                                         OccuredInternal = 1;    // xs:all also occurs 0 or 1 always.
547                                         return this;
548                                 }
549                         }
550                         return XsdValidationState.Invalid;
551                 }
552
553                 public override bool EvaluateEndElement ()
554                 {
555                         if (all.Emptiable || all.ValidatedMinOccurs == 0)
556                                 return true;
557                         if (all.ValidatedMinOccurs > 0 && consumed.Count == 0)
558                                 return false;
559                         if (all.CompiledItems.Count == consumed.Count)
560                                 return true;
561                         foreach (XmlSchemaElement el in all.CompiledItems)
562                                 if (el.MinOccurs > 0 && !consumed.Contains (el))
563                                         return false;
564                         return true;
565                 }
566
567                 internal override bool EvaluateIsEmptiable ()\r
568                 {\r
569                         if (all.Emptiable || all.ValidatedMinOccurs == 0)
570                                 return true;
571                         foreach (XmlSchemaElement el in all.CompiledItems)
572                                 if (el.MinOccurs > 0 && !consumed.Contains (el))
573                                         return false;
574                         return true;
575                 }\r
576         }
577
578         public class XsdAnyValidationState : XsdValidationState
579         {
580                 XmlSchemaAny any;
581
582                 public XsdAnyValidationState (XmlSchemaAny any, XsdValidationStateManager manager)
583                         : base (manager)
584                 {
585                         this.any = any;
586                 }
587
588                 // Methods
589                 public override XsdValidationState EvaluateStartElement (string name, string ns)
590                 {
591                         if (!MatchesNamespace (ns))
592                                 return XsdValidationState.Invalid;
593
594                         OccuredInternal++;
595                         Manager.SetProcessContents (any.ProcessContents);
596                         if (Occured > any.ValidatedMaxOccurs)
597                                 return XsdValidationState.Invalid;
598                         else if (Occured == any.ValidatedMaxOccurs)
599                                 return Manager.Create (XmlSchemaParticle.Empty);
600                         else
601                                 return this;
602                 }
603
604                 private bool MatchesNamespace (string ns)
605                 {
606                         if (any.HasValueAny)
607                                 return true;
608                         if (any.HasValueLocal && ns == String.Empty)
609                                 return true;
610                         if (any.HasValueOther && (any.TargetNamespace == "" || any.TargetNamespace != ns))
611                                 return true;
612                         if (any.HasValueTargetNamespace && any.TargetNamespace == ns)
613                                 return true;
614                         foreach (string iter in any.ResolvedNamespaces)
615                                 if (iter == ns)
616                                         return true;
617                         return false;
618                 }
619
620                 public override bool EvaluateEndElement ()
621                 {
622                         return EvaluateIsEmptiable ();
623                 }
624
625                 internal override bool EvaluateIsEmptiable ()\r
626                 {\r
627                         return any.ValidatedMinOccurs <= Occured &&
628                                 any.ValidatedMaxOccurs >= Occured;
629                 }\r
630         }
631
632         public class XsdAppendedValidationState : XsdValidationState
633         {
634                 public XsdAppendedValidationState (XsdValidationStateManager manager,
635                         XsdValidationState head, XsdValidationState rest)
636                         : base (manager)
637                 {
638                         this.head = head;
639                         this.rest = rest;
640                 }
641
642                 XsdValidationState head;
643                 XsdValidationState rest;
644
645                 // Methods
646                 public override XsdValidationState EvaluateStartElement (string name, string ns)
647                 {
648                         XsdValidationState afterHead = head.EvaluateStartElement (name, ns);
649                         if (afterHead != XsdValidationState.Invalid) {
650                                 head = afterHead;
651                                 return afterHead is XsdEmptyValidationState ? rest : this;
652                         } else if (!head.EvaluateIsEmptiable ()) {
653                                 return XsdValidationState.Invalid;
654                         }
655
656                         return rest.EvaluateStartElement (name, ns);
657                 }
658
659                 public override bool EvaluateEndElement ()
660                 {
661                         if (head.EvaluateEndElement ())
662 //                              return true;
663                                 return rest.EvaluateIsEmptiable ();
664                         if (!head.EvaluateIsEmptiable ())
665                                 return false;
666                         return rest.EvaluateEndElement ();
667                 }
668
669                 internal override bool EvaluateIsEmptiable ()\r
670                 {\r
671                         if (head.EvaluateIsEmptiable ())\r
672                                 return rest.EvaluateIsEmptiable ();\r
673                         else\r
674                                 return false;\r
675                 }\r
676         }
677
678         public class XsdEmptyValidationState : XsdValidationState
679         {
680                 public XsdEmptyValidationState (XsdValidationStateManager manager)
681                         : base (manager)
682                 {
683                 }
684
685                 // Methods
686                 public override XsdValidationState EvaluateStartElement (string name, string ns)
687                 {
688                         return XsdValidationState.Invalid;
689                 }
690
691                 public override bool EvaluateEndElement ()
692                 {
693                         return true;
694                 }
695
696                 internal override bool EvaluateIsEmptiable ()\r
697                 {\r
698                         return true;\r
699                 }\r
700 \r
701         }
702
703         public class XsdInvalidValidationState : XsdValidationState
704         {
705                 internal XsdInvalidValidationState (XsdValidationStateManager manager)
706                         : base (manager)
707                 {
708                 }
709
710                 // Methods
711                 public override XsdValidationState EvaluateStartElement (string name, string ns)
712                 {
713                         return this;
714                 }
715
716                 public override bool EvaluateEndElement ()
717                 {
718                         return false;
719                 }
720
721                 internal override bool EvaluateIsEmptiable ()\r
722                 {\r
723                         return false;\r
724                 }\r
725 \r
726         }
727 }