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