The Labs \ Source Viewer \ SSCLI \ System.Xml \ DocumentSchemaValidator

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

Developer Fusion