Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Dom / DocumentSchemaValidator.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlDocumentValidator.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
7
8 using System;
9 using System.Text;
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.Diagnostics;
13 using System.Xml;
14 using System.Xml.Schema;
15 using System.Xml.XPath;
16 using System.Globalization;
17 using System.Security;
18 using System.Security.Policy;
19 using System.Security.Permissions;
20 using System.Reflection;
21 using System.Runtime.Versioning;
22
23 namespace System.Xml {
24
25     internal sealed class DocumentSchemaValidator : IXmlNamespaceResolver {
26         XmlSchemaValidator validator;
27         XmlSchemaSet schemas;
28         
29         XmlNamespaceManager nsManager;
30         XmlNameTable nameTable;
31
32         //Attributes
33         ArrayList defaultAttributes;
34         XmlValueGetter nodeValueGetter;
35         XmlSchemaInfo attributeSchemaInfo;
36
37         //Element PSVI 
38         XmlSchemaInfo schemaInfo;
39
40         //Event Handler
41         ValidationEventHandler eventHandler;
42         ValidationEventHandler internalEventHandler;
43
44         //Store nodes
45         XmlNode startNode;
46         XmlNode currentNode;
47         XmlDocument document;
48
49         //List of nodes for partial validation tree walk
50         XmlNode[] nodeSequenceToValidate;
51         bool isPartialTreeValid;
52
53         bool psviAugmentation;
54         bool isValid;
55
56         //To avoid SchemaNames creation
57         private string NsXmlNs;
58         private string NsXsi;
59         private string XsiType;
60         private string XsiNil;
61
62         public DocumentSchemaValidator(XmlDocument ownerDocument, XmlSchemaSet schemas, ValidationEventHandler eventHandler) {
63             this.schemas = schemas;
64             this.eventHandler = eventHandler;
65             document = ownerDocument;
66             this.internalEventHandler = new ValidationEventHandler(InternalValidationCallBack);
67             
68             this.nameTable = document.NameTable;
69             nsManager = new XmlNamespaceManager(nameTable);
70             
71             Debug.Assert(schemas != null && schemas.Count > 0);
72
73             nodeValueGetter = new XmlValueGetter(GetNodeValue);
74             psviAugmentation = true;
75
76             //Add common strings to be compared to NameTable
77             NsXmlNs = nameTable.Add(XmlReservedNs.NsXmlNs);
78             NsXsi = nameTable.Add(XmlReservedNs.NsXsi);
79             XsiType = nameTable.Add("type");
80             XsiNil = nameTable.Add("nil");
81         }
82
83         public bool PsviAugmentation {
84             get { return psviAugmentation; }
85             set { psviAugmentation = value; }
86         }
87
88         public bool Validate(XmlNode nodeToValidate) {
89             XmlSchemaObject partialValidationType = null;
90             XmlSchemaValidationFlags validationFlags = XmlSchemaValidationFlags.AllowXmlAttributes;
91             Debug.Assert(nodeToValidate.SchemaInfo != null);
92
93             startNode = nodeToValidate;
94             switch (nodeToValidate.NodeType) {
95                 case XmlNodeType.Document:
96                     validationFlags |= XmlSchemaValidationFlags.ProcessIdentityConstraints;
97                     break;
98
99                 case XmlNodeType.DocumentFragment:
100                     break;
101
102                 case XmlNodeType.Element: //Validate children of this element
103                     IXmlSchemaInfo schemaInfo = nodeToValidate.SchemaInfo;
104                     XmlSchemaElement schemaElement = schemaInfo.SchemaElement;
105                     if (schemaElement != null) {
106                         if (!schemaElement.RefName.IsEmpty) { //If it is element ref,
107                             partialValidationType = schemas.GlobalElements[schemaElement.QualifiedName]; //Get Global element with correct Nillable, Default etc
108                         }
109                         else { //local element
110                             partialValidationType = schemaElement;
111                         }
112                         //Verify that if there was xsi:type, the schemaElement returned has the correct type set
113                         Debug.Assert(schemaElement.ElementSchemaType == schemaInfo.SchemaType);
114                     }
115                     else { //Can be an element that matched xs:any and had xsi:type
116                         partialValidationType = schemaInfo.SchemaType;   
117                      
118                         if (partialValidationType == null) { //Validated against xs:any with pc= lax or skip or undeclared / not validated element
119                             if (nodeToValidate.ParentNode.NodeType == XmlNodeType.Document) {
120                                 //If this is the documentElement and it has not been validated at all
121                                 nodeToValidate = nodeToValidate.ParentNode;
122                             }
123                             else {
124                                 partialValidationType = FindSchemaInfo(nodeToValidate as XmlElement);
125                                 if (partialValidationType == null) { 
126                                     throw new XmlSchemaValidationException(Res.XmlDocument_NoNodeSchemaInfo, null, nodeToValidate);
127                                 }
128                             }
129                         }
130                     }
131                     break;
132
133                 case XmlNodeType.Attribute:
134                     if (nodeToValidate.XPNodeType == XPathNodeType.Namespace) goto default;
135                     partialValidationType = nodeToValidate.SchemaInfo.SchemaAttribute;
136                     if (partialValidationType == null) { //Validated against xs:anyAttribute with pc = lax or skip / undeclared attribute
137                         partialValidationType = FindSchemaInfo(nodeToValidate as XmlAttribute);
138                         if (partialValidationType == null) { 
139                             throw new XmlSchemaValidationException(Res.XmlDocument_NoNodeSchemaInfo, null, nodeToValidate);
140                         }
141                     }
142                     break;
143
144                 default:
145                     throw new InvalidOperationException(Res.GetString(Res.XmlDocument_ValidateInvalidNodeType, null));
146             }
147             isValid = true;
148             CreateValidator(partialValidationType, validationFlags);
149             if (psviAugmentation) {
150                 if (schemaInfo == null) { //Might have created it during FindSchemaInfo
151                     schemaInfo = new XmlSchemaInfo();
152                 }
153                 attributeSchemaInfo = new XmlSchemaInfo();
154             }
155             ValidateNode(nodeToValidate);
156             validator.EndValidation();    
157             return isValid; 
158         }
159
160         public IDictionary<string,string> GetNamespacesInScope(XmlNamespaceScope scope) {
161             IDictionary<string,string> dictionary = nsManager.GetNamespacesInScope(scope); 
162             if (scope != XmlNamespaceScope.Local) {
163                 XmlNode node = startNode;
164                 while (node != null) {
165                     switch (node.NodeType) {
166                         case XmlNodeType.Element:
167                             XmlElement elem = (XmlElement)node;
168                             if (elem.HasAttributes) {
169                                 XmlAttributeCollection attrs = elem.Attributes;
170                                 for (int i = 0; i < attrs.Count; i++) {
171                                     XmlAttribute attr = attrs[i];
172                                     if (Ref.Equal(attr.NamespaceURI, document.strReservedXmlns)) {
173                                         if (attr.Prefix.Length == 0) {
174                                             // xmlns='' declaration
175                                             if (!dictionary.ContainsKey(string.Empty)) {
176                                                 dictionary.Add(string.Empty, attr.Value);
177                                             }
178                                         }
179                                         else {
180                                             // xmlns:prefix='' declaration
181                                             if (!dictionary.ContainsKey(attr.LocalName)) {
182                                                 dictionary.Add(attr.LocalName, attr.Value);
183                                             }
184                                         }
185                                     }
186                                 }
187                             }
188                             node = node.ParentNode;
189                             break;
190                         case XmlNodeType.Attribute:
191                             node = ((XmlAttribute)node).OwnerElement;
192                             break;
193                         default:
194                             node = node.ParentNode;
195                             break;
196                     }
197                 }
198             }
199             return dictionary;
200         }
201
202         public string LookupNamespace(string prefix) {
203             string namespaceName = nsManager.LookupNamespace(prefix);
204             if (namespaceName == null) {
205                 namespaceName = startNode.GetNamespaceOfPrefixStrict(prefix);
206             }
207             return namespaceName;
208         }
209
210         public string LookupPrefix(string namespaceName) {
211             string prefix = nsManager.LookupPrefix(namespaceName);
212             if (prefix == null) {
213                 prefix = startNode.GetPrefixOfNamespaceStrict(namespaceName);
214             }
215             return prefix;
216         }
217
218         private IXmlNamespaceResolver NamespaceResolver {
219             get {
220                 if ((object)startNode == (object)document) {
221                     return nsManager;
222                 }
223                 return this;
224             }
225         }
226
227         private void CreateValidator(XmlSchemaObject partialValidationType, XmlSchemaValidationFlags validationFlags) {
228             validator = new XmlSchemaValidator(nameTable, schemas, NamespaceResolver, validationFlags);
229             validator.SourceUri = XmlConvert.ToUri(document.BaseURI);
230             validator.XmlResolver = null;
231             validator.ValidationEventHandler += internalEventHandler;
232             validator.ValidationEventSender = this;
233             
234             if (partialValidationType != null) {
235                 validator.Initialize(partialValidationType);
236             }
237             else {
238                 validator.Initialize();
239             }
240         }
241
242         private void ValidateNode(XmlNode node) {
243             currentNode = node;
244             switch (currentNode.NodeType) {
245                 case XmlNodeType.Document:
246                     XmlElement docElem = ((XmlDocument)node).DocumentElement;
247                     if (docElem == null) {
248                         throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidXmlDocument, Res.GetString(Res.Xdom_NoRootEle)));
249                     }
250                     ValidateNode(docElem);
251                     break;
252
253                 case XmlNodeType.DocumentFragment:
254                 case XmlNodeType.EntityReference:
255                     for (XmlNode child = node.FirstChild; child != null; child = child.NextSibling) {
256                         ValidateNode(child);
257                     }
258                     break;
259
260                 case XmlNodeType.Element:
261                     ValidateElement();
262                     break;
263
264                 case XmlNodeType.Attribute: //Top-level attribute
265                     XmlAttribute attr = currentNode as XmlAttribute;
266                     validator.ValidateAttribute(attr.LocalName, attr.NamespaceURI, nodeValueGetter, attributeSchemaInfo);
267                     if (psviAugmentation) {
268                         attr.XmlName = document.AddAttrXmlName(attr.Prefix, attr.LocalName, attr.NamespaceURI, attributeSchemaInfo);
269                     }
270                     break;
271
272                 case XmlNodeType.Text:
273                     validator.ValidateText(nodeValueGetter);
274                     break;
275
276                 case XmlNodeType.CDATA:
277                     validator.ValidateText(nodeValueGetter);
278                     break;
279
280                 case XmlNodeType.Whitespace:
281                 case XmlNodeType.SignificantWhitespace:
282                     validator.ValidateWhitespace(nodeValueGetter);
283                     break;
284
285                 case XmlNodeType.Comment:
286                 case XmlNodeType.ProcessingInstruction:
287                     break;
288
289                 default:
290                     throw new InvalidOperationException( Res.GetString( Res.Xml_UnexpectedNodeType, new string[]{ currentNode.NodeType.ToString() } ) );
291             }
292         }
293
294         // SxS: This function calls ValidateElement on XmlSchemaValidator which is annotated with ResourceExposure attribute.
295         // Since the resource names passed to ValidateElement method are null and the function does not expose any resources 
296         // it is fine to suppress the SxS warning. 
297         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
298         [ResourceExposure(ResourceScope.None)]
299         private void ValidateElement() {
300             nsManager.PushScope();
301             XmlElement elementNode = currentNode as XmlElement;
302             Debug.Assert(elementNode != null);
303
304             XmlAttributeCollection attributes = elementNode.Attributes;
305             XmlAttribute attr = null;
306
307             //Find Xsi attributes that need to be processed before validating the element
308             string xsiNil = null;
309             string xsiType = null; 
310
311             for (int i = 0; i < attributes.Count; i++) {
312                 attr = attributes[i];
313                 string objectNs = attr.NamespaceURI;
314                 string objectName = attr.LocalName;
315                 Debug.Assert(nameTable.Get(attr.NamespaceURI) != null);
316                 Debug.Assert(nameTable.Get(attr.LocalName) != null);
317
318                 if (Ref.Equal(objectNs, NsXsi)) {
319                     if (Ref.Equal(objectName, XsiType)) {
320                         xsiType = attr.Value;
321                     }
322                     else if (Ref.Equal(objectName, XsiNil)) {
323                         xsiNil = attr.Value;
324                     }
325                 }
326                 else if (Ref.Equal(objectNs,NsXmlNs)) {
327                     nsManager.AddNamespace(attr.Prefix.Length == 0 ? string.Empty : attr.LocalName, attr.Value);
328                 }
329             }
330             validator.ValidateElement(elementNode.LocalName, elementNode.NamespaceURI, schemaInfo, xsiType, xsiNil, null, null);
331             ValidateAttributes(elementNode);
332             validator.ValidateEndOfAttributes(schemaInfo);
333
334             //If element has children, drill down
335             for (XmlNode child = elementNode.FirstChild; child != null; child = child.NextSibling) {
336                 ValidateNode(child);
337             }
338             //Validate end of element
339             currentNode = elementNode; //Reset current Node for validation call back
340             validator.ValidateEndElement(schemaInfo);
341             //Get XmlName, as memberType / validity might be set now
342             if (psviAugmentation) {
343                 elementNode.XmlName = document.AddXmlName(elementNode.Prefix, elementNode.LocalName, elementNode.NamespaceURI, schemaInfo);
344                 if (schemaInfo.IsDefault) { //the element has a default value
345                     XmlText textNode = document.CreateTextNode(schemaInfo.SchemaElement.ElementDecl.DefaultValueRaw);
346                     elementNode.AppendChild(textNode);
347                 }
348             }
349
350             nsManager.PopScope(); //Pop current namespace scope
351         }
352
353         private void ValidateAttributes(XmlElement elementNode) {
354             XmlAttributeCollection attributes = elementNode.Attributes;
355             XmlAttribute attr = null;
356
357             for (int i = 0; i < attributes.Count; i++) {
358                 attr = attributes[i];
359                 currentNode = attr; //For nodeValueGetter to pick up the right attribute value
360                 if (Ref.Equal(attr.NamespaceURI,NsXmlNs)) { //Do not validate namespace decls
361                     continue;
362                 }
363                 validator.ValidateAttribute(attr.LocalName, attr.NamespaceURI, nodeValueGetter, attributeSchemaInfo);
364                 if (psviAugmentation) {
365                     attr.XmlName = document.AddAttrXmlName(attr.Prefix, attr.LocalName, attr.NamespaceURI, attributeSchemaInfo);
366                 }
367             }
368     
369             if (psviAugmentation) {
370                 //Add default attributes to the attributes collection
371                 if (defaultAttributes == null) {
372                     defaultAttributes = new ArrayList();
373                 }
374                 else {
375                     defaultAttributes.Clear();
376                 }
377                 validator.GetUnspecifiedDefaultAttributes(defaultAttributes);
378                 XmlSchemaAttribute schemaAttribute = null;
379                 XmlQualifiedName attrQName;
380                 attr = null;
381                 for (int i = 0; i < defaultAttributes.Count; i++) {
382                     schemaAttribute = defaultAttributes[i] as XmlSchemaAttribute;
383                     attrQName = schemaAttribute.QualifiedName;
384                     Debug.Assert(schemaAttribute != null);
385                     attr = document.CreateDefaultAttribute(GetDefaultPrefix(attrQName.Namespace), attrQName.Name, attrQName.Namespace);
386                     SetDefaultAttributeSchemaInfo(schemaAttribute);
387                     attr.XmlName = document.AddAttrXmlName(attr.Prefix, attr.LocalName, attr.NamespaceURI, attributeSchemaInfo);
388                     attr.AppendChild(document.CreateTextNode(schemaAttribute.AttDef.DefaultValueRaw));
389                     attributes.Append(attr);
390                     XmlUnspecifiedAttribute defAttr = attr as XmlUnspecifiedAttribute;
391                     if (defAttr != null) {
392                         defAttr.SetSpecified(false);
393                     }
394                 }
395             }
396         }
397
398         private void SetDefaultAttributeSchemaInfo(XmlSchemaAttribute schemaAttribute) {
399             Debug.Assert(attributeSchemaInfo != null);
400             attributeSchemaInfo.Clear();
401             attributeSchemaInfo.IsDefault = true;
402             attributeSchemaInfo.IsNil = false;
403             attributeSchemaInfo.SchemaType = schemaAttribute.AttributeSchemaType;
404             attributeSchemaInfo.SchemaAttribute = schemaAttribute;
405             
406             //Get memberType for default attribute
407             SchemaAttDef attributeDef = schemaAttribute.AttDef;                
408             if (attributeDef.Datatype.Variety == XmlSchemaDatatypeVariety.Union) {
409                 XsdSimpleValue simpleValue = attributeDef.DefaultValueTyped as XsdSimpleValue;
410                 Debug.Assert(simpleValue != null);
411                 attributeSchemaInfo.MemberType = simpleValue.XmlType;
412             }
413             attributeSchemaInfo.Validity = XmlSchemaValidity.Valid;
414         }
415
416         private string GetDefaultPrefix(string attributeNS) {
417             IDictionary<string,string> namespaceDecls = NamespaceResolver.GetNamespacesInScope(XmlNamespaceScope.All);
418             string defaultPrefix = null;
419             string defaultNS;
420             attributeNS = nameTable.Add(attributeNS); //atomize ns
421
422             foreach (KeyValuePair<string,string> pair in namespaceDecls) {
423                 defaultNS = nameTable.Add(pair.Value);
424                 if (object.ReferenceEquals(defaultNS, attributeNS)) {
425                     defaultPrefix = pair.Key;
426                     if (defaultPrefix.Length != 0) { //Locate first non-empty prefix
427                         return defaultPrefix;
428                     }
429                 }
430             }
431             return defaultPrefix;
432         }
433
434         private object GetNodeValue() {
435             return currentNode.Value;
436         }
437
438         //Code for finding type during partial validation
439         private XmlSchemaObject FindSchemaInfo(XmlElement elementToValidate) {
440             isPartialTreeValid = true;
441             Debug.Assert(elementToValidate.ParentNode.NodeType != XmlNodeType.Document); //Handle if it is the documentElement seperately            
442             
443             //Create nodelist to navigate down again
444             XmlNode currentNode = elementToValidate;
445             IXmlSchemaInfo parentSchemaInfo = null;
446             int nodeIndex = 0;
447             
448             //Check common case of parent node first
449             XmlNode parentNode = currentNode.ParentNode;
450             do {
451                 parentSchemaInfo = parentNode.SchemaInfo;
452                 if (parentSchemaInfo.SchemaElement != null || parentSchemaInfo.SchemaType != null) {
453                     break; //Found ancestor with schemaInfo
454                 }
455                 CheckNodeSequenceCapacity(nodeIndex);
456                 nodeSequenceToValidate[nodeIndex++] = parentNode;
457                 parentNode = parentNode.ParentNode;
458             } while (parentNode != null);
459
460             if (parentNode == null) { //Did not find any type info all the way to the root, currentNode is Document || DocumentFragment
461                 nodeIndex = nodeIndex - 1; //Subtract the one for document and set the node to null
462                 nodeSequenceToValidate[nodeIndex] = null;
463                 return GetTypeFromAncestors(elementToValidate, null, nodeIndex);
464             }
465             else {
466                 //Start validating down from the parent or ancestor that has schema info and shallow validate all previous siblings
467                 //to correctly ascertain particle for current node
468                 CheckNodeSequenceCapacity(nodeIndex);
469                 nodeSequenceToValidate[nodeIndex++] = parentNode;
470                 XmlSchemaObject ancestorSchemaObject = parentSchemaInfo.SchemaElement;
471                 if (ancestorSchemaObject == null) {
472                     ancestorSchemaObject = parentSchemaInfo.SchemaType;
473                 }
474                 return GetTypeFromAncestors(elementToValidate, ancestorSchemaObject, nodeIndex);
475
476             }
477         }
478
479         /*private XmlSchemaElement GetTypeFromParent(XmlElement elementToValidate, XmlSchemaComplexType parentSchemaType) {
480             XmlQualifiedName elementName = new XmlQualifiedName(elementToValidate.LocalName, elementToValidate.NamespaceURI);
481             XmlSchemaElement elem = parentSchemaType.LocalElements[elementName] as XmlSchemaElement;
482             if (elem == null) { //Element not found as direct child of the content model. It might be invalid at this position or it might be a substitution member
483                 SchemaInfo compiledSchemaInfo = schemas.CompiledInfo;
484                 XmlSchemaElement memberElem = compiledSchemaInfo.GetElement(elementName);
485                 if (memberElem != null) {
486                 }
487             }
488         }*/
489
490         private void CheckNodeSequenceCapacity(int currentIndex) {
491             if (nodeSequenceToValidate == null) { //Normally users would call Validate one level down, this allows for 4
492                 nodeSequenceToValidate = new XmlNode[4];
493             }
494             else if (currentIndex >= nodeSequenceToValidate.Length -1 ) { //reached capacity of array, Need to increase capacity to twice the initial
495                 XmlNode[] newNodeSequence = new XmlNode[nodeSequenceToValidate.Length * 2];
496                 Array.Copy(nodeSequenceToValidate, 0, newNodeSequence, 0, nodeSequenceToValidate.Length);
497                 nodeSequenceToValidate = newNodeSequence;
498             }
499         }
500
501         private XmlSchemaAttribute FindSchemaInfo(XmlAttribute attributeToValidate) {
502             XmlElement parentElement = attributeToValidate.OwnerElement;
503             XmlSchemaObject schemaObject = FindSchemaInfo(parentElement);
504             XmlSchemaComplexType elementSchemaType = GetComplexType(schemaObject);
505             if (elementSchemaType == null) {
506                 return null;
507             }
508             XmlQualifiedName attName = new XmlQualifiedName(attributeToValidate.LocalName, attributeToValidate.NamespaceURI);
509             XmlSchemaAttribute schemaAttribute = elementSchemaType.AttributeUses[attName] as XmlSchemaAttribute;
510             if (schemaAttribute == null) {
511                 XmlSchemaAnyAttribute anyAttribute = elementSchemaType.AttributeWildcard;
512                 if (anyAttribute != null) {
513                     if (anyAttribute.NamespaceList.Allows(attName)){ //Match wildcard against global attribute
514                         schemaAttribute = schemas.GlobalAttributes[attName] as XmlSchemaAttribute;
515                     }
516                 }
517             }
518             return schemaAttribute;
519         }
520
521         private XmlSchemaObject GetTypeFromAncestors(XmlElement elementToValidate, XmlSchemaObject ancestorType, int ancestorsCount) {
522             
523             //schemaInfo is currentNode's schemaInfo
524             validator = CreateTypeFinderValidator(ancestorType);
525             schemaInfo = new XmlSchemaInfo();
526
527             //start at the ancestor to start validating
528             int startIndex = ancestorsCount - 1;
529         
530             bool ancestorHasWildCard = AncestorTypeHasWildcard(ancestorType);
531             for (int i = startIndex; i >= 0; i--) {
532                 XmlNode node = nodeSequenceToValidate[i];
533                 XmlElement currentElement = node as XmlElement;
534                 ValidateSingleElement(currentElement, false, schemaInfo);
535                 if (!ancestorHasWildCard) { //store type if ancestor does not have wildcard in its content model
536                     currentElement.XmlName = document.AddXmlName(currentElement.Prefix, currentElement.LocalName, currentElement.NamespaceURI, schemaInfo);
537                     //update wildcard flag
538                     ancestorHasWildCard = AncestorTypeHasWildcard(schemaInfo.SchemaElement);
539                 }
540                 
541                 validator.ValidateEndOfAttributes(null);
542                 if (i > 0) {
543                     ValidateChildrenTillNextAncestor(node, nodeSequenceToValidate[i - 1]);
544                 }
545                 else { //i == 0
546                     ValidateChildrenTillNextAncestor(node, elementToValidate);
547                 }
548             }
549
550             Debug.Assert(nodeSequenceToValidate[0] == elementToValidate.ParentNode);
551             //validate element whose type is needed,
552             ValidateSingleElement(elementToValidate, false, schemaInfo);
553
554             XmlSchemaObject schemaInfoFound = null;
555             if (schemaInfo.SchemaElement != null) {
556                 schemaInfoFound = schemaInfo.SchemaElement;
557             }
558             else {
559                 schemaInfoFound = schemaInfo.SchemaType;
560             }
561             if (schemaInfoFound == null) { //Detect if the node was validated lax or skip
562                 if (validator.CurrentProcessContents == XmlSchemaContentProcessing.Skip) {
563                     if (isPartialTreeValid) { //Then node assessed as skip; if there was error we turn processContents to skip as well. But this is not the same as validating as skip.
564                         return XmlSchemaComplexType.AnyTypeSkip;
565                     }
566                 }
567                 else if (validator.CurrentProcessContents == XmlSchemaContentProcessing.Lax) {
568                     return XmlSchemaComplexType.AnyType;
569                 }
570             }
571             return schemaInfoFound;
572         }
573
574         private bool AncestorTypeHasWildcard(XmlSchemaObject ancestorType) {
575             XmlSchemaComplexType ancestorSchemaType = GetComplexType(ancestorType);
576             if (ancestorType != null) {
577                 return ancestorSchemaType.HasWildCard;
578             }
579             return false;
580         }
581
582         private XmlSchemaComplexType GetComplexType(XmlSchemaObject schemaObject) {
583             if (schemaObject == null) {
584                 return null;
585             }
586             XmlSchemaElement schemaElement = schemaObject as XmlSchemaElement;
587             XmlSchemaComplexType complexType = null;
588             if (schemaElement != null) {
589                 complexType = schemaElement.ElementSchemaType as XmlSchemaComplexType;
590             }
591             else {
592                 complexType = schemaObject as XmlSchemaComplexType;
593             }
594             return complexType;
595         }
596
597         // SxS: This function calls ValidateElement on XmlSchemaValidator which is annotated with ResourceExposure attribute.
598         // Since the resource names passed to ValidateElement method are null and the function does not expose any resources 
599         // it is fine to supress the warning. 
600         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
601         [ResourceExposure(ResourceScope.None)]
602         private void ValidateSingleElement(XmlElement elementNode, bool skipToEnd, XmlSchemaInfo newSchemaInfo) {
603             nsManager.PushScope();
604             Debug.Assert(elementNode != null);
605             
606             XmlAttributeCollection attributes = elementNode.Attributes;
607             XmlAttribute attr = null;
608
609             //Find Xsi attributes that need to be processed before validating the element
610             string xsiNil = null;
611             string xsiType = null; 
612
613             for (int i = 0; i < attributes.Count; i++) {
614                 attr = attributes[i];
615                 string objectNs = attr.NamespaceURI;
616                 string objectName = attr.LocalName;
617                 Debug.Assert(nameTable.Get(attr.NamespaceURI) != null);
618                 Debug.Assert(nameTable.Get(attr.LocalName) != null);
619
620                 if (Ref.Equal(objectNs, NsXsi)) {
621                     if (Ref.Equal(objectName, XsiType)) {
622                         xsiType = attr.Value;
623                     }
624                     else if (Ref.Equal(objectName, XsiNil)) {
625                         xsiNil = attr.Value;
626                     }
627                 }
628                 else if (Ref.Equal(objectNs,NsXmlNs)) {
629                     nsManager.AddNamespace(attr.Prefix.Length == 0 ? string.Empty : attr.LocalName, attr.Value);
630                 }
631             }
632             validator.ValidateElement(elementNode.LocalName, elementNode.NamespaceURI, newSchemaInfo, xsiType, xsiNil, null, null);
633             //Validate end of element
634             if (skipToEnd) {
635                 validator.ValidateEndOfAttributes(newSchemaInfo);
636                 validator.SkipToEndElement(newSchemaInfo);
637                 nsManager.PopScope(); //Pop current namespace scope
638             }
639         }
640
641         private void ValidateChildrenTillNextAncestor(XmlNode parentNode, XmlNode childToStopAt) {
642             XmlNode child;
643
644             for (child = parentNode.FirstChild; child != null; child = child.NextSibling) {
645                 if (child == childToStopAt) {
646                     break;
647                 }
648                 switch (child.NodeType) {
649                     case XmlNodeType.EntityReference:
650                         ValidateChildrenTillNextAncestor(child, childToStopAt);
651                         break;
652
653                     case XmlNodeType.Element: //Flat validation, do not drill down into children
654                         ValidateSingleElement(child as XmlElement, true, null);
655                         break;
656
657                     case XmlNodeType.Text:
658                     case XmlNodeType.CDATA:
659                         validator.ValidateText(child.Value);
660                         break;
661
662                     case XmlNodeType.Whitespace:
663                     case XmlNodeType.SignificantWhitespace:
664                         validator.ValidateWhitespace(child.Value);
665                         break;
666
667                     case XmlNodeType.Comment:
668                     case XmlNodeType.ProcessingInstruction:
669                         break;
670
671                     default:
672                         throw new InvalidOperationException( Res.GetString( Res.Xml_UnexpectedNodeType, new string[]{ currentNode.NodeType.ToString() } ) );
673                 }
674             }
675             Debug.Assert(child == childToStopAt);
676         }
677
678         private XmlSchemaValidator CreateTypeFinderValidator(XmlSchemaObject partialValidationType) {
679             XmlSchemaValidator findTypeValidator = new XmlSchemaValidator(document.NameTable, document.Schemas, this.nsManager, XmlSchemaValidationFlags.None);
680             findTypeValidator.ValidationEventHandler += new ValidationEventHandler(TypeFinderCallBack);
681             if (partialValidationType != null) {
682                 findTypeValidator.Initialize(partialValidationType);
683             }
684             else { //If we walked up to the root and no schemaInfo was there, start validating from root 
685                 findTypeValidator.Initialize();
686             }
687             return findTypeValidator;
688         }
689
690         private void TypeFinderCallBack(object sender, ValidationEventArgs arg) {
691             if (arg.Severity == XmlSeverityType.Error) {
692                 isPartialTreeValid = false;
693             }
694         }
695         
696         private void InternalValidationCallBack(object sender, ValidationEventArgs arg) {
697             if (arg.Severity == XmlSeverityType.Error) {
698                 isValid = false;
699             }
700             XmlSchemaValidationException ex = arg.Exception as XmlSchemaValidationException;
701             Debug.Assert(ex != null);
702             ex.SetSourceObject(currentNode);
703             if (this.eventHandler != null) { //Invoke user's event handler
704                 eventHandler(sender, arg);
705             }
706             else if (arg.Severity == XmlSeverityType.Error) {
707                 throw ex;
708             }
709         }
710
711     }
712 }