Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Xml / System / Xml / Schema / XsdValidator.cs
blobeeed4aecae006bdadb8a0f7a4fbf014dc6336a11
1 //------------------------------------------------------------------------------
2 // <copyright file="XsdValidator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
8 namespace System.Xml.Schema {
10 using System.Collections;
11 using System.Collections.Specialized;
12 using System.Text;
13 using System.IO;
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;
40 private string NsXs;
41 private string NsXsi;
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) {
50 Init();
53 internal XsdValidator(XmlValidatingReaderImpl reader, XmlSchemaCollection schemaCollection, IValidationEventHandling eventHandling) : base(reader, schemaCollection, eventHandling) {
54 Init();
57 private void Init() {
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();
86 else {
87 switch (reader.NodeType) {
88 case XmlNodeType.Element:
89 ValidateElement();
90 if (reader.IsEmptyElement) {
91 goto case XmlNodeType.EndElement;
93 break;
94 case XmlNodeType.Whitespace:
95 ValidateWhitespace();
96 break;
97 case XmlNodeType.Text: // text inside a node
98 case XmlNodeType.CDATA: // <![CDATA[...]]>
99 case XmlNodeType.SignificantWhitespace:
100 ValidateText();
101 break;
102 case XmlNodeType.EndElement:
103 ValidateEndElement();
104 break;
110 public override void CompleteValidation() {
111 CheckForwardRefs();
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) {
134 try {
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();
161 else {
162 ProcessElement(particle);
166 private object ValidateChildElement() {
167 object particle = null;
168 int errorCode = 0;
169 if (context.NeedValidateChildren) {
170 if (context.IsNill) {
171 SendValidationEvent(Res.Sch_ContentInNill, elementName.ToString());
172 return null;
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);
183 return particle;
186 private void ProcessElement(object particle) {
187 XmlQualifiedName xsiType;
188 string xsiNil;
189 SchemaElementDecl elementDecl = FastGetElementDecl (particle);
190 Push(elementName);
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));
203 else {
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;
227 xsiNil = null;
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()) {
240 do {
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();
302 Pop();
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;
313 else {
314 XmlSchemaAny any = (XmlSchemaAny)particle;
315 processContents = any.ProcessContentsCorrect;
318 return elementDecl;
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));
329 elementDecl = null;
332 else if(xsiNil != null && xsiNil.Equals("true")) {
333 SendValidationEvent(Res.Sch_XsiNilAndType);
335 else {
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());
345 elementDecl = null;
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) });
349 elementDecl = null;
351 else {
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);
367 return elementDecl;
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
382 else {
383 reader.TypedValueObject = null; //Typed value cleanup
385 if (this.context.ElementDecl.HasRequiredAttribute || HasIdentityConstraints) {
386 attPresence.Clear();
390 if (reader.MoveToFirstAttribute()) {
391 do {
392 if ((object)reader.NamespaceURI == (object)NsXmlNs) {
393 continue;
395 if ((object)reader.NamespaceURI == (object)NsXsi) {
396 continue;
399 try {
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());
429 else {
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) {
456 try {
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;
467 hasSibling = false;
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;
481 try {
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) {
505 schemaInfo = null;
506 SendValidationEvent(Res.Sch_CannotLoadSchema, new string[] {uri, e.Message}, XmlSeverityType.Error);
508 catch(Exception e) {
509 schemaInfo = null;
510 SendValidationEvent(Res.Sch_CannotLoadSchema, new string[] {uri, e.Message}, XmlSeverityType.Warning);
512 finally {
513 if (reader != null) {
514 reader.Close();
519 [ResourceConsumption(ResourceScope.Machine)]
520 [ResourceExposure(ResourceScope.Machine)]
521 private void LoadSchema(string uri, string url) {
522 if (this.XmlResolver == null) {
523 return;
525 if (SchemaInfo.TargetNamespaces.ContainsKey(uri) && nsManager.LookupPrefix(uri) != null) {
526 return;
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);
537 return;
539 if (url != null) {
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,
553 string name
555 switch(ttype) {
556 case XmlTokenizedType.ID:
557 if (FindId(name) != null) {
558 SendValidationEvent(Res.Sch_DupId, name);
560 else {
561 AddID(name, context.LocalName);
563 break;
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);
569 break;
570 case XmlTokenizedType.ENTITY:
571 ProcessEntity(schemaInfo, name, this, EventHandler, reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition);
572 break;
573 default:
574 break;
578 private void CheckValue(
579 string value,
580 SchemaAttDef attdef
582 try {
583 reader.TypedValueObject = null;
584 bool isAttn = attdef != null;
585 XmlSchemaDatatype dtype = isAttn ? attdef.Datatype : context.ElementDecl.Datatype;
586 if (dtype == null) {
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]);
601 else {
602 ProcessTokenizedType(dtype.TokenizedType, (string)typedValue);
606 SchemaDeclBase decl = isAttn ? (SchemaDeclBase)attdef : (SchemaDeclBase)context.ElementDecl;
607 if (!decl.CheckValue(typedValue)) {
608 if (isAttn) {
609 SendValidationEvent(Res.Sch_FixedAttributeValue, attdef.Name.ToString());
611 else {
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());
624 else {
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.
636 if (IDs == null) {
637 IDs = new Hashtable();
640 IDs.Add(name, node);
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;
668 private void Pop() {
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
687 next = ptr;
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];
713 int id = 0;
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 ) {
722 bool find = false;
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) {
727 continue;
729 // else
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) {
733 find = true;
734 if (constraints[j].keyrefTable == null) {
735 constraints[j].keyrefTable = new Hashtable();
737 context.Constr[i].qualifiedTable = constraints[j].keyrefTable;
738 break;
742 if (find) {
743 break;
746 if (!find) {
747 // didn't find connections, throw exceptions
748 SendValidationEvent(Res.Sch_RefNotInScope, XmlSchemaValidator.QNameString(context.LocalName, context.Namespace));
750 } // finished dealing with keyref
752 } // end foreach
754 // initial set
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) {
764 continue;
767 // else
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);
791 else {
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) {
810 continue;
813 // else
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
829 // two matches...
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;
846 return 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) {
853 continue;
856 // else
857 ConstraintStruct[] constraints = ((ValidationState)this.validationStack[ci]).Constr;
858 for (int i = 0; i < constraints.Length; ++i) {
859 // EndChildren
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");
868 // fill value
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);
875 else {
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);
883 // EndChildren
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));
906 else {
907 constraints[i].qualifiedTable.Add (ks, ks);
909 break;
910 case CompiledIdentityConstraint.ConstraintRole.Unique:
911 if (! ks.IsQualified()) {
912 continue;
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));
920 else {
921 constraints[i].qualifiedTable.Add (ks, ks);
923 break;
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)) {
930 continue;
932 constraints[i].qualifiedTable.Add (ks, ks);
934 break;
941 // current level's constraint struct
942 ConstraintStruct[] vcs = ((ValidationState)(this.validationStack[this.validationStack.Length - 1])).Constr;
943 if ( vcs != null) {
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)) {
948 continue;
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