This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[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
10 //
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.
29 //
30 using System;
31 using System.Collections;
32 using System.Xml;
33 using System.Xml.Schema;
34 using Mono.Xml;
35
36 namespace Mono.Xml.Schema
37 {
38         internal enum XsdParticleEvaluationResult
39         {
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, 
44         }
45
46         internal class XsdValidationStateManager
47         {
48                 Hashtable table;
49                 XmlSchemaElement currentElement;
50                 XmlSchemaContentProcessing processContents;
51
52                 public XsdValidationStateManager ()
53                 {
54                         table = new Hashtable ();
55                         processContents = XmlSchemaContentProcessing.Strict; // not Lax
56                 }
57
58                 public XmlSchemaElement CurrentElement {
59                         get { return currentElement; }
60                         set { currentElement = value; }
61                 }
62
63                 internal void SetCurrentElement (XmlSchemaElement elt)
64                 {
65                         this.currentElement = elt;
66                 }
67
68                 public XmlSchemaContentProcessing ProcessContents {
69                         get { return processContents; }
70                 }
71
72                 internal void SetProcessContents (XmlSchemaContentProcessing value)
73                 {
74                         this.processContents = value;
75                 }
76
77                 public XsdValidationState Get (XmlSchemaParticle xsobj)
78                 {
79                         XsdValidationState got = table [xsobj] as XsdValidationState;
80                         if (got == null)
81                                 got = Create (xsobj);
82                         return got;
83                 }
84
85                 public XsdValidationState Create (XmlSchemaObject xsobj)
86                 {
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.");
104                         }
105                 }
106
107                 internal XsdValidationState MakeSequence (XsdValidationState head, XsdValidationState rest)
108                 {
109                         if (head is XsdEmptyValidationState)
110                                 return rest;
111                         else
112                                 return new XsdAppendedValidationState (this, head, rest);
113                 }
114
115                 private XsdElementValidationState AddElement (XmlSchemaElement element)
116                 {
117                         XsdElementValidationState got = new XsdElementValidationState (element, this);
118                         return got;
119                 }
120
121                 private XsdSequenceValidationState AddSequence (XmlSchemaSequence sequence)
122                 {
123                         XsdSequenceValidationState got = new XsdSequenceValidationState (sequence, this);
124                         return got;
125                 }
126
127                 private XsdChoiceValidationState AddChoice (XmlSchemaChoice choice)
128                 {
129                         XsdChoiceValidationState got = new XsdChoiceValidationState (choice, this);
130                         return got;
131                 }
132
133                 private XsdAllValidationState AddAll (XmlSchemaAll all)
134                 {
135                         XsdAllValidationState got = new XsdAllValidationState (all, this);
136                         return got;
137                 }
138
139                 private XsdAnyValidationState AddAny (XmlSchemaAny any)
140                 {
141                         XsdAnyValidationState got = new XsdAnyValidationState (any, this);
142                         return got;
143                 }
144
145                 private XsdEmptyValidationState AddEmpty ()
146                 {
147                         return new XsdEmptyValidationState (this);
148                 }
149         }
150
151         internal abstract class XsdValidationState
152         {
153                 // Static members
154
155                 static XsdInvalidValidationState invalid;
156
157                 static XsdValidationState ()
158                 {
159                         invalid = new XsdInvalidValidationState (null);
160                 }
161
162                 public static XsdInvalidValidationState Invalid {
163                         get { return invalid; }
164                 }
165
166                 // Dynamic members
167
168                 int occured;
169                 string message;
170                 XsdValidationStateManager manager;
171
172                 public XsdValidationState (XsdValidationStateManager manager)
173                 {
174                         this.manager = manager;
175                 }
176
177                 // Normally checks Max Occurs boundary
178                 public abstract XsdValidationState EvaluateStartElement (string localName, string ns);
179
180                 // Normally checks Min Occurs boundary
181                 public abstract bool EvaluateEndElement ();
182
183                 internal abstract bool EvaluateIsEmptiable ();
184
185                 public XsdValidationStateManager Manager {
186                         get { return manager; }
187                 }
188
189                 public string Message {
190                         get { return message; }
191                 }
192
193                 public string MessageInternal {
194                         get { return message; }
195                         set { message = value; }
196                 }
197
198                 public int Occured {
199                         get { return occured; }
200                 }
201
202                 internal int OccuredInternal {
203                         get { return occured; }
204                         set { occured = value; }
205                 }
206         }
207
208         internal class XsdElementValidationState : XsdValidationState
209         {
210                 public XsdElementValidationState (XmlSchemaElement element, XsdValidationStateManager manager)
211                         : base (manager)
212                 {
213                         this.element = element;
214                         name = element.QualifiedName.Name;
215                         ns = element.QualifiedName.Namespace;
216                 }
217
218                 // final fields
219                 XmlSchemaElement element;
220                 string name;
221                 string ns;
222
223                 // Methods
224                 
225                 public override XsdValidationState EvaluateStartElement (string name, string ns)
226                 {
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);
235                                         }
236                                 }
237                                 return XsdValidationState.Invalid;
238                         }
239                 }
240
241                 private XsdValidationState CheckOccurence (XmlSchemaElement maybeSubstituted)
242                 {
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;
252                         }
253                 }
254
255                 public override bool EvaluateEndElement ()
256                 {
257                         return EvaluateIsEmptiable ();
258                 }
259
260                 internal override bool EvaluateIsEmptiable ()
261                 {
262                         return (element.ValidatedMinOccurs <= Occured &&
263                                 element.ValidatedMaxOccurs >= Occured);
264                 }
265         }
266
267         internal class XsdSequenceValidationState : XsdValidationState
268         {
269                 XmlSchemaSequence seq;
270                 int current;
271                 XsdValidationState currentAutomata;
272                 bool emptiable;
273                 decimal minOccurs;
274                 decimal maxOccurs;
275
276                 public XsdSequenceValidationState (XmlSchemaSequence sequence, XsdValidationStateManager manager)
277                         : this (sequence, manager, sequence.ValidatedMinOccurs, sequence.ValidatedMaxOccurs, -1)
278                 {
279                 }
280
281                 public XsdSequenceValidationState (XmlSchemaSequence sequence, XsdValidationStateManager manager,
282                         decimal minOccurs, decimal maxOccurs, int current)
283                         : base (manager)
284                 {
285                         seq = sequence;
286                         this.minOccurs = minOccurs;
287                         this.maxOccurs = maxOccurs;
288                         this.current = current;
289                 }
290
291                 public override XsdValidationState EvaluateStartElement (string name, string ns)
292                 {
293                         if (seq.CompiledItems.Count == 0)
294                                 return XsdValidationState.Invalid;
295
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;
301
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;
310                                 }
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;
321                                                 }
322                                         } else {
323                                                 current = idx;
324                                                 currentAutomata = result;
325                                                 if (increment) {
326                                                         OccuredInternal++;
327                                                         if (Occured > maxOccurs)
328                                                                 return XsdValidationState.Invalid;
329                                                 }
330 //                                              current++;
331 //                                              return Manager.MakeSequence (result, this);
332                                                 return this;
333                                         // skip in other cases.
334                                         }
335                                 }
336                                 idx++;
337                                 if (idx > current && increment && current >= 0) {
338                                         return XsdValidationState.Invalid;
339                                 }
340                                 if (seq.CompiledItems.Count > idx) {
341                                         xa = Manager.Create (seq.CompiledItems [idx] as XmlSchemaParticle);
342                                 }
343                                 else if (current < 0) { // started from top
344                                         return XsdValidationState.Invalid;
345                                 }
346                                 else {          // started from middle
347                                         idx = 0;
348                                         xa = null;
349                                 }
350                         }
351                 }
352
353                 public override bool EvaluateEndElement ()
354                 {
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;
361
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;
375                         }
376                         if (current < 0)
377                                 OccuredInternal++;
378
379                         return minOccurs <= Occured && maxOccurs >= Occured;
380                 }
381
382                 internal override bool EvaluateIsEmptiable ()\r
383                 {\r
384                         if (minOccurs > Occured + 1)\r
385                                 return false;\r
386                         if (minOccurs == 0 && currentAutomata == null)\r
387                                 return true;\r
388 \r
389                         if (emptiable)\r
390                                 return true;\r
391                         if (seq.CompiledItems.Count == 0)\r
392                                 return true;\r
393 \r
394                         int idx = current < 0 ? 0 : current;
395                         XsdValidationState xa = currentAutomata;\r
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;
406                         }
407                         emptiable = true;
408                         return true;
409                 }\r
410
411         }
412
413         internal class XsdChoiceValidationState : XsdValidationState
414         {
415                 XmlSchemaChoice choice;
416                 bool emptiable;
417                 bool emptiableComputed;
418
419                 public XsdChoiceValidationState (XmlSchemaChoice choice, XsdValidationStateManager manager)
420                         : base (manager)
421                 {
422                         this.choice = choice;
423                 }
424
425                 public override XsdValidationState EvaluateStartElement (string localName, string ns)
426                 {
427                         emptiableComputed = false;
428
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);
441                                 }
442                         }
443                         emptiable = choice.ValidatedMinOccurs <= Occured;
444                         emptiableComputed = true;
445                         return XsdValidationState.Invalid;
446                 }
447
448                 public override bool EvaluateEndElement ()
449                 {
450                         emptiableComputed = false;
451
452                         if (choice.ValidatedMinOccurs > Occured + 1)
453                                 return false;
454
455                         else if (choice.ValidatedMinOccurs <= Occured)
456                                 return true;
457
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;
462                         }
463                         return false;
464                 }
465
466                 internal override bool EvaluateIsEmptiable ()\r
467                 {\r
468                         if (emptiableComputed)
469                                 return emptiable;
470 \r
471                         if (choice.ValidatedMaxOccurs < Occured)\r
472                                 return false;\r
473                         else if (choice.ValidatedMinOccurs > Occured + 1)\r
474                                 return false;\r
475 \r
476                         for (int i = Occured; i < choice.ValidatedMinOccurs; i++) {\r
477                                 bool next = false;\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 ()) {
481                                                 next = true;
482                                                 break;
483                                         }
484                                 }\r
485                                 if (!next)\r
486                                         return false;\r
487                         }\r
488                         return true;\r
489                 }\r
490         }
491
492         internal class XsdAllValidationState : XsdValidationState
493         {
494                 XmlSchemaAll all;
495                 ArrayList consumed = new ArrayList ();
496
497                 public XsdAllValidationState (XmlSchemaAll all, XsdValidationStateManager manager)
498                         : base (manager)
499                 {
500                         this.all = all;
501                 }
502
503                 public override XsdValidationState EvaluateStartElement (string localName, string ns)
504                 {
505                         if (all.CompiledItems.Count == 0)
506                                 return XsdValidationState.Invalid;
507
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;
520                                 }
521                         }
522                         return XsdValidationState.Invalid;
523                 }
524
525                 public override bool EvaluateEndElement ()
526                 {
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;
537                         }
538                         return true;
539                 }
540
541                 internal override bool EvaluateIsEmptiable ()\r
542                 {\r
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;
549                         }
550                         return true;
551                 }\r
552         }
553
554         internal class XsdAnyValidationState : XsdValidationState
555         {
556                 XmlSchemaAny any;
557
558                 public XsdAnyValidationState (XmlSchemaAny any, XsdValidationStateManager manager)
559                         : base (manager)
560                 {
561                         this.any = any;
562                 }
563
564                 // Methods
565                 public override XsdValidationState EvaluateStartElement (string name, string ns)
566                 {
567                         if (!MatchesNamespace (ns))
568                                 return XsdValidationState.Invalid;
569
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;
578                 }
579
580                 private bool MatchesNamespace (string ns)
581                 {
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;
594                 }
595
596                 public override bool EvaluateEndElement ()
597                 {
598                         return EvaluateIsEmptiable ();
599                 }
600
601                 internal override bool EvaluateIsEmptiable ()\r
602                 {\r
603                         return any.ValidatedMinOccurs <= Occured &&
604                                 any.ValidatedMaxOccurs >= Occured;
605                 }\r
606         }
607
608         internal class XsdAppendedValidationState : XsdValidationState
609         {
610                 public XsdAppendedValidationState (XsdValidationStateManager manager,
611                         XsdValidationState head, XsdValidationState rest)
612                         : base (manager)
613                 {
614                         this.head = head;
615                         this.rest = rest;
616                 }
617
618                 XsdValidationState head;
619                 XsdValidationState rest;
620
621                 // Methods
622                 public override XsdValidationState EvaluateStartElement (string name, string ns)
623                 {
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;
630                         }
631
632                         return rest.EvaluateStartElement (name, ns);
633                 }
634
635                 public override bool EvaluateEndElement ()
636                 {
637                         if (head.EvaluateEndElement ())
638 //                              return true;
639                                 return rest.EvaluateIsEmptiable ();
640                         if (!head.EvaluateIsEmptiable ())
641                                 return false;
642                         return rest.EvaluateEndElement ();
643                 }
644
645                 internal override bool EvaluateIsEmptiable ()\r
646                 {\r
647                         if (head.EvaluateIsEmptiable ())\r
648                                 return rest.EvaluateIsEmptiable ();\r
649                         else\r
650                                 return false;\r
651                 }\r
652         }
653
654         internal class XsdEmptyValidationState : XsdValidationState
655         {
656                 public XsdEmptyValidationState (XsdValidationStateManager manager)
657                         : base (manager)
658                 {
659                 }
660
661                 // Methods
662                 public override XsdValidationState EvaluateStartElement (string name, string ns)
663                 {
664                         return XsdValidationState.Invalid;
665                 }
666
667                 public override bool EvaluateEndElement ()
668                 {
669                         return true;
670                 }
671
672                 internal override bool EvaluateIsEmptiable ()\r
673                 {\r
674                         return true;\r
675                 }\r
676 \r
677         }
678
679         internal class XsdInvalidValidationState : XsdValidationState
680         {
681                 internal XsdInvalidValidationState (XsdValidationStateManager manager)
682                         : base (manager)
683                 {
684                 }
685
686                 // Methods
687                 public override XsdValidationState EvaluateStartElement (string name, string ns)
688                 {
689                         return this;
690                 }
691
692                 public override bool EvaluateEndElement ()
693                 {
694                         return false;
695                 }
696
697                 internal override bool EvaluateIsEmptiable ()\r
698                 {\r
699                         return false;\r
700                 }\r
701 \r
702         }
703 }