XNodeSchemaApplier.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Design / system / Data / Services / Design / Xml / XNodeSchemaApplier.cs / 1305376 / XNodeSchemaApplier.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a class used to make an XElement conform to a given
//      XML Schema. 
//  
//
// @owner  [....] 
//---------------------------------------------------------------------

namespace System.Data.Services.Design.Xml
{ 
    #region Namespaces.
 
    using System.Diagnostics; 
    using System;
    using System.Linq; 
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Linq;
    using System.Collections.Generic; 
    using System.Runtime.Versioning;
 
    #endregion Namespaces. 

    /// Use this class to remove unexpected elements and attributes from an XDocument instance. 
    internal class XNodeSchemaApplier
    {
        #region Private fields.
 
        /// Namespace manager for current scope.
        private readonly XmlNamespaceManager namespaceManager; 
 
        /// Schemas used to predict expected elements and attributes.
        private readonly XmlSchemaSet schemas; 

        /// XName for xsi:type.
        private readonly XName xsiTypeName;
 
        /// XName for xsi:nil.
        private readonly XName xsiNilName; 
 
        /// Schema validator used to predict expected elements and attributes.
        private XmlSchemaValidator validator; 

        #endregion Private fields.

        #region Constructors. 

        /// Initializes a new  instance. 
        /// Schemas to use to predict elements and attributes. 
        private XNodeSchemaApplier(XmlSchemaSet schemas)
        { 
            Debug.Assert(schemas != null, "schemas != null");

            this.schemas = schemas;
            XNamespace xsi = XNamespace.Get("http://www.w3.org/2001/XMLSchema-instance"); 
            this.xsiTypeName = xsi.GetName("type");
            this.xsiNilName = xsi.GetName("nil"); 
            this.namespaceManager = new XmlNamespaceManager(schemas.NameTable); 
        }
 
        #endregion Constructors.

        #region Internal methods.
 
        /// 
        /// Appends  to the specified , creating as necessary. 
        ///  
        /// List element type.
        /// List to add the element to, possibly null on entry. 
        /// Element to add to the list.
        internal static void AppendWithCreation(ref List list, T element)
        {
            if (list == null) 
            {
                list = new List(); 
            } 

            list.Add(element); 
        }

        /// 
        /// Applies the specified  to remove unexpected elements and attributes from the 
        /// given .
        ///  
        /// Set of schemas to apply. 
        /// Document to remove elements and attributes from.
        internal static void Apply(XmlSchemaSet schemas, XElement element) 
        {
            Debug.Assert(schemas != null, "schemas != null");
            Debug.Assert(element != null, "document != null");
 
            XNodeSchemaApplier applier = new XNodeSchemaApplier(schemas);
            applier.Validate(element); 
        } 

        #endregion Internal methods. 

        #region Private methods.

        /// Determines whether the specified  is expected. 
        /// Element to check.
        ///  
        ///  of the  (passed to avoid recreation). 
        /// 
        /// Expected schema particle. 
        /// true if the element is expected; false otherwise.
        private static bool IsElementExpected(XElement element, XmlQualifiedName elementName, XmlSchemaParticle expected)
        {
            Debug.Assert(element != null, "element != null"); 
            Debug.Assert(elementName != null, "elementName != null");
            Debug.Assert(expected != null, "expected != null"); 
            Debug.Assert( 
                ToQualifiedName(element.Name) == elementName,
                "ToQualifiedName(element.Name) == elementName -- otherwise the caller get the 'caching' wrong"); 

            // These are all possibilities for a particle.
            XmlSchemaGroupRef schemaGroupRef = expected as XmlSchemaGroupRef;
            XmlSchemaAny schemaAny = expected as XmlSchemaAny; 
            XmlSchemaElement schemaElement = expected as XmlSchemaElement;
            XmlSchemaAll schemaAll = expected as XmlSchemaAll; 
            XmlSchemaChoice schemaChoice = expected as XmlSchemaChoice; 
            XmlSchemaSequence schemaSequence = expected as XmlSchemaSequence;
 
            Debug.Assert(schemaGroupRef == null, "schemaGroupRef == null -- the validator flattens this out as options.");
            Debug.Assert(schemaSequence == null, "schemaSequence == null -- the validator flattens this out and picks the right one in seq.");
            Debug.Assert(schemaAll == null, "schemaAll == null -- the validator flattens this out as options.");
            Debug.Assert(schemaChoice == null, "schemaChoice == null -- the validator flattens this out as options."); 

            if (schemaAny != null) 
            { 
                Debug.Assert(
                    schemaAny.Namespace == "##other" || schemaAny.Namespace == "##any", 
                    "schemaAny.Namespace == '##other' || '##any' -- otherwise CSDL XSD resource was changed");
                if (schemaAny.Namespace == "##any")
                {
                    return true; 
                }
                else if (schemaAny.Namespace == "##other") 
                { 
                    string realElementNamespace = element.Name.NamespaceName;
                    if (realElementNamespace != GetTargetNamespace(expected)) 
                    {
                        return true;
                    }
                } 
            }
 
            if (schemaElement != null) 
            {
                if (schemaElement.QualifiedName == elementName) 
                {
                    return true;
                }
            } 

            return false; 
        } 

        /// Gets the target namespace that applies to the specified . 
        /// XML schema object for which to get target namespace.
        /// Target namespace for the specified  (never null).
        private static string GetTargetNamespace(XmlSchemaObject schemaObject)
        { 
            Debug.Assert(schemaObject != null, "schemaObject != null");
 
            string result = null; 
            do
            { 
                XmlSchema schema = schemaObject as XmlSchema;
                if (schema != null)
                {
                    result = schema.TargetNamespace; 
                    Debug.Assert(!String.IsNullOrEmpty(schema.TargetNamespace), "schema.TargetNamespace != null||'' -- otherwise this isn't CSDL");
                } 
                else 
                {
                    schemaObject = schemaObject.Parent; 
                    Debug.Assert(schemaObject != null, "o != null -- otherwise the object isn't parented to a schema");
                }
            }
            while (result == null); 

            return result; 
        } 

        /// Determines whether the specified  is expected. 
        /// Element to check.
        /// Expected schema particles (possibly empty).
        /// true if the element is expected; false otherwise.
        private static bool IsElementExpected(XElement element, XmlSchemaParticle[] expectedParticles) 
        {
            Debug.Assert(element != null, "element != null"); 
            Debug.Assert(expectedParticles != null, "expectedParticles != null"); 

            XmlQualifiedName elementName = ToQualifiedName(element.Name); 
            foreach (var expected in expectedParticles)
            {
                if (IsElementExpected(element, elementName, expected))
                { 
                    return true;
                } 
            } 

            return false; 
        }

        /// Determines whether the specified  is expected.
        /// Attribute to check. 
        /// Expected attributes (possibly empty).
        /// anyAttribute schema for a complex type element (possibly null). 
        /// true if the attribute is expected; false otherwise. 
        private static bool IsAttributeExpected(XAttribute attribute, XmlSchemaAnyAttribute anyAttribute, XmlSchemaAttribute[] expectedAttributes)
        { 
            Debug.Assert(attribute != null, "attribute != null");
            Debug.Assert(expectedAttributes != null, "expectedAttributes != null");
            Debug.Assert(expectedAttributes.All(a => a.Form != XmlSchemaForm.Qualified), "expectedAttributes.All(a => a.Form != XmlSchemaForm.Qualified)");
 
            var name = ToQualifiedName(attribute.Name);
            if (name.Namespace.Length == 0) 
            { 
                foreach (var expected in expectedAttributes)
                { 
                    if (expected.Name == name.Name)
                    {
                        return true;
                    } 
                }
            } 
 
            if (anyAttribute != null)
            { 
                Debug.Assert(
                    anyAttribute.Namespace == "##any" || anyAttribute.Namespace == "##other",
                    "anyAttribute.Namespace == '##any' || '##other' -- otherwise CSDL XSD resource was changed");
                if (anyAttribute.Namespace == "##any") 
                {
                    return true; 
                } 
                else
                { 
                    string attributeNamespace = attribute.Name.NamespaceName;
                    if (attributeNamespace.Length > 0 && attributeNamespace != GetTargetNamespace(anyAttribute))
                    {
                        return true; 
                    }
                } 
            } 

            return false; 
        }

        /// 
        /// Return the  representation of the specified . 
        /// 
        /// XML name to return. 
        /// An  that represents the given . 
        private static XmlQualifiedName ToQualifiedName(XName name)
        { 
            Debug.Assert(name != null, "name != null");
            return new XmlQualifiedName(name.LocalName, name.NamespaceName);
        }
 
        /// Validates the specified  object.
        /// Source object for validation (must be an element). 
        private void Validate(XElement element) 
        {
            Debug.Assert(element != null, "element != null"); 

            XmlSchemaValidationFlags validationFlags = XmlSchemaValidationFlags.AllowXmlAttributes;
            this.PushAncestorsAndSelf(element.Parent);
 
            validator = new XmlSchemaValidator(schemas.NameTable, schemas, namespaceManager, validationFlags);
            validator.XmlResolver = null; 
            validator.Initialize(); 
            this.ValidateElement(element);
            validator.EndValidation(); 
        }

        /// Pushes the specifed  namespaces and those of ancestors.
        /// Element to push from - possibly null. 
        /// 
        /// Pushing in reverse order (up the tree rather than down the tree) is OK, because we check that 
        /// the namespace local name hasn't been added yet. Use  as we go down 
        /// the tree to push/pop as usual.
        ///  
        private void PushAncestorsAndSelf(XElement element)
        {
            while (element != null)
            { 
                foreach (XAttribute attribute in element.Attributes())
                { 
                    if (attribute.IsNamespaceDeclaration) 
                    {
                        string localName = attribute.Name.LocalName; 
                        if (localName == "xmlns")
                        {
                            localName = string.Empty;
                        } 

                        if (!namespaceManager.HasNamespace(localName)) 
                        { 
                            namespaceManager.AddNamespace(localName, attribute.Value);
                        } 
                    }
                }

                element = element.Parent as XElement; 
            }
        } 
 
        /// Pushes the specifed  namespaces and those of ancestors.
        /// Element to push. 
        /// The value for xsi:type on this element.
        /// The value for xsi:nil on this element.
        private void PushElement(XElement element, ref string xsiType, ref string xsiNil)
        { 
            Debug.Assert(element != null, "e != null");
            namespaceManager.PushScope(); 
            foreach (XAttribute attribute in element.Attributes()) 
            {
                if (attribute.IsNamespaceDeclaration) 
                {
                    string localName = attribute.Name.LocalName;
                    if (localName == "xmlns")
                    { 
                        localName = string.Empty;
                    } 
 
                    namespaceManager.AddNamespace(localName, attribute.Value);
                } 
                else
                {
                    XName name = attribute.Name;
                    if (name == xsiTypeName) 
                    {
                        xsiType = attribute.Value; 
                    } 
                    else if (name == xsiNilName)
                    { 
                        xsiNil = attribute.Value;
                    }
                }
            } 
        }
 
        /// Validates all attributes on the specified . 
        /// Element to validate attributes on.
        private void ValidateAttributes(XElement element) 
        {
            Debug.Assert(element != null, "e != null");

            foreach (XAttribute attribute in element.Attributes()) 
            {
                if (!attribute.IsNamespaceDeclaration) 
                { 
                    validator.ValidateAttribute(attribute.Name.LocalName, attribute.Name.NamespaceName, attribute.Value, null);
                } 
            }
        }

        //// SxS: This method does not expose any resources to the caller and passes null as resource names to 
        //// XmlSchemaValidator.ValidateElement (annotated with ResourceExposure(ResourceScope.None).
        //// It's OK to suppress the SxS warning. 
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] 
        [ResourceExposure(ResourceScope.None)]
        private void ValidateElement(XElement e) 
        {
            Debug.Assert(e != null, "e != null");

            XmlSchemaInfo schemaInfo = new XmlSchemaInfo(); 
            string xsiType = null;
            string xsiNil = null; 
            this.PushElement(e, ref xsiType, ref xsiNil); 

            // The current element is always valid - otherwise we wouldn't have recursed into it in the first place. 
            validator.ValidateElement(e.Name.LocalName, e.Name.NamespaceName, schemaInfo, xsiType, xsiNil, null, null);

            // When we have no schema element, then e was included but we don't know about it - it's an extension
            // element, likely under CSDL documentation. We'll skip the whole thing in this case. 
            if (schemaInfo.SchemaElement != null)
            { 
                XmlSchemaComplexType schemaComplexType = schemaInfo.SchemaElement.ElementSchemaType as XmlSchemaComplexType; 
                this.TrimAttributes(e, (schemaComplexType == null) ? null : schemaComplexType.AttributeWildcard);
                this.ValidateAttributes(e); 
                validator.ValidateEndOfAttributes(null);

                this.TrimAndValidateNodes(e);
            } 

            validator.ValidateEndElement(null); 
            this.namespaceManager.PopScope(); 
        }
 
        /// Removes attributes from the specified  if they're unexpected.
        /// Element to remove attributes from.
        /// anyAttribute schema for a complex type element (possibly null).
        private void TrimAttributes(XElement element, XmlSchemaAnyAttribute anyAttribute) 
        {
            Debug.Assert(element != null, "e != null"); 
 
            List unexpectedAttributes = null;
            var expectedAttributes = validator.GetExpectedAttributes(); 
            foreach (XAttribute attribute in element.Attributes())
            {
                if (attribute.IsNamespaceDeclaration)
                { 
                    continue;
                } 
 
                if (!IsAttributeExpected(attribute, anyAttribute, expectedAttributes))
                { 
                    AppendWithCreation(ref unexpectedAttributes, attribute);
                }
            }
 
            if (unexpectedAttributes != null)
            { 
                foreach (var attribute in unexpectedAttributes) 
                {
                    attribute.Remove(); 
                }
            }
        }
 
        /// 
        /// Removes nodes from the specified  element and validates its nodes. 
        ///  
        /// 
        ///  
        /// While it's cleaner to do this in two passes, trim then validate, like we do with attributes, we need to
        /// validate as we go for the validator to return sequence elements in the right order.
        /// 
        private void TrimAndValidateNodes(XElement parent) 
        {
            Debug.Assert(parent != null, "parent != null"); 
 
            List unexpectedNodes = null;
            XmlSchemaParticle[] expectedParticles = null; 
            foreach (XNode node in parent.Nodes())
            {
                // expectedParticles will be null the first iteration and right after we validate,
                // when we potentially have something different to validate against. 
                if (expectedParticles == null)
                { 
                    expectedParticles = validator.GetExpectedParticles(); 
                }
 
                Debug.Assert(expectedParticles != null, "expectedParticles != null -- GetExpectedParticles should return empty at worst");
                XElement element = node as XElement;
                if (element != null)
                { 
                    if (!IsElementExpected(element, expectedParticles))
                    { 
                        AppendWithCreation(ref unexpectedNodes, element); 
                    }
                    else 
                    {
                        this.ValidateElement(element);
                        expectedParticles = null;
                    } 
                }
                else 
                { 
                    XText text = node as XText;
                    if (text != null) 
                    {
                        string s = text.Value;
                        if (s.Length > 0)
                        { 
                            validator.ValidateText(s);
                            expectedParticles = null; 
                        } 
                    }
                } 
            }

            if (unexpectedNodes != null)
            { 
                foreach (var node in unexpectedNodes)
                { 
                    node.Remove(); 
                }
            } 
        }

        #endregion Private methods.
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK