1 //------------------------------------------------------------------------------
2 // <copyright file="XsdValidator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
8 namespace System
.Xml
.Schema
{
10 using System
.Collections
;
11 using System
.Collections
.Specialized
;
14 using System
.Diagnostics
;
15 using System
.Xml
.Schema
;
16 using System
.Xml
.XPath
;
17 using System
.Runtime
.Versioning
;
19 #pragma warning disable 618
20 internal sealed class XsdValidator
: BaseValidator
{
22 private int startIDConstraint
= -1;
23 private const int STACK_INCREMENT
= 10;
24 private HWStack validationStack
; // validaton contexts
26 private Hashtable attPresence
;
27 private XmlNamespaceManager nsManager
;
28 private bool bManageNamespaces
= false;
29 private Hashtable IDs
;
30 private IdRefNode idRefListHead
;
31 private Parser inlineSchemaParser
= null;
32 private XmlSchemaContentProcessing processContents
;
34 private static readonly XmlSchemaDatatype dtCDATA
= XmlSchemaDatatype
.FromXmlTokenizedType(XmlTokenizedType
.CDATA
);
35 private static readonly XmlSchemaDatatype dtQName
= XmlSchemaDatatype
.FromXmlTokenizedTypeXsd(XmlTokenizedType
.QName
);
36 private static readonly XmlSchemaDatatype dtStringArray
= dtCDATA
.DeriveByList(null);
38 //To avoid SchemaNames creation
39 private string NsXmlNs
;
42 private string XsiType
;
43 private string XsiNil
;
44 private string XsiSchemaLocation
;
45 private string XsiNoNamespaceSchemaLocation
;
46 private string XsdSchema
;
49 internal XsdValidator(BaseValidator validator
) : base(validator
) {
53 internal XsdValidator(XmlValidatingReaderImpl reader
, XmlSchemaCollection schemaCollection
, IValidationEventHandling eventHandling
) : base(reader
, schemaCollection
, eventHandling
) {
58 nsManager
= reader
.NamespaceManager
;
59 if (nsManager
== null) {
60 nsManager
= new XmlNamespaceManager(NameTable
);
61 bManageNamespaces
= true;
63 validationStack
= new HWStack(STACK_INCREMENT
);
64 textValue
= new StringBuilder();
65 attPresence
= new Hashtable();
66 schemaInfo
= new SchemaInfo ();
67 checkDatatype
= false;
68 processContents
= XmlSchemaContentProcessing
.Strict
;
69 Push (XmlQualifiedName
.Empty
);
71 //Add common strings to be compared to NameTable
72 NsXmlNs
= NameTable
.Add(XmlReservedNs
.NsXmlNs
);
73 NsXs
= NameTable
.Add(XmlReservedNs
.NsXs
);
74 NsXsi
= NameTable
.Add(XmlReservedNs
.NsXsi
);
75 XsiType
= NameTable
.Add("type");
76 XsiNil
= NameTable
.Add("nil");
77 XsiSchemaLocation
= NameTable
.Add("schemaLocation");
78 XsiNoNamespaceSchemaLocation
= NameTable
.Add("noNamespaceSchemaLocation");
79 XsdSchema
= NameTable
.Add("schema");
82 public override void Validate() {
83 if (IsInlineSchemaStarted
) {
84 ProcessInlineSchema();
87 switch (reader
.NodeType
) {
88 case XmlNodeType
.Element
:
90 if (reader
.IsEmptyElement
) {
91 goto case XmlNodeType
.EndElement
;
94 case XmlNodeType
.Whitespace
:
97 case XmlNodeType
.Text
: // text inside a node
98 case XmlNodeType
.CDATA
: // <![CDATA[...]]>
99 case XmlNodeType
.SignificantWhitespace
:
102 case XmlNodeType
.EndElement
:
103 ValidateEndElement();
110 public override void CompleteValidation() {
114 //for frag validation
115 public ValidationState Context
{
116 set { context = value; }
119 //share for frag validation
120 public static XmlSchemaDatatype DtQName
{
121 get { return dtQName; }
124 private bool IsInlineSchemaStarted
{
125 get { return inlineSchemaParser != null; }
128 private void ProcessInlineSchema() {
129 if (!inlineSchemaParser
.ParseReaderNode()) { // Done
130 inlineSchemaParser
.FinishParsing();
131 XmlSchema schema
= inlineSchemaParser
.XmlSchema
;
132 string inlineNS
= null;
133 if (schema
!= null && schema
.ErrorCount
== 0) {
135 SchemaInfo inlineSchemaInfo
= new SchemaInfo();
136 inlineSchemaInfo
.SchemaType
= SchemaType
.XSD
;
137 inlineNS
= schema
.TargetNamespace
== null? string.Empty
: schema
.TargetNamespace
;
138 if (!SchemaInfo
.TargetNamespaces
.ContainsKey(inlineNS
)) {
139 if (SchemaCollection
.Add(inlineNS
, inlineSchemaInfo
, schema
, true) != null) { //If no errors on compile
140 //Add to validator's SchemaInfo
141 SchemaInfo
.Add(inlineSchemaInfo
, EventHandler
);
145 catch(XmlSchemaException e
) {
146 SendValidationEvent(Res
.Sch_CannotLoadSchema
, new string[] {BaseUri.AbsoluteUri, e.Message}
, XmlSeverityType
.Error
);
149 inlineSchemaParser
= null;
153 private void ValidateElement() {
154 elementName
.Init(reader
.LocalName
, reader
.NamespaceURI
);
155 object particle
= ValidateChildElement ();
156 if (IsXSDRoot(elementName
.Name
, elementName
.Namespace
) && reader
.Depth
> 0) {
157 inlineSchemaParser
= new Parser(SchemaType
.XSD
, NameTable
, SchemaNames
, EventHandler
);
158 inlineSchemaParser
.StartParsing(reader
, null);
159 ProcessInlineSchema();
162 ProcessElement(particle
);
166 private object ValidateChildElement() {
167 object particle
= null;
169 if (context
.NeedValidateChildren
) {
170 if (context
.IsNill
) {
171 SendValidationEvent(Res
.Sch_ContentInNill
, elementName
.ToString());
174 particle
= context
.ElementDecl
.ContentValidator
.ValidateElement(elementName
, context
, out errorCode
);
175 if (particle
== null) {
176 processContents
= context
.ProcessContents
= XmlSchemaContentProcessing
.Skip
;
177 if (errorCode
== -2) { //ContentModel all group error
178 SendValidationEvent(Res
.Sch_AllElement
, elementName
.ToString());
180 XmlSchemaValidator
.ElementValidationError(elementName
, context
, EventHandler
, reader
, reader
.BaseURI
, PositionInfo
.LineNumber
, PositionInfo
.LinePosition
, null);
186 private void ProcessElement(object particle
) {
187 XmlQualifiedName xsiType
;
189 SchemaElementDecl elementDecl
= FastGetElementDecl (particle
);
191 if (bManageNamespaces
) {
192 nsManager
.PushScope();
194 ProcessXsiAttributes(out xsiType
, out xsiNil
);
195 if (processContents
!= XmlSchemaContentProcessing
.Skip
) {
196 if (elementDecl
== null || !xsiType
.IsEmpty
|| xsiNil
!= null) {
197 elementDecl
= ThoroughGetElementDecl(elementDecl
, xsiType
, xsiNil
);
199 if (elementDecl
== null) {
200 if (HasSchema
&& processContents
== XmlSchemaContentProcessing
.Strict
) {
201 SendValidationEvent(Res
.Sch_UndeclaredElement
, XmlSchemaValidator
.QNameString(context
.LocalName
, context
.Namespace
));
204 SendValidationEvent(Res
.Sch_NoElementSchemaFound
, XmlSchemaValidator
.QNameString(context
.LocalName
, context
.Namespace
), XmlSeverityType
.Warning
);
209 context
.ElementDecl
= elementDecl
;
210 ValidateStartElementIdentityConstraints();
211 ValidateStartElement();
212 if (context
.ElementDecl
!= null) {
213 ValidateEndStartElement();
214 context
.NeedValidateChildren
= processContents
!= XmlSchemaContentProcessing
.Skip
;
215 context
.ElementDecl
.ContentValidator
.InitValidation(context
);
219 // SxS: This method processes attributes read from source document and does not expose any resources.
220 // It's OK to suppress the SxS warning.
221 [ResourceConsumption(ResourceScope
.Machine
, ResourceScope
.Machine
)]
222 [ResourceExposure(ResourceScope
.None
)]
223 private void ProcessXsiAttributes(out XmlQualifiedName xsiType
, out string xsiNil
) {
224 string[] xsiSchemaLocation
= null;
225 string xsiNoNamespaceSchemaLocation
= null;
226 xsiType
= XmlQualifiedName
.Empty
;
229 if (reader
.Depth
== 0) {
230 //Load schema for empty namespace
231 LoadSchema(string.Empty
, null);
233 //Should load schemas for namespaces already added to nsManager
234 foreach(string ns
in nsManager
.GetNamespacesInScope(XmlNamespaceScope
.ExcludeXml
).Values
) {
235 LoadSchema(ns
, null);
239 if (reader
.MoveToFirstAttribute()) {
241 string objectNs
= reader
.NamespaceURI
;
242 string objectName
= reader
.LocalName
;
243 if (Ref
.Equal(objectNs
, NsXmlNs
)) {
244 LoadSchema(reader
.Value
, null);
245 if (bManageNamespaces
) {
246 nsManager
.AddNamespace(reader
.Prefix
.Length
== 0 ? string.Empty
: reader
.LocalName
, reader
.Value
);
249 else if (Ref
.Equal(objectNs
, NsXsi
)) {
250 if (Ref
.Equal(objectName
, XsiSchemaLocation
)) {
251 xsiSchemaLocation
= (string[])dtStringArray
.ParseValue(reader
.Value
, NameTable
, nsManager
);
253 else if (Ref
.Equal(objectName
, XsiNoNamespaceSchemaLocation
)) {
254 xsiNoNamespaceSchemaLocation
= reader
.Value
;
256 else if (Ref
.Equal(objectName
, XsiType
)) {
257 xsiType
= (XmlQualifiedName
)dtQName
.ParseValue(reader
.Value
, NameTable
, nsManager
);
259 else if (Ref
.Equal(objectName
, XsiNil
)) {
260 xsiNil
= reader
.Value
;
263 } while(reader
.MoveToNextAttribute());
264 reader
.MoveToElement();
266 if (xsiNoNamespaceSchemaLocation
!= null) {
267 LoadSchema(string.Empty
, xsiNoNamespaceSchemaLocation
);
269 if (xsiSchemaLocation
!= null) {
270 for (int i
= 0; i
< xsiSchemaLocation
.Length
- 1; i
+= 2) {
271 LoadSchema((string)xsiSchemaLocation
[i
], (string)xsiSchemaLocation
[i
+ 1]);
276 private void ValidateEndElement() {
277 if (bManageNamespaces
) {
278 nsManager
.PopScope();
280 if (context
.ElementDecl
!= null) {
281 if (!context
.IsNill
) {
282 if (context
.NeedValidateChildren
) {
283 if(!context
.ElementDecl
.ContentValidator
.CompleteValidation(context
)) {
284 XmlSchemaValidator
.CompleteValidationError(context
, EventHandler
, reader
, reader
.BaseURI
, PositionInfo
.LineNumber
, PositionInfo
.LinePosition
, null);
288 if (checkDatatype
&& !context
.IsNill
) {
289 string stringValue
= !hasSibling
? textString
: textValue
.ToString(); // only for identity-constraint exception reporting
290 if(!(stringValue
.Length
== 0 && context
.ElementDecl
.DefaultValueTyped
!= null)) {
291 CheckValue(stringValue
, null);
292 checkDatatype
= false;
297 // for each level in the stack, endchildren and fill value from element
298 if (HasIdentityConstraints
) {
299 EndElementIdentityConstraints();
306 private SchemaElementDecl
FastGetElementDecl(object particle
) {
307 SchemaElementDecl elementDecl
= null;
308 if (particle
!= null) {
309 XmlSchemaElement element
= particle
as XmlSchemaElement
;
310 if (element
!= null) {
311 elementDecl
= element
.ElementDecl
;
314 XmlSchemaAny any
= (XmlSchemaAny
)particle
;
315 processContents
= any
.ProcessContentsCorrect
;
321 private SchemaElementDecl
ThoroughGetElementDecl(SchemaElementDecl elementDecl
, XmlQualifiedName xsiType
, string xsiNil
) {
322 if (elementDecl
== null) {
323 elementDecl
= schemaInfo
.GetElementDecl(elementName
);
325 if (elementDecl
!= null) {
326 if (xsiType
.IsEmpty
) {
327 if (elementDecl
.IsAbstract
) {
328 SendValidationEvent(Res
.Sch_AbstractElement
, XmlSchemaValidator
.QNameString(context
.LocalName
, context
.Namespace
));
332 else if(xsiNil
!= null && xsiNil
.Equals("true")) {
333 SendValidationEvent(Res
.Sch_XsiNilAndType
);
336 SchemaElementDecl elementDeclXsi
;
337 if (!schemaInfo
.ElementDeclsByType
.TryGetValue(xsiType
, out elementDeclXsi
) && xsiType
.Namespace
== NsXs
) {
338 XmlSchemaSimpleType simpleType
= DatatypeImplementation
.GetSimpleTypeFromXsdType(new XmlQualifiedName(xsiType
.Name
,NsXs
));
339 if (simpleType
!= null) {
340 elementDeclXsi
= simpleType
.ElementDecl
;
343 if (elementDeclXsi
== null) {
344 SendValidationEvent(Res
.Sch_XsiTypeNotFound
, xsiType
.ToString());
347 else if (!XmlSchemaType
.IsDerivedFrom(elementDeclXsi
.SchemaType
,elementDecl
.SchemaType
,elementDecl
.Block
)) {
348 SendValidationEvent(Res
.Sch_XsiTypeBlockedEx
, new string[] { xsiType.ToString(), XmlSchemaValidator.QNameString(context.LocalName, context.Namespace) }
);
352 elementDecl
= elementDeclXsi
;
355 if (elementDecl
!= null && elementDecl
.IsNillable
) {
356 if (xsiNil
!= null) {
357 context
.IsNill
= XmlConvert
.ToBoolean(xsiNil
);
358 if (context
.IsNill
&& elementDecl
.DefaultValueTyped
!= null) {
359 SendValidationEvent(Res
.Sch_XsiNilAndFixed
);
363 else if (xsiNil
!= null) {
364 SendValidationEvent(Res
.Sch_InvalidXsiNill
);
370 private void ValidateStartElement() {
371 if (context
.ElementDecl
!= null) {
372 if (context
.ElementDecl
.IsAbstract
) {
373 SendValidationEvent(Res
.Sch_AbstractElement
, XmlSchemaValidator
.QNameString(context
.LocalName
, context
.Namespace
));
376 reader
.SchemaTypeObject
= context
.ElementDecl
.SchemaType
;
378 if (reader
.IsEmptyElement
&& !context
.IsNill
&& context
.ElementDecl
.DefaultValueTyped
!= null) {
379 reader
.TypedValueObject
= UnWrapUnion(context
.ElementDecl
.DefaultValueTyped
);
380 context
.IsNill
= true; // reusing IsNill
383 reader
.TypedValueObject
= null; //Typed value cleanup
385 if (this.context
.ElementDecl
.HasRequiredAttribute
|| HasIdentityConstraints
) {
390 if (reader
.MoveToFirstAttribute()) {
392 if ((object)reader
.NamespaceURI
== (object)NsXmlNs
) {
395 if ((object)reader
.NamespaceURI
== (object)NsXsi
) {
400 reader
.SchemaTypeObject
= null;
401 XmlQualifiedName attQName
= new XmlQualifiedName(reader
.LocalName
, reader
.NamespaceURI
);
402 bool skipContents
= (processContents
== XmlSchemaContentProcessing
.Skip
);
403 SchemaAttDef attnDef
= schemaInfo
.GetAttributeXsd(context
.ElementDecl
, attQName
, ref skipContents
);
405 if (attnDef
!= null) {
406 if (context
.ElementDecl
!= null && (context
.ElementDecl
.HasRequiredAttribute
|| this.startIDConstraint
!= -1)) {
407 attPresence
.Add(attnDef
.Name
, attnDef
);
409 Debug
.Assert(attnDef
.SchemaType
!= null);
410 reader
.SchemaTypeObject
= attnDef
.SchemaType
;
411 if (attnDef
.Datatype
!= null) {
413 // need to check the contents of this attribute to make sure
414 // it is valid according to the specified attribute type.
415 CheckValue(reader
.Value
, attnDef
);
417 if (HasIdentityConstraints
) {
418 AttributeIdentityConstraints(reader
.LocalName
, reader
.NamespaceURI
, reader
.TypedValueObject
, reader
.Value
, attnDef
);
421 else if (!skipContents
) {
422 if (context
.ElementDecl
== null
423 && processContents
== XmlSchemaContentProcessing
.Strict
424 && attQName
.Namespace
.Length
!= 0
425 && schemaInfo
.Contains(attQName
.Namespace
)
427 SendValidationEvent(Res
.Sch_UndeclaredAttribute
, attQName
.ToString());
430 SendValidationEvent(Res
.Sch_NoAttributeSchemaFound
, attQName
.ToString(), XmlSeverityType
.Warning
);
434 catch (XmlSchemaException e
) {
435 e
.SetSource(reader
.BaseURI
, PositionInfo
.LineNumber
, PositionInfo
.LinePosition
);
436 SendValidationEvent(e
);
438 } while(reader
.MoveToNextAttribute());
439 reader
.MoveToElement();
443 private void ValidateEndStartElement() {
444 if (context
.ElementDecl
.HasDefaultAttribute
) {
445 for (int i
= 0; i
< context
.ElementDecl
.DefaultAttDefs
.Count
; ++i
) {
446 SchemaAttDef attdef
= (SchemaAttDef
)context
.ElementDecl
.DefaultAttDefs
[i
];
447 reader
.AddDefaultAttribute(attdef
);
448 // even default attribute i have to move to... but can't exist
449 if (HasIdentityConstraints
&& !attPresence
.Contains(attdef
.Name
)) {
450 AttributeIdentityConstraints(attdef
.Name
.Name
, attdef
.Name
.Namespace
, UnWrapUnion(attdef
.DefaultValueTyped
), attdef
.DefaultValueRaw
, attdef
);
455 if (context
.ElementDecl
.HasRequiredAttribute
) {
457 context
.ElementDecl
.CheckAttributes(attPresence
, reader
.StandAlone
);
459 catch (XmlSchemaException e
) {
460 e
.SetSource(reader
.BaseURI
, PositionInfo
.LineNumber
, PositionInfo
.LinePosition
);
461 SendValidationEvent(e
);
465 if (context
.ElementDecl
.Datatype
!= null) {
466 checkDatatype
= true;
468 textString
= string.Empty
;
469 textValue
.Length
= 0;
474 [ResourceConsumption(ResourceScope
.Machine
)]
475 [ResourceExposure(ResourceScope
.Machine
)]
476 private void LoadSchemaFromLocation(string uri
, string url
) {
478 XmlReader reader
= null;
479 SchemaInfo schemaInfo
= null;
482 Uri ruri
= this.XmlResolver
.ResolveUri(BaseUri
, url
);
483 Stream stm
= (Stream
)this.XmlResolver
.GetEntity(ruri
,null,null);
484 reader
= new XmlTextReader(ruri
.ToString(), stm
, NameTable
);
485 //XmlSchema schema = SchemaCollection.Add(uri, reader, this.XmlResolver);
487 Parser parser
= new Parser(SchemaType
.XSD
, NameTable
, SchemaNames
, EventHandler
);
488 parser
.XmlResolver
= this.XmlResolver
;
489 SchemaType schemaType
= parser
.Parse(reader
, uri
);
491 schemaInfo
= new SchemaInfo();
492 schemaInfo
.SchemaType
= schemaType
;
493 if (schemaType
== SchemaType
.XSD
) {
494 if (SchemaCollection
.EventHandler
== null) {
495 SchemaCollection
.EventHandler
= this.EventHandler
;
497 SchemaCollection
.Add(uri
, schemaInfo
, parser
.XmlSchema
, true);
499 //Add to validator's SchemaInfo
500 SchemaInfo
.Add(schemaInfo
, EventHandler
);
502 while(reader
.Read());// wellformness check
504 catch(XmlSchemaException e
) {
506 SendValidationEvent(Res
.Sch_CannotLoadSchema
, new string[] {uri, e.Message}
, XmlSeverityType
.Error
);
510 SendValidationEvent(Res
.Sch_CannotLoadSchema
, new string[] {uri, e.Message}
, XmlSeverityType
.Warning
);
513 if (reader
!= null) {
519 [ResourceConsumption(ResourceScope
.Machine
)]
520 [ResourceExposure(ResourceScope
.Machine
)]
521 private void LoadSchema(string uri
, string url
) {
522 if (this.XmlResolver
== null) {
525 if (SchemaInfo
.TargetNamespaces
.ContainsKey(uri
) && nsManager
.LookupPrefix(uri
) != null) {
529 SchemaInfo schemaInfo
= null;
530 if (SchemaCollection
!= null)
531 schemaInfo
= SchemaCollection
.GetSchemaInfo(uri
);
532 if (schemaInfo
!= null) {
533 if(schemaInfo
.SchemaType
!= SchemaType
.XSD
) {
534 throw new XmlException(Res
.Xml_MultipleValidaitonTypes
, string.Empty
, this.PositionInfo
.LineNumber
, this.PositionInfo
.LinePosition
);
536 SchemaInfo
.Add(schemaInfo
, EventHandler
);
540 LoadSchemaFromLocation(uri
, url
);
544 private bool HasSchema { get { return schemaInfo.SchemaType != SchemaType.None;}}
546 public override bool PreserveWhitespace
{
547 get { return context.ElementDecl != null ? context.ElementDecl.ContentValidator.PreserveWhitespace : false; }
551 void ProcessTokenizedType(
552 XmlTokenizedType ttype
,
556 case XmlTokenizedType
.ID
:
557 if (FindId(name
) != null) {
558 SendValidationEvent(Res
.Sch_DupId
, name
);
561 AddID(name
, context
.LocalName
);
564 case XmlTokenizedType
.IDREF
:
565 object p
= FindId(name
);
566 if (p
== null) { // add it to linked list to check it later
567 idRefListHead
= new IdRefNode(idRefListHead
, name
, this.PositionInfo
.LineNumber
, this.PositionInfo
.LinePosition
);
570 case XmlTokenizedType
.ENTITY
:
571 ProcessEntity(schemaInfo
, name
, this, EventHandler
, reader
.BaseURI
, PositionInfo
.LineNumber
, PositionInfo
.LinePosition
);
578 private void CheckValue(
583 reader
.TypedValueObject
= null;
584 bool isAttn
= attdef
!= null;
585 XmlSchemaDatatype dtype
= isAttn
? attdef
.Datatype
: context
.ElementDecl
.Datatype
;
587 return; // no reason to check
590 object typedValue
= dtype
.ParseValue(value, NameTable
, nsManager
, true);
592 // Check special types
593 XmlTokenizedType ttype
= dtype
.TokenizedType
;
594 if (ttype
== XmlTokenizedType
.ENTITY
|| ttype
== XmlTokenizedType
.ID
|| ttype
== XmlTokenizedType
.IDREF
) {
595 if (dtype
.Variety
== XmlSchemaDatatypeVariety
.List
) {
596 string[] ss
= (string[])typedValue
;
597 for (int i
= 0; i
< ss
.Length
; ++i
) {
598 ProcessTokenizedType(dtype
.TokenizedType
, ss
[i
]);
602 ProcessTokenizedType(dtype
.TokenizedType
, (string)typedValue
);
606 SchemaDeclBase decl
= isAttn
? (SchemaDeclBase
)attdef
: (SchemaDeclBase
)context
.ElementDecl
;
607 if (!decl
.CheckValue(typedValue
)) {
609 SendValidationEvent(Res
.Sch_FixedAttributeValue
, attdef
.Name
.ToString());
612 SendValidationEvent(Res
.Sch_FixedElementValue
, XmlSchemaValidator
.QNameString(context
.LocalName
, context
.Namespace
));
615 if (dtype
.Variety
== XmlSchemaDatatypeVariety
.Union
) {
616 typedValue
= UnWrapUnion(typedValue
);
618 reader
.TypedValueObject
= typedValue
;
620 catch (XmlSchemaException
) {
621 if (attdef
!= null) {
622 SendValidationEvent(Res
.Sch_AttributeValueDataType
, attdef
.Name
.ToString());
625 SendValidationEvent(Res
.Sch_ElementValueDataType
, XmlSchemaValidator
.QNameString(context
.LocalName
, context
.Namespace
));
632 internal void AddID(string name
, object node
) {
633 // Note: It used to be true that we only called this if _fValidate was true,
634 // but due to the fact that you can now dynamically type somethign as an ID
635 // that is no longer true.
637 IDs
= new Hashtable();
643 public override object FindId(string name
) {
644 return IDs
== null ? null : IDs
[name
];
647 public bool IsXSDRoot(string localName
, string ns
) {
648 return Ref
.Equal(ns
, NsXs
) && Ref
.Equal(localName
, XsdSchema
);
651 private void Push(XmlQualifiedName elementName
) {
652 context
= (ValidationState
)validationStack
.Push();
653 if (context
== null) {
654 context
= new ValidationState();
655 validationStack
.AddToTop(context
);
657 context
.LocalName
= elementName
.Name
;
658 context
.Namespace
= elementName
.Namespace
;
659 context
.HasMatched
= false;
660 context
.IsNill
= false;
661 context
.ProcessContents
= processContents
;
662 context
.NeedValidateChildren
= false;
663 context
.Constr
= null; //resetting the constraints to be null incase context != null
664 // when pushing onto stack;
669 if (validationStack
.Length
> 1) {
670 validationStack
.Pop();
671 if (startIDConstraint
== validationStack
.Length
) {
672 startIDConstraint
= -1;
674 context
= (ValidationState
)validationStack
.Peek();
675 processContents
= context
.ProcessContents
;
679 private void CheckForwardRefs() {
680 IdRefNode next
= idRefListHead
;
681 while (next
!= null) {
682 if(FindId(next
.Id
) == null) {
683 SendValidationEvent(new XmlSchemaException(Res
.Sch_UndeclaredId
, next
.Id
, reader
.BaseURI
, next
.LineNo
, next
.LinePos
));
685 IdRefNode ptr
= next
.Next
;
686 next
.Next
= null; // unhook each object so it is cleaned up by Garbage Collector
689 // not needed any more.
690 idRefListHead
= null;
694 private void ValidateStartElementIdentityConstraints() {
695 // added on June 15, set the context here, so the stack can have them
696 if (context
.ElementDecl
!= null) {
697 if (context
.ElementDecl
.Constraints
!= null) {
698 AddIdentityConstraints();
700 //foreach constraint in stack (including the current one)
701 if (HasIdentityConstraints
) {
702 ElementIdentityConstraints();
707 private bool HasIdentityConstraints
{
708 get { return startIDConstraint != -1; }
711 private void AddIdentityConstraints() {
712 context
.Constr
= new ConstraintStruct
[context
.ElementDecl
.Constraints
.Length
];
714 for (int i
= 0; i
< context
.ElementDecl
.Constraints
.Length
; ++i
) {
715 context
.Constr
[id
++] = new ConstraintStruct (context
.ElementDecl
.Constraints
[i
]);
716 } // foreach constraint /constraintstruct
718 // added on June 19, make connections between new keyref tables with key/unique tables in stack
719 // i can't put it in the above loop, coz there will be key on the same level
720 for (int i
= 0; i
< context
.Constr
.Length
; ++i
) {
721 if ( context
.Constr
[i
].constraint
.Role
== CompiledIdentityConstraint
.ConstraintRole
.Keyref
) {
723 // go upwards checking or only in this level
724 for (int level
= this.validationStack
.Length
- 1; level
>= ((this.startIDConstraint
>= 0) ? this.startIDConstraint
: this.validationStack
.Length
- 1); level
--) {
725 // no constraint for this level
726 if (((ValidationState
)(this.validationStack
[level
])).Constr
== null) {
730 ConstraintStruct
[] constraints
= ((ValidationState
) this.validationStack
[level
]).Constr
;
731 for (int j
= 0; j
< constraints
.Length
; ++j
) {
732 if (constraints
[j
].constraint
.name
== context
.Constr
[i
].constraint
.refer
) {
734 if (constraints
[j
].keyrefTable
== null) {
735 constraints
[j
].keyrefTable
= new Hashtable();
737 context
.Constr
[i
].qualifiedTable
= constraints
[j
].keyrefTable
;
747 // didn't find connections, throw exceptions
748 SendValidationEvent(Res
.Sch_RefNotInScope
, XmlSchemaValidator
.QNameString(context
.LocalName
, context
.Namespace
));
750 } // finished dealing with keyref
755 if (this.startIDConstraint
== -1) {
756 this.startIDConstraint
= this.validationStack
.Length
- 1;
760 private void ElementIdentityConstraints () {
761 for (int i
= this.startIDConstraint
; i
< this.validationStack
.Length
; i
++) {
762 // no constraint for this level
763 if (((ValidationState
)(this.validationStack
[i
])).Constr
== null) {
768 ConstraintStruct
[] constraints
= ((ValidationState
)this.validationStack
[i
]).Constr
;
769 for (int j
= 0; j
< constraints
.Length
; ++j
) {
770 // check selector from here
771 if (constraints
[j
].axisSelector
.MoveToStartElement(reader
.LocalName
, reader
.NamespaceURI
)) {
772 // selector selects new node, activate a new set of fields
773 Debug
.WriteLine("Selector Match!");
774 Debug
.WriteLine("Name: " + reader
.LocalName
+ "\t|\tURI: " + reader
.NamespaceURI
+ "\n");
775 // in which axisFields got updated
776 constraints
[j
].axisSelector
.PushKS(PositionInfo
.LineNumber
, PositionInfo
.LinePosition
);
779 // axisFields is not null, but may be empty
780 for (int k
= 0; k
< constraints
[j
].axisFields
.Count
; ++k
) {
781 LocatedActiveAxis laxis
= (LocatedActiveAxis
)constraints
[j
].axisFields
[k
];
783 // check field from here
784 if (laxis
.MoveToStartElement(reader
.LocalName
, reader
.NamespaceURI
)) {
785 Debug
.WriteLine("Element Field Match!");
786 // checking simpleType / simpleContent
787 if (context
.ElementDecl
!= null) { // nextElement can be null when xml/xsd are not valid
788 if (context
.ElementDecl
.Datatype
== null) {
789 SendValidationEvent(Res
.Sch_FieldSimpleTypeExpected
, reader
.LocalName
);
792 // can't fill value here, wait till later....
793 // fill type : xsdType
794 laxis
.isMatched
= true;
795 // since it's simpletyped element, the endchildren will come consequently... don't worry
805 // facilitate modifying
806 private void AttributeIdentityConstraints(string name
, string ns
, object obj
, string sobj
, SchemaAttDef attdef
) {
807 for (int ci
= this.startIDConstraint
; ci
< this.validationStack
.Length
; ci
++) {
808 // no constraint for this level
809 if (((ValidationState
)(this.validationStack
[ci
])).Constr
== null) {
814 ConstraintStruct
[] constraints
= ((ValidationState
)this.validationStack
[ci
]).Constr
;
815 for (int i
= 0; i
< constraints
.Length
; ++i
) {
816 // axisFields is not null, but may be empty
817 for (int j
= 0; j
< constraints
[i
].axisFields
.Count
; ++j
) {
818 LocatedActiveAxis laxis
= (LocatedActiveAxis
)constraints
[i
].axisFields
[j
];
820 // check field from here
821 if (laxis
.MoveToAttribute(name
, ns
)) {
822 Debug
.WriteLine("Attribute Field Match!");
823 //attribute is only simpletype, so needn't checking...
824 // can fill value here, yeah!!
825 Debug
.WriteLine("Attribute Field Filling Value!");
826 Debug
.WriteLine("Name: " + name
+ "\t|\tURI: " + ns
+ "\t|\tValue: " + obj
+ "\n");
827 if (laxis
.Ks
[laxis
.Column
] != null) {
828 // should be evaluated to either an empty node-set or a node-set with exactly one member
830 SendValidationEvent (Res
.Sch_FieldSingleValueExpected
, name
);
832 else if ((attdef
!= null) && (attdef
.Datatype
!= null)){
833 laxis
.Ks
[laxis
.Column
] = new TypedObject (obj
, sobj
, attdef
.Datatype
);
841 private object UnWrapUnion(object typedValue
) {
842 XsdSimpleValue simpleValue
= typedValue
as XsdSimpleValue
;
843 if (simpleValue
!= null) {
844 typedValue
= simpleValue
.TypedValue
;
849 private void EndElementIdentityConstraints() {
850 for (int ci
= this.validationStack
.Length
- 1; ci
>= this.startIDConstraint
; ci
--) {
851 // no constraint for this level
852 if (((ValidationState
)(this.validationStack
[ci
])).Constr
== null) {
857 ConstraintStruct
[] constraints
= ((ValidationState
)this.validationStack
[ci
]).Constr
;
858 for (int i
= 0; i
< constraints
.Length
; ++i
) {
860 // axisFields is not null, but may be empty
861 for (int j
= 0; j
< constraints
[i
].axisFields
.Count
; ++j
) {
862 LocatedActiveAxis laxis
= (LocatedActiveAxis
)constraints
[i
].axisFields
[j
];
863 // check field from here
864 // isMatched is false when nextElement is null. so needn't change this part.
865 if (laxis
.isMatched
) {
866 Debug
.WriteLine("Element Field Filling Value!");
867 Debug
.WriteLine("Name: " + reader
.LocalName
+ "\t|\tURI: " + reader
.NamespaceURI
+ "\t|\tValue: " + reader
.TypedValueObject
+ "\n");
869 laxis
.isMatched
= false;
870 if (laxis
.Ks
[laxis
.Column
] != null) {
871 // [field...] should be evaluated to either an empty node-set or a node-set with exactly one member
872 // two matches... already existing field value in the table.
873 SendValidationEvent (Res
.Sch_FieldSingleValueExpected
, reader
.LocalName
);
876 // for element, reader.Value = "";
877 string stringValue
= !hasSibling
? textString
: textValue
.ToString(); // only for identity-constraint exception reporting
878 if(reader
.TypedValueObject
!= null && stringValue
.Length
!= 0) {
879 laxis
.Ks
[laxis
.Column
] = new TypedObject(reader
.TypedValueObject
,stringValue
,context
.ElementDecl
.Datatype
);
884 laxis
.EndElement(reader
.LocalName
, reader
.NamespaceURI
);
887 if (constraints
[i
].axisSelector
.EndElement(reader
.LocalName
, reader
.NamespaceURI
)) {
888 // insert key sequence into hash (+ located active axis tuple leave for later)
889 KeySequence ks
= constraints
[i
].axisSelector
.PopKS();
890 // unqualified keysequence are not allowed
891 switch (constraints
[i
].constraint
.Role
) {
892 case CompiledIdentityConstraint
.ConstraintRole
.Key
:
893 if (! ks
.IsQualified()) {
894 //Key's fields can't be null... if we can return context node's line info maybe it will be better
895 //only keymissing & keyduplicate reporting cases are necessary to be dealt with... 3 places...
896 SendValidationEvent(new XmlSchemaException(Res
.Sch_MissingKey
, constraints
[i
].constraint
.name
.ToString(), reader
.BaseURI
, ks
.PosLine
, ks
.PosCol
));
898 else if (constraints
[i
].qualifiedTable
.Contains (ks
)) {
899 // unique or key checking value confliction
900 // for redundant key, reporting both occurings
901 // doesn't work... how can i retrieve value out??
902 SendValidationEvent(new XmlSchemaException(Res
.Sch_DuplicateKey
,
903 new string[2] {ks.ToString(), constraints[i].constraint.name.ToString()}
,
904 reader
.BaseURI
, ks
.PosLine
, ks
.PosCol
));
907 constraints
[i
].qualifiedTable
.Add (ks
, ks
);
910 case CompiledIdentityConstraint
.ConstraintRole
.Unique
:
911 if (! ks
.IsQualified()) {
914 if (constraints
[i
].qualifiedTable
.Contains (ks
)) {
915 // unique or key checking confliction
916 SendValidationEvent(new XmlSchemaException(Res
.Sch_DuplicateKey
,
917 new string[2] {ks.ToString(), constraints[i].constraint.name.ToString()}
,
918 reader
.BaseURI
, ks
.PosLine
, ks
.PosCol
));
921 constraints
[i
].qualifiedTable
.Add (ks
, ks
);
924 case CompiledIdentityConstraint
.ConstraintRole
.Keyref
:
925 // is there any possibility:
926 // 2 keyrefs: value is equal, type is not
927 // both put in the hashtable, 1 reference, 1 not
928 if (constraints
[i
].qualifiedTable
!= null) { //Will be null in cases when the keyref is outside the scope of the key, that is not allowed by our impl
929 if (! ks
.IsQualified() || constraints
[i
].qualifiedTable
.Contains (ks
)) {
932 constraints
[i
].qualifiedTable
.Add (ks
, ks
);
941 // current level's constraint struct
942 ConstraintStruct
[] vcs
= ((ValidationState
)(this.validationStack
[this.validationStack
.Length
- 1])).Constr
;
944 // validating all referencing tables...
945 for (int i
= 0; i
< vcs
.Length
; ++i
) {
946 if (( vcs
[i
].constraint
.Role
== CompiledIdentityConstraint
.ConstraintRole
.Keyref
)
947 || (vcs
[i
].keyrefTable
== null)) {
950 foreach (KeySequence ks
in vcs
[i
].keyrefTable
.Keys
) {
951 if (! vcs
[i
].qualifiedTable
.Contains (ks
)) {
952 SendValidationEvent(new XmlSchemaException(Res
.Sch_UnresolvedKeyref
, new string[2] { ks.ToString(), vcs[i].constraint.name.ToString() }
,
953 reader
.BaseURI
, ks
.PosLine
, ks
.PosCol
));
961 #pragma warning restore 618