| File: System\Xml\XmlBoundElement.cs | Web Access |
| Project: src\runtime\src\libraries\System.Data.Common\src\System.Data.Common.csproj (System.Data.Common) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Data; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; #pragma warning disable 618 // ignore obsolete warning about XmlDataDocument namespace System.Xml { internal enum ElementState { None, Defoliated, WeakFoliation, StrongFoliation, Foliating, Defoliating, } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)] internal sealed class XmlBoundElement : XmlElement { private DataRow? _row; private ElementState _state; internal XmlBoundElement(string prefix, string localName, string namespaceURI, XmlDocument doc) : base(prefix, localName, namespaceURI, doc) { _state = ElementState.None; } public override XmlAttributeCollection Attributes { get { AutoFoliate(); return base.Attributes; } } public override bool HasAttributes => Attributes.Count > 0; public override XmlNode? FirstChild { get { AutoFoliate(); return base.FirstChild; } } internal XmlNode? SafeFirstChild => base.FirstChild; public override XmlNode? LastChild { get { AutoFoliate(); return base.LastChild; } } public override XmlNode? PreviousSibling { get { XmlNode? prev = base.PreviousSibling; if (prev == null) { XmlBoundElement? parent = ParentNode as XmlBoundElement; if (parent != null) { parent.AutoFoliate(); return base.PreviousSibling; } } return prev; } } internal XmlNode? SafePreviousSibling => base.PreviousSibling; public override XmlNode? NextSibling { get { XmlNode? next = base.NextSibling; if (next == null) { XmlBoundElement? parent = ParentNode as XmlBoundElement; if (parent != null) { parent.AutoFoliate(); return base.NextSibling; } } return next; } } internal XmlNode? SafeNextSibling => base.NextSibling; public override bool HasChildNodes { get { AutoFoliate(); return base.HasChildNodes; } } public override XmlNode? InsertBefore(XmlNode newChild, XmlNode? refChild) { AutoFoliate(); return base.InsertBefore(newChild, refChild); } public override XmlNode? InsertAfter(XmlNode newChild, XmlNode? refChild) { AutoFoliate(); return base.InsertAfter(newChild, refChild); } public override XmlNode ReplaceChild(XmlNode newChild, XmlNode oldChild) { AutoFoliate(); return base.ReplaceChild(newChild, oldChild); } public override XmlNode? AppendChild(XmlNode newChild) { AutoFoliate(); return base.AppendChild(newChild); } internal void RemoveAllChildren() { XmlNode? child = FirstChild; XmlNode? sibling; while (child != null) { sibling = child.NextSibling; RemoveChild(child); child = sibling; } } public override string InnerXml { get { return base.InnerXml; } set { RemoveAllChildren(); XmlDataDocument doc = (XmlDataDocument)OwnerDocument; bool bOrigIgnoreXmlEvents = doc.IgnoreXmlEvents; bool bOrigIgnoreDataSetEvents = doc.IgnoreDataSetEvents; doc.IgnoreXmlEvents = true; doc.IgnoreDataSetEvents = true; base.InnerXml = value; doc.SyncTree(this); doc.IgnoreDataSetEvents = bOrigIgnoreDataSetEvents; doc.IgnoreXmlEvents = bOrigIgnoreXmlEvents; } } internal DataRow? Row { get { return _row; } set { _row = value; } } internal bool IsFoliated { get { while (_state == ElementState.Foliating || _state == ElementState.Defoliating) { Thread.Sleep(0); } //has to be sure that we are either foliated or defoliated when ask for IsFoliated. return _state != ElementState.Defoliated; } } internal ElementState ElementState { get { return _state; } set { _state = value; } } internal void Foliate(ElementState newState) { XmlDataDocument doc = (XmlDataDocument)OwnerDocument; doc?.Foliate(this, newState); } // Foliate the node as a side effect of user calling functions on this node (like NextSibling) OR as a side effect of DataDocNav using nodes to do editing private void AutoFoliate() { XmlDataDocument doc = (XmlDataDocument)OwnerDocument; doc?.Foliate(this, doc.AutoFoliationState); } public override XmlNode CloneNode(bool deep) { XmlDataDocument doc = (XmlDataDocument)(OwnerDocument); ElementState oldAutoFoliationState = doc.AutoFoliationState; doc.AutoFoliationState = ElementState.WeakFoliation; XmlElement element; try { Foliate(ElementState.WeakFoliation); element = (XmlElement)(base.CloneNode(deep)); // Clone should create a XmlBoundElement node Debug.Assert(element is XmlBoundElement); } finally { doc.AutoFoliationState = oldAutoFoliationState; } return element; } public override void WriteContentTo(XmlWriter w) { DataPointer dp = new DataPointer((XmlDataDocument)OwnerDocument, this); try { dp.AddPointer(); WriteBoundElementContentTo(dp, w); } finally { dp.SetNoLongerUse(); } } public override void WriteTo(XmlWriter w) { DataPointer dp = new DataPointer((XmlDataDocument)OwnerDocument, this); try { dp.AddPointer(); WriteRootBoundElementTo(dp, w); } finally { dp.SetNoLongerUse(); } } private void WriteRootBoundElementTo(DataPointer dp, XmlWriter w) { Debug.Assert(dp.NodeType == XmlNodeType.Element); XmlDataDocument doc = (XmlDataDocument)OwnerDocument; w.WriteStartElement(dp.Prefix, dp.LocalName, dp.NamespaceURI); int cAttr = dp.AttributeCount; bool bHasXSI = false; if (cAttr > 0) { for (int iAttr = 0; iAttr < cAttr; iAttr++) { dp.MoveToAttribute(iAttr); if (dp.Prefix == "xmlns" && dp.LocalName == XmlDataDocument.XSI) { bHasXSI = true; } WriteTo(dp, w); dp.MoveToOwnerElement(); } } if (!bHasXSI && doc._bLoadFromDataSet && doc._bHasXSINIL) { w.WriteAttributeString("xmlns", "xsi", "http://www.w3.org/2000/xmlns/", Keywords.XSINS); } WriteBoundElementContentTo(dp, w); // Force long end tag when the elem is not empty, even if there are no children. if (dp.IsEmptyElement) { w.WriteEndElement(); } else { w.WriteFullEndElement(); } } private static void WriteBoundElementTo(DataPointer dp, XmlWriter w) { Debug.Assert(dp.NodeType == XmlNodeType.Element); w.WriteStartElement(dp.Prefix, dp.LocalName, dp.NamespaceURI); int cAttr = dp.AttributeCount; if (cAttr > 0) { for (int iAttr = 0; iAttr < cAttr; iAttr++) { dp.MoveToAttribute(iAttr); WriteTo(dp, w); dp.MoveToOwnerElement(); } } WriteBoundElementContentTo(dp, w); // Force long end tag when the elem is not empty, even if there are no children. if (dp.IsEmptyElement) { w.WriteEndElement(); } else { w.WriteFullEndElement(); } } private static void WriteBoundElementContentTo(DataPointer dp, XmlWriter w) { if (!dp.IsEmptyElement && dp.MoveToFirstChild()) { do { WriteTo(dp, w); } while (dp.MoveToNextSibling()); dp.MoveToParent(); } } private static void WriteTo(DataPointer dp, XmlWriter w) { switch (dp.NodeType) { case XmlNodeType.Attribute: if (!dp.IsDefault) { w.WriteStartAttribute(dp.Prefix, dp.LocalName, dp.NamespaceURI); if (dp.MoveToFirstChild()) { do { WriteTo(dp, w); } while (dp.MoveToNextSibling()); dp.MoveToParent(); } w.WriteEndAttribute(); } break; case XmlNodeType.Element: WriteBoundElementTo(dp, w); break; case XmlNodeType.Text: w.WriteString(dp.Value); break; default: Debug.Assert(((IXmlDataVirtualNode)dp).IsOnColumn(null)); dp.GetNode()?.WriteTo(w); break; } } public override XmlNodeList GetElementsByTagName(string name) { // Retrieving nodes from the returned nodelist may cause foliation which causes new nodes to be created, // so the System.Xml iterator will throw if this happens during iteration. To avoid this, foliate everything // before iteration, so iteration will not cause foliation (and as a result of this, creation of new nodes). XmlNodeList tempNodeList = base.GetElementsByTagName(name); _ = tempNodeList.Count; return tempNodeList; } } }