| File: System\Xml\Schema\Parser.cs | Web Access |
| Project: src\runtime\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Text; namespace System.Xml.Schema { internal sealed partial class Parser { private SchemaType _schemaType; private readonly XmlNameTable _nameTable; private readonly SchemaNames _schemaNames; private readonly ValidationEventHandler? _eventHandler; private XmlNamespaceManager? _namespaceManager; private XmlReader? _reader; private PositionInfo? _positionInfo; private bool _isProcessNamespaces; private int _schemaXmlDepth; private int _markupDepth; private SchemaBuilder? _builder; private XmlSchema? _schema; private SchemaInfo? _xdrSchema; private XmlResolver? _xmlResolver; //to be used only by XDRBuilder //xs:Annotation perf fix private readonly XmlDocument _dummyDocument; private bool _processMarkup; private XmlNode? _parentNode; private XmlNamespaceManager? _annotationNSManager; private string? _xmlns; public Parser(SchemaType schemaType, XmlNameTable nameTable, SchemaNames schemaNames, ValidationEventHandler? eventHandler) { _schemaType = schemaType; _nameTable = nameTable; _schemaNames = schemaNames; _eventHandler = eventHandler; _xmlResolver = null; _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); if (!CheckSchemaRoot(rootType, out string? 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, [NotNullWhen(false)] out string? code) { code = null; if (_schemaType == SchemaType.None) { _schemaType = rootType; } switch (rootType) { case SchemaType.XSD: if (_schemaType != SchemaType.XSD) { code = SR.Sch_MixSchemaTypes; return false; } break; case SchemaType.XDR: if (_schemaType == SchemaType.XSD) { code = SR.Sch_XSDSchemaOnly; return false; } else if (_schemaType != SchemaType.XDR) { code = SR.Sch_MixSchemaTypes; return false; } break; case SchemaType.DTD: //Did not detect schema type that can be parsed by this parser case SchemaType.None: code = SR.Sch_SchemaRootExpected; if (_schemaType == SchemaType.XSD) { code = SR.Sch_XSDSchemaRootExpected; } return false; default: Debug.Fail($"Unexpected root type {rootType}"); 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(); 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 System.Xml