File: FrameworkFork\Microsoft.Xml\Xml\schema\Parser.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
namespace Microsoft.Xml.Schema
{
    using System;
    using System.Collections;
    using System.Globalization;
    using System.Text;
    using System.IO;
    using System.Diagnostics;
 
    internal sealed partial class Parser
    {
        private SchemaType _schemaType;
        private XmlNameTable _nameTable;
        private SchemaNames _schemaNames;
        private ValidationEventHandler _eventHandler;
        private XmlNamespaceManager _namespaceManager;
        private XmlReader _reader;
        private PositionInfo _positionInfo;
        private bool _isProcessNamespaces;
        private int _schemaXmlDepth = 0;
        private int _markupDepth;
        private SchemaBuilder _builder;
        private XmlSchema _schema;
        private SchemaInfo _xdrSchema;
        private XmlResolver _xmlResolver = null; //to be used only by XDRBuilder
 
        //xs:Annotation perf fix
        private XmlDocument _dummyDocument;
        private bool _processMarkup;
        private XmlNode _parentNode;
        private XmlNamespaceManager _annotationNSManager;
        private string _xmlns;
 
        //Whitespace check for text nodes
        private XmlCharType _xmlCharType = XmlCharType.Instance;
 
        public Parser(SchemaType schemaType, XmlNameTable nameTable, SchemaNames schemaNames, ValidationEventHandler eventHandler)
        {
            _schemaType = schemaType;
            _nameTable = nameTable;
            _schemaNames = schemaNames;
            _eventHandler = eventHandler;
            _xmlResolver = Microsoft.Xml.XmlConfiguration.XmlReaderSection.CreateDefaultResolver();
            _processMarkup = true;
            _dummyDocument = new XmlDocument();
        }
 
        public SchemaType Parse(XmlReader reader, string targetNamespace)
        {
            StartParsing(reader, targetNamespace);
            while (ParseReaderNode() && reader.Read()) { }
            return FinishParsing();
        }
 
        public void StartParsing(XmlReader reader, string targetNamespace)
        {
            _reader = reader;
            _positionInfo = PositionInfo.GetPositionInfo(reader);
            _namespaceManager = reader.NamespaceManager;
            if (_namespaceManager == null)
            {
                _namespaceManager = new XmlNamespaceManager(_nameTable);
                _isProcessNamespaces = true;
            }
            else
            {
                _isProcessNamespaces = false;
            }
            while (reader.NodeType != XmlNodeType.Element && reader.Read()) { }
 
            _markupDepth = int.MaxValue;
            _schemaXmlDepth = reader.Depth;
            SchemaType rootType = _schemaNames.SchemaTypeFromRoot(reader.LocalName, reader.NamespaceURI);
 
            string code;
            if (!CheckSchemaRoot(rootType, out code))
            {
                throw new XmlSchemaException(code, reader.BaseURI, _positionInfo.LineNumber, _positionInfo.LinePosition);
            }
 
            if (_schemaType == SchemaType.XSD)
            {
                _schema = new XmlSchema();
                _schema.BaseUri = new Uri(reader.BaseURI, UriKind.RelativeOrAbsolute);
                _builder = new XsdBuilder(reader, _namespaceManager, _schema, _nameTable, _schemaNames, _eventHandler);
            }
            else
            {
                Debug.Assert(_schemaType == SchemaType.XDR);
                _xdrSchema = new SchemaInfo();
                _xdrSchema.SchemaType = SchemaType.XDR;
                _builder = new XdrBuilder(reader, _namespaceManager, _xdrSchema, targetNamespace, _nameTable, _schemaNames, _eventHandler);
                ((XdrBuilder)_builder).XmlResolver = _xmlResolver;
            }
        }
 
        private bool CheckSchemaRoot(SchemaType rootType, out string code)
        {
            code = null;
            if (_schemaType == SchemaType.None)
            {
                _schemaType = rootType;
            }
            switch (rootType)
            {
                case SchemaType.XSD:
                    if (_schemaType != SchemaType.XSD)
                    {
                        code = ResXml.Sch_MixSchemaTypes;
                        return false;
                    }
                    break;
 
                case SchemaType.XDR:
                    if (_schemaType == SchemaType.XSD)
                    {
                        code = ResXml.Sch_XSDSchemaOnly;
                        return false;
                    }
                    else if (_schemaType != SchemaType.XDR)
                    {
                        code = ResXml.Sch_MixSchemaTypes;
                        return false;
                    }
                    break;
 
                case SchemaType.DTD: //Did not detect schema type that can be parsed by this parser
                case SchemaType.None:
                    code = ResXml.Sch_SchemaRootExpected;
                    if (_schemaType == SchemaType.XSD)
                    {
                        code = ResXml.Sch_XSDSchemaRootExpected;
                    }
                    return false;
 
                default:
                    Debug.Assert(false);
                    break;
            }
            return true;
        }
 
        public SchemaType FinishParsing()
        {
            return _schemaType;
        }
 
        public XmlSchema XmlSchema
        {
            get { return _schema; }
        }
 
        internal XmlResolver XmlResolver
        {
            set
            {
                _xmlResolver = value;
            }
        }
 
        public SchemaInfo XdrSchema
        {
            get { return _xdrSchema; }
        }
 
        public bool ParseReaderNode()
        {
            if (_reader.Depth > _markupDepth)
            {
                if (_processMarkup)
                {
                    ProcessAppInfoDocMarkup(false);
                }
                return true;
            }
            else if (_reader.NodeType == XmlNodeType.Element)
            {
                if (_builder.ProcessElement(_reader.Prefix, _reader.LocalName, _reader.NamespaceURI))
                {
                    _namespaceManager.PushScope();
                    if (_reader.MoveToFirstAttribute())
                    {
                        do
                        {
                            _builder.ProcessAttribute(_reader.Prefix, _reader.LocalName, _reader.NamespaceURI, _reader.Value);
                            if (Ref.Equal(_reader.NamespaceURI, _schemaNames.NsXmlNs) && _isProcessNamespaces)
                            {
                                _namespaceManager.AddNamespace(_reader.Prefix.Length == 0 ? string.Empty : _reader.LocalName, _reader.Value);
                            }
                        }
                        while (_reader.MoveToNextAttribute());
                        _reader.MoveToElement(); // get back to the element
                    }
                    _builder.StartChildren();
                    if (_reader.IsEmptyElement)
                    {
                        _namespaceManager.PopScope();
                        _builder.EndChildren();
                        if (_reader.Depth == _schemaXmlDepth)
                        {
                            return false; // done
                        }
                    }
                    else if (!_builder.IsContentParsed())
                    { //AppInfo and Documentation
                        _markupDepth = _reader.Depth;
                        _processMarkup = true;
                        if (_annotationNSManager == null)
                        {
                            _annotationNSManager = new XmlNamespaceManager(_nameTable);
                            _xmlns = _nameTable.Add("xmlns");
                        }
                        ProcessAppInfoDocMarkup(true);
                    }
                }
                else if (!_reader.IsEmptyElement)
                { //UnsupportedElement in that context
                    _markupDepth = _reader.Depth;
                    _processMarkup = false; //Hack to not process unsupported elements
                }
            }
            else if (_reader.NodeType == XmlNodeType.Text)
            { //Check for whitespace
                if (!_xmlCharType.IsOnlyWhitespace(_reader.Value))
                {
                    _builder.ProcessCData(_reader.Value);
                }
            }
            else if (_reader.NodeType == XmlNodeType.EntityReference ||
                _reader.NodeType == XmlNodeType.SignificantWhitespace ||
                _reader.NodeType == XmlNodeType.CDATA)
            {
                _builder.ProcessCData(_reader.Value);
            }
            else if (_reader.NodeType == XmlNodeType.EndElement)
            {
                if (_reader.Depth == _markupDepth)
                {
                    if (_processMarkup)
                    {
                        Debug.Assert(_parentNode != null);
                        XmlNodeList list = _parentNode.ChildNodes;
                        XmlNode[] markup = new XmlNode[list.Count];
                        for (int i = 0; i < list.Count; i++)
                        {
                            markup[i] = list[i];
                        }
                        _builder.ProcessMarkup(markup);
                        _namespaceManager.PopScope();
                        _builder.EndChildren();
                    }
                    _markupDepth = int.MaxValue;
                }
                else
                {
                    _namespaceManager.PopScope();
                    _builder.EndChildren();
                }
                if (_reader.Depth == _schemaXmlDepth)
                {
                    return false; // done
                }
            }
            return true;
        }
 
        private void ProcessAppInfoDocMarkup(bool root)
        {
            //First time reader is positioned on AppInfo or Documentation element
            XmlNode currentNode = null;
 
            switch (_reader.NodeType)
            {
                case XmlNodeType.Element:
                    _annotationNSManager.PushScope();
                    currentNode = LoadElementNode(root);
                    //  Dev10 (TFS) #479761: The following code was to address the issue of where an in-scope namespace delaration attribute
                    //      was not added when an element follows an empty element. This fix will result in persisting schema in a consistent form
                    //      although it does not change the semantic meaning of the schema.
                    //      Since it is as a breaking change and Dev10 needs to maintain the backward compatibility, this fix is being reverted.
                    //  if (reader.IsEmptyElement) {
                    //      annotationNSManager.PopScope();
                    //  }
                    break;
 
                case XmlNodeType.Text:
                    currentNode = _dummyDocument.CreateTextNode(_reader.Value);
                    goto default;
 
                case XmlNodeType.SignificantWhitespace:
                    currentNode = _dummyDocument.CreateSignificantWhitespace(_reader.Value);
                    goto default;
 
                case XmlNodeType.CDATA:
                    currentNode = _dummyDocument.CreateCDataSection(_reader.Value);
                    goto default;
 
                case XmlNodeType.EntityReference:
                    currentNode = _dummyDocument.CreateEntityReference(_reader.Name);
                    goto default;
 
                case XmlNodeType.Comment:
                    currentNode = _dummyDocument.CreateComment(_reader.Value);
                    goto default;
 
                case XmlNodeType.ProcessingInstruction:
                    currentNode = _dummyDocument.CreateProcessingInstruction(_reader.Name, _reader.Value);
                    goto default;
 
                case XmlNodeType.EndEntity:
                    break;
 
                case XmlNodeType.Whitespace:
                    break;
 
                case XmlNodeType.EndElement:
                    _annotationNSManager.PopScope();
                    _parentNode = _parentNode.ParentNode;
                    break;
 
                default: //other possible node types: Document/DocType/DocumentFrag/Entity/Notation/Xmldecl cannot appear as children of xs:appInfo or xs:doc
                    Debug.Assert(currentNode != null);
                    Debug.Assert(_parentNode != null);
                    _parentNode.AppendChild(currentNode);
                    break;
            }
        }
 
        private XmlElement LoadElementNode(bool root)
        {
            Debug.Assert(_reader.NodeType == XmlNodeType.Element);
 
            XmlReader r = _reader;
            bool fEmptyElement = r.IsEmptyElement;
 
            XmlElement element = _dummyDocument.CreateElement(r.Prefix, r.LocalName, r.NamespaceURI);
            element.IsEmpty = fEmptyElement;
 
            if (root)
            {
                _parentNode = element;
            }
            else
            {
                XmlAttributeCollection attributes = element.Attributes;
                if (r.MoveToFirstAttribute())
                {
                    do
                    {
                        if (Ref.Equal(r.NamespaceURI, _schemaNames.NsXmlNs))
                        { //Namespace Attribute
                            _annotationNSManager.AddNamespace(r.Prefix.Length == 0 ? string.Empty : _reader.LocalName, _reader.Value);
                        }
                        XmlAttribute attr = LoadAttributeNode();
                        attributes.Append(attr);
                    } while (r.MoveToNextAttribute());
                }
                r.MoveToElement();
                string ns = _annotationNSManager.LookupNamespace(r.Prefix);
                if (ns == null)
                {
                    XmlAttribute attr = CreateXmlNsAttribute(r.Prefix, _namespaceManager.LookupNamespace(r.Prefix));
                    attributes.Append(attr);
                }
                else if (ns.Length == 0)
                { //string.Empty prefix is mapped to string.Empty NS by default
                    string elemNS = _namespaceManager.LookupNamespace(r.Prefix);
                    if (elemNS != string.Empty)
                    {
                        XmlAttribute attr = CreateXmlNsAttribute(r.Prefix, elemNS);
                        attributes.Append(attr);
                    }
                }
 
                while (r.MoveToNextAttribute())
                {
                    if (r.Prefix.Length != 0)
                    {
                        string attNS = _annotationNSManager.LookupNamespace(r.Prefix);
                        if (attNS == null)
                        {
                            XmlAttribute attr = CreateXmlNsAttribute(r.Prefix, _namespaceManager.LookupNamespace(r.Prefix));
                            attributes.Append(attr);
                        }
                    }
                }
                r.MoveToElement();
 
                _parentNode.AppendChild(element);
                if (!r.IsEmptyElement)
                {
                    _parentNode = element;
                }
            }
            return element;
        }
 
        private XmlAttribute CreateXmlNsAttribute(string prefix, string value)
        {
            XmlAttribute attr;
            if (prefix.Length == 0)
            {
                attr = _dummyDocument.CreateAttribute(string.Empty, _xmlns, XmlReservedNs.NsXmlNs);
            }
            else
            {
                attr = _dummyDocument.CreateAttribute(_xmlns, prefix, XmlReservedNs.NsXmlNs);
            }
            attr.AppendChild(_dummyDocument.CreateTextNode(value));
            _annotationNSManager.AddNamespace(prefix, value);
            return attr;
        }
 
        private XmlAttribute LoadAttributeNode()
        {
            Debug.Assert(_reader.NodeType == XmlNodeType.Attribute);
 
            XmlReader r = _reader;
 
            XmlAttribute attr = _dummyDocument.CreateAttribute(r.Prefix, r.LocalName, r.NamespaceURI);
 
            while (r.ReadAttributeValue())
            {
                switch (r.NodeType)
                {
                    case XmlNodeType.Text:
                        attr.AppendChild(_dummyDocument.CreateTextNode(r.Value));
                        continue;
                    case XmlNodeType.EntityReference:
                        attr.AppendChild(LoadEntityReferenceInAttribute());
                        continue;
                    default:
                        throw XmlLoader.UnexpectedNodeType(r.NodeType);
                }
            }
 
            return attr;
        }
 
        private XmlEntityReference LoadEntityReferenceInAttribute()
        {
            Debug.Assert(_reader.NodeType == XmlNodeType.EntityReference);
 
            XmlEntityReference eref = _dummyDocument.CreateEntityReference(_reader.LocalName);
            if (!_reader.CanResolveEntity)
            {
                return eref;
            }
            _reader.ResolveEntity();
 
            while (_reader.ReadAttributeValue())
            {
                switch (_reader.NodeType)
                {
                    case XmlNodeType.Text:
                        eref.AppendChild(_dummyDocument.CreateTextNode(_reader.Value));
                        continue;
                    case XmlNodeType.EndEntity:
                        if (eref.ChildNodes.Count == 0)
                        {
                            eref.AppendChild(_dummyDocument.CreateTextNode(String.Empty));
                        }
                        return eref;
                    case XmlNodeType.EntityReference:
                        eref.AppendChild(LoadEntityReferenceInAttribute());
                        break;
                    default:
                        throw XmlLoader.UnexpectedNodeType(_reader.NodeType);
                }
            }
 
            return eref;
        }
    };
} // namespace Microsoft.Xml