From 5a53c6f0c467cd64ea066aae0278a13e0c60ae55 Mon Sep 17 00:00:00 2001 From: Cory Nelson Date: Mon, 17 Nov 2014 10:50:36 -0600 Subject: [PATCH 01/13] Add async document/element loading for XLinq. --- .../System.Xml.XDocument.csproj | 3 +- .../System/Xml/Linq/XContainer.cs | 97 +++++++++++++++---- .../System/Xml/Linq/XDocument.cs | 54 ++++++++++- .../System/Xml/Linq/XElement.cs | 73 ++++++++++++-- 4 files changed, 196 insertions(+), 31 deletions(-) diff --git a/src/System.Xml.XDocument/System.Xml.XDocument.csproj b/src/System.Xml.XDocument/System.Xml.XDocument.csproj index 46122d3781f7..e15da69484c3 100644 --- a/src/System.Xml.XDocument/System.Xml.XDocument.csproj +++ b/src/System.Xml.XDocument/System.Xml.XDocument.csproj @@ -1,4 +1,4 @@ - + @@ -61,6 +61,7 @@ + diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs index 0e7f9974d4d7..8a395915c4c4 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs @@ -7,6 +7,8 @@ using IEnumerable = System.Collections.IEnumerable; using StringBuilder = System.Text.StringBuilder; using Interlocked = System.Threading.Interlocked; +using System.Threading.Tasks; +using System.Threading; namespace System.Xml.Linq { @@ -851,10 +853,74 @@ internal static string GetStringValue(object value) internal void ReadContentFrom(XmlReader r) { if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); - XContainer c = this; + + ContentReader cr = new ContentReader(this); + while (cr.ReadContentFrom(this, r) && r.Read()) ; + } + + internal void ReadContentFrom(XmlReader r, LoadOptions o) + { + if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == 0) + { + ReadContentFrom(r); + return; + } + if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + + ContentReader cr = new ContentReader(this); + while (cr.ReadContentFrom(this, r, o) && r.Read()) ; + } + + internal async Task ReadContentFromAsync(XmlReader r, CancellationToken token) + { + if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + + ContentReader cr = new ContentReader(this); + while (!token.IsCancellationRequested && cr.ReadContentFrom(this, r) && await r.ReadAsync().ConfigureAwait(false)) ; + + token.ThrowIfCancellationRequested(); + } + + internal async Task ReadContentFromAsync(XmlReader r, LoadOptions o, CancellationToken token) + { + if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == 0) + { + await ReadContentFromAsync(r, token).ConfigureAwait(false); + return; + } + if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + + ContentReader cr = new ContentReader(this); + while (!token.IsCancellationRequested && cr.ReadContentFrom(this, r, o) && await r.ReadAsync().ConfigureAwait(false)) ; + + token.ThrowIfCancellationRequested(); + } + + /// + /// Contains the inner loop shared between ReadContentFrom / ReadContentFromAsync. + /// + sealed class ContentReader + { + XContainer c; + XNode n; NamespaceCache eCache = new NamespaceCache(); NamespaceCache aCache = new NamespaceCache(); - do + string baseUri; + IXmlLineInfo li; + + public ContentReader(XContainer c) + { + this.c = c; + } + + public ContentReader(XContainer @this, XmlReader r, LoadOptions o) + { + c = @this; + baseUri = (o & LoadOptions.SetBaseUri) != 0 ? r.BaseURI : null; + li = (o & LoadOptions.SetLineInfo) != 0 ? r as IXmlLineInfo : null; + } + + public bool ReadContentFrom(XContainer @this, XmlReader r) { switch (r.NodeType) { @@ -879,7 +945,7 @@ internal void ReadContentFrom(XmlReader r) { c.content = string.Empty; } - if (c == this) return; + if (c == @this) return false; c = c.parent; break; case XmlNodeType.Text: @@ -908,24 +974,11 @@ internal void ReadContentFrom(XmlReader r) default: throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnexpectedNodeType, r.NodeType)); } - } while (r.Read()); - } - internal void ReadContentFrom(XmlReader r, LoadOptions o) - { - if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == 0) - { - ReadContentFrom(r); - return; + return true; } - if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); - XContainer c = this; - XNode n = null; - NamespaceCache eCache = new NamespaceCache(); - NamespaceCache aCache = new NamespaceCache(); - string baseUri = (o & LoadOptions.SetBaseUri) != 0 ? r.BaseURI : null; - IXmlLineInfo li = (o & LoadOptions.SetLineInfo) != 0 ? r as IXmlLineInfo : null; - do + + public bool ReadContentFrom(XContainer @this, XmlReader r, LoadOptions o) { string uri = r.BaseURI; switch (r.NodeType) @@ -979,7 +1032,7 @@ internal void ReadContentFrom(XmlReader r, LoadOptions o) { e.SetEndElementLineInfo(li.LineNumber, li.LinePosition); } - if (c == this) return; + if (c == @this) return false; if (baseUri != null && c.HasBaseUri) { baseUri = c.parent.BaseUri; @@ -1034,7 +1087,9 @@ internal void ReadContentFrom(XmlReader r, LoadOptions o) c.AddNodeSkipNotify(n); n = null; } - } while (r.Read()); + + return true; + } } internal void RemoveNode(XNode n) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs index e54a8c0b7d10..33b53abb1383 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs @@ -5,6 +5,8 @@ using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; using Encoding = System.Text.Encoding; +using System.Threading; +using System.Threading.Tasks; namespace System.Xml.Linq { @@ -350,6 +352,55 @@ public static XDocument Load(XmlReader reader, LoadOptions options) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.ReadState == ReadState.Initial) reader.Read(); + + XDocument d = InitLoad(reader, options); + d.ReadContentFrom(reader, options); + + if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile); + if (d.Root == null) throw new InvalidOperationException(SR.InvalidOperation_MissingRoot); + return d; + } + + /// + /// Create a new containing the contents of the + /// passed in . + /// + /// + /// An containing the XML to be read into the new + /// . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// + /// A new containing the contents of the passed + /// in . + /// + public static async Task LoadAsync(XmlReader reader, LoadOptions options, CancellationToken token) + { + if (reader == null) throw new ArgumentNullException("reader"); + if (reader.ReadState == ReadState.Initial) + { + token.ThrowIfCancellationRequested(); + await reader.ReadAsync().ConfigureAwait(false); + } + + XDocument d = InitLoad(reader, options); + await d.ReadContentFromAsync(reader, options, token); + + if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile); + if (d.Root == null) throw new InvalidOperationException(SR.InvalidOperation_MissingRoot); + return d; + } + + /// + /// Performs shared initialization between Load and LoadAsync. + /// + static XDocument InitLoad(XmlReader reader, LoadOptions options) + { XDocument d = new XDocument(); if ((options & LoadOptions.SetBaseUri) != 0) { @@ -371,9 +422,6 @@ public static XDocument Load(XmlReader reader, LoadOptions options) { d.Declaration = new XDeclaration(reader); } - d.ReadContentFrom(reader, options); - if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile); - if (d.Root == null) throw new InvalidOperationException(SR.InvalidOperation_MissingRoot); return d; } diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs index f6fd30462fc8..de38428540aa 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs @@ -8,6 +8,8 @@ using IEnumerable = System.Collections.IEnumerable; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; using StringBuilder = System.Text.StringBuilder; +using System.Threading.Tasks; +using System.Threading; namespace System.Xml.Linq { @@ -677,6 +679,39 @@ public static XElement Load(XmlReader reader, LoadOptions options) return e; } + /// + /// Create a new containing the contents of the + /// passed in . + /// + /// + /// An containing the XML to be read into the new + /// . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// A new containing the contents of the passed + /// in . + /// + public static async Task LoadAsync(XmlReader reader, LoadOptions options, CancellationToken token) + { + if (reader == null) throw new ArgumentNullException("reader"); + + token.ThrowIfCancellationRequested(); + if (await reader.MoveToContentAsync().ConfigureAwait(false) != XmlNodeType.Element) throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ExpectedNodeType, XmlNodeType.Element, reader.NodeType)); + + XElement e = new XElement(reader, options); + + token.ThrowIfCancellationRequested(); + await reader.MoveToContentAsync().ConfigureAwait(false); + + if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile); + return e; + } + /// /// Parses a string containing XML into an . Optionally /// whitespace can be preserved. @@ -1704,6 +1739,38 @@ internal override int GetDeepHashCode() } void ReadElementFrom(XmlReader r, LoadOptions o) + { + ReadElementFromImpl(r, o); + + if (!r.IsEmptyElement) + { + r.Read(); + ReadContentFrom(r, o); + } + + r.Read(); + } + + async Task ReadElementFromAsync(XmlReader r, LoadOptions o, CancellationToken token) + { + ReadElementFromImpl(r, o); + + if (!r.IsEmptyElement) + { + token.ThrowIfCancellationRequested(); + await r.ReadAsync().ConfigureAwait(false); + + await ReadContentFromAsync(r, o, token).ConfigureAwait(false); + } + + token.ThrowIfCancellationRequested(); + await r.ReadAsync().ConfigureAwait(false); + } + + /// + /// Shared implementation between ReadElementFrom / ReadElementFromAsync. + /// + void ReadElementFromImpl(XmlReader r, LoadOptions o) { if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); name = XNamespace.Get(r.NamespaceURI).GetName(r.LocalName); @@ -1737,12 +1804,6 @@ void ReadElementFrom(XmlReader r, LoadOptions o) } while (r.MoveToNextAttribute()); r.MoveToElement(); } - if (!r.IsEmptyElement) - { - r.Read(); - ReadContentFrom(r, o); - } - r.Read(); } internal void RemoveAttribute(XAttribute a) From ded599bf4b24972979a73ee9bbf8e131e9733b43 Mon Sep 17 00:00:00 2001 From: Cory Nelson Date: Mon, 17 Nov 2014 11:19:51 -0600 Subject: [PATCH 02/13] Construct XElement asynchronously. --- src/System.Xml.XDocument/System/Xml/Linq/XElement.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs index de38428540aa..4bc82145c27a 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs @@ -138,11 +138,19 @@ internal XElement(XmlReader r) { } + internal XElement(AsyncConstructionSentry s) + { + } + internal XElement(XmlReader r, LoadOptions o) { ReadElementFrom(r, o); } + internal struct AsyncConstructionSentry + { + } + /// /// Gets the first attribute of an element. /// @@ -703,7 +711,8 @@ public static async Task LoadAsync(XmlReader reader, LoadOptions optio token.ThrowIfCancellationRequested(); if (await reader.MoveToContentAsync().ConfigureAwait(false) != XmlNodeType.Element) throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ExpectedNodeType, XmlNodeType.Element, reader.NodeType)); - XElement e = new XElement(reader, options); + XElement e = new XElement(new AsyncConstructionSentry()); + await e.ReadElementFromAsync(reader, options, token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); await reader.MoveToContentAsync().ConfigureAwait(false); From 715267a4cb5dc5497b88cc207000676cbdee3ba8 Mon Sep 17 00:00:00 2001 From: Cory Nelson Date: Wed, 19 Nov 2014 16:00:30 -0600 Subject: [PATCH 03/13] Add string/Stream/TextReader overloads to achieve functional parity with sync methods. This is needed due to them creating XmlReaderSettings using undocumented functionality based on the given LoadOptions. --- .../System/Xml/Linq/XDocument.cs | 102 ++++++++++++++++++ .../System/Xml/Linq/XElement.cs | 99 +++++++++++++++++ 2 files changed, 201 insertions(+) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs index 33b53abb1383..7d6bc7c80c63 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs @@ -222,6 +222,42 @@ public static XDocument Load(string uri, LoadOptions options) } } + /// + /// Create a new based on the contents of the file + /// referenced by the URI parameter passed in. Optionally, whitespace can be preserved. + /// + /// + /// + /// This method uses the method to create + /// an to read the raw XML into an underlying + /// XML tree. If LoadOptions.PreserveWhitespace is enabled then + /// the property + /// is set to false. + /// + /// + /// A string representing the URI of the file to be loaded into a new . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// + /// An initialized with the contents of the file referenced + /// in the passed uri parameter. If LoadOptions.PreserveWhitespace is enabled then + /// all whitespace will be preserved. + /// + [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "Back-compat with System.Xml.")] + public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken token) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + using (XmlReader r = XmlReader.Create(uri, rs)) + { + return await LoadAsync(r, options, token).ConfigureAwait(false); + } + } + /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. @@ -269,6 +305,39 @@ public static XDocument Load(Stream stream, LoadOptions options) } } + /// + /// Create a new and initialize its underlying XML tree using + /// the passed parameter. Optionally whitespace handling + /// can be preserved. + /// + /// + /// If LoadOptions.PreserveWhitespace is enabled then + /// the underlying property + /// is set to false. + /// + /// + /// A containing the raw XML to read into the newly + /// created . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// + /// A new containing the contents of the passed in + /// . + /// + public static async Task LoadAsync(Stream stream, LoadOptions options, CancellationToken token) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + using (XmlReader r = XmlReader.Create(stream, rs)) + { + return await LoadAsync(r, options, token).ConfigureAwait(false); + } + } + /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. @@ -316,6 +385,39 @@ public static XDocument Load(TextReader textReader, LoadOptions options) } } + /// + /// Create a new and initialize its underlying XML tree using + /// the passed parameter. Optionally whitespace handling + /// can be preserved. + /// + /// + /// If LoadOptions.PreserveWhitespace is enabled then + /// the property + /// is set to false. + /// + /// + /// A containing the raw XML to read into the newly + /// created . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// + /// A new containing the contents of the passed in + /// . + /// + public static async Task LoadAsync(TextReader textReader, LoadOptions options, CancellationToken token) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + using (XmlReader r = XmlReader.Create(textReader, rs)) + { + return await LoadAsync(r, options, token).ConfigureAwait(false); + } + } + /// /// Create a new containing the contents of the /// passed in . diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs index 4bc82145c27a..11f57d01c4aa 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs @@ -552,6 +552,40 @@ public static XElement Load(string uri, LoadOptions options) } } + /// + /// Create a new based on the contents of the file + /// referenced by the URI parameter passed in. Optionally, whitespace can be preserved. + /// + /// + /// + /// + /// This method uses the method to create + /// an to read the raw XML into an underlying + /// XML tree. If LoadOptions.PreserveWhitespace is enabled then + /// the property + /// is set to false. + /// + /// + /// A string representing the URI of the file to be loaded into a new . + /// + /// + /// A set of . + /// + /// + /// An initialized with the contents of the file referenced + /// in the passed uri parameter. If LoadOptions.PreserveWhitespace is enabled then + /// significant whitespace will be preserved. + /// + [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "Back-compat with System.Xml.")] + public static async Task Load(string uri, LoadOptions options, CancellationToken token) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + using (XmlReader r = XmlReader.Create(uri, rs)) + { + return await LoadAsync(r, options, token).ConfigureAwait(false); + } + } + /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. @@ -598,6 +632,39 @@ public static XElement Load(Stream stream, LoadOptions options) return Load(r, options); } } + + /// + /// Create a new and initialize its underlying XML tree using + /// the passed parameter. Optionally whitespace handling + /// can be preserved. + /// + /// + /// If LoadOptions.PreserveWhitespace is enabled then + /// the property + /// is set to false. + /// + /// + /// A containing the raw XML to read into the newly + /// created . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// A new containing the contents of the passed in + /// . + /// + public static async Task Load(Stream stream, LoadOptions options, CancellationToken token) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + using (XmlReader r = XmlReader.Create(stream, rs)) + { + return await LoadAsync(r, options, token).ConfigureAwait(false); + } + } + /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. @@ -645,6 +712,38 @@ public static XElement Load(TextReader textReader, LoadOptions options) } } + /// + /// Create a new and initialize its underlying XML tree using + /// the passed parameter. Optionally whitespace handling + /// can be preserved. + /// + /// + /// If LoadOptions.PreserveWhitespace is enabled then + /// the property + /// is set to false. + /// + /// + /// A containing the raw XML to read into the newly + /// created . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// A new containing the contents of the passed in + /// . + /// + public static async Task LoadAsync(TextReader textReader, LoadOptions options, CancellationToken token) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + using (XmlReader r = XmlReader.Create(textReader, rs)) + { + return await LoadAsync(r, options, token).ConfigureAwait(false); + } + } + /// /// Create a new containing the contents of the /// passed in . From 035582e442994d1622bcb3664b8f1773f1ffae14 Mon Sep 17 00:00:00 2001 From: Cory Nelson Date: Fri, 19 Dec 2014 09:58:22 -0600 Subject: [PATCH 04/13] Fix naming oversight (Load -> LoadAsync). --- src/System.Xml.XDocument/System/Xml/Linq/XElement.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs index 11f57d01c4aa..917188ec96bf 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs @@ -3,13 +3,13 @@ using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using CultureInfo = System.Globalization.CultureInfo; using IEnumerable = System.Collections.IEnumerable; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; using StringBuilder = System.Text.StringBuilder; -using System.Threading.Tasks; -using System.Threading; namespace System.Xml.Linq { @@ -577,7 +577,7 @@ public static XElement Load(string uri, LoadOptions options) /// significant whitespace will be preserved. /// [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "Back-compat with System.Xml.")] - public static async Task Load(string uri, LoadOptions options, CancellationToken token) + public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken token) { XmlReaderSettings rs = GetXmlReaderSettings(options); using (XmlReader r = XmlReader.Create(uri, rs)) @@ -656,7 +656,7 @@ public static XElement Load(Stream stream, LoadOptions options) /// A new containing the contents of the passed in /// . /// - public static async Task Load(Stream stream, LoadOptions options, CancellationToken token) + public static async Task LoadAsync(Stream stream, LoadOptions options, CancellationToken token) { XmlReaderSettings rs = GetXmlReaderSettings(options); using (XmlReader r = XmlReader.Create(stream, rs)) From 59895507218b0213404d6aeb091321cdd220ee4e Mon Sep 17 00:00:00 2001 From: scalablecory Date: Thu, 15 Jan 2015 18:28:35 -0600 Subject: [PATCH 05/13] Use "cancellationToken" rather than "token" to follow existing convention. --- .../System/Xml/Linq/XContainer.cs | 14 ++++------ .../System/Xml/Linq/XDocument.cs | 26 ++++++++--------- .../System/Xml/Linq/XElement.cs | 28 ++++++++++--------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs index 8a395915c4c4..61bd71c6798d 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs @@ -871,29 +871,27 @@ internal void ReadContentFrom(XmlReader r, LoadOptions o) while (cr.ReadContentFrom(this, r, o) && r.Read()) ; } - internal async Task ReadContentFromAsync(XmlReader r, CancellationToken token) + internal async Task ReadContentFromAsync(XmlReader r, CancellationToken cancellationToken) { if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); ContentReader cr = new ContentReader(this); - while (!token.IsCancellationRequested && cr.ReadContentFrom(this, r) && await r.ReadAsync().ConfigureAwait(false)) ; - - token.ThrowIfCancellationRequested(); + while (!cancellationToken.IsCancellationRequested && cr.ReadContentFrom(this, r) && await r.ReadAsync().ConfigureAwait(false)) ; } - internal async Task ReadContentFromAsync(XmlReader r, LoadOptions o, CancellationToken token) + internal async Task ReadContentFromAsync(XmlReader r, LoadOptions o, CancellationToken cancellationToken) { if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == 0) { - await ReadContentFromAsync(r, token).ConfigureAwait(false); + await ReadContentFromAsync(r, cancellationToken).ConfigureAwait(false); return; } if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); ContentReader cr = new ContentReader(this); - while (!token.IsCancellationRequested && cr.ReadContentFrom(this, r, o) && await r.ReadAsync().ConfigureAwait(false)) ; + while (!cancellationToken.IsCancellationRequested && cr.ReadContentFrom(this, r, o) && await r.ReadAsync().ConfigureAwait(false)) ; - token.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); } /// diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs index 7d6bc7c80c63..840138f42596 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs @@ -240,7 +240,7 @@ public static XDocument Load(string uri, LoadOptions options) /// /// A set of . /// - /// + /// /// A cancellation token. /// /// @@ -249,12 +249,12 @@ public static XDocument Load(string uri, LoadOptions options) /// all whitespace will be preserved. /// [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "Back-compat with System.Xml.")] - public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken token) + public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); using (XmlReader r = XmlReader.Create(uri, rs)) { - return await LoadAsync(r, options, token).ConfigureAwait(false); + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); } } @@ -322,19 +322,19 @@ public static XDocument Load(Stream stream, LoadOptions options) /// /// A set of . /// - /// + /// /// A cancellation token. /// /// /// A new containing the contents of the passed in /// . /// - public static async Task LoadAsync(Stream stream, LoadOptions options, CancellationToken token) + public static async Task LoadAsync(Stream stream, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); using (XmlReader r = XmlReader.Create(stream, rs)) { - return await LoadAsync(r, options, token).ConfigureAwait(false); + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); } } @@ -402,19 +402,19 @@ public static XDocument Load(TextReader textReader, LoadOptions options) /// /// A set of . /// - /// + /// /// A cancellation token. /// /// /// A new containing the contents of the passed in /// . /// - public static async Task LoadAsync(TextReader textReader, LoadOptions options, CancellationToken token) + public static async Task LoadAsync(TextReader textReader, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); using (XmlReader r = XmlReader.Create(textReader, rs)) { - return await LoadAsync(r, options, token).ConfigureAwait(false); + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); } } @@ -474,24 +474,24 @@ public static XDocument Load(XmlReader reader, LoadOptions options) /// /// A set of . /// - /// + /// /// A cancellation token. /// /// /// A new containing the contents of the passed /// in . /// - public static async Task LoadAsync(XmlReader reader, LoadOptions options, CancellationToken token) + public static async Task LoadAsync(XmlReader reader, LoadOptions options, CancellationToken cancellationToken) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.ReadState == ReadState.Initial) { - token.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); await reader.ReadAsync().ConfigureAwait(false); } XDocument d = InitLoad(reader, options); - await d.ReadContentFromAsync(reader, options, token); + await d.ReadContentFromAsync(reader, options, cancellationToken); if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile); if (d.Root == null) throw new InvalidOperationException(SR.InvalidOperation_MissingRoot); diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs index 917188ec96bf..3d1bc233f8d8 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs @@ -571,18 +571,20 @@ public static XElement Load(string uri, LoadOptions options) /// /// A set of . /// + /// + /// A cancellation token. /// /// An initialized with the contents of the file referenced /// in the passed uri parameter. If LoadOptions.PreserveWhitespace is enabled then /// significant whitespace will be preserved. /// [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "Back-compat with System.Xml.")] - public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken token) + public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); using (XmlReader r = XmlReader.Create(uri, rs)) { - return await LoadAsync(r, options, token).ConfigureAwait(false); + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); } } @@ -650,18 +652,18 @@ public static XElement Load(Stream stream, LoadOptions options) /// /// A set of . /// - /// + /// /// A cancellation token. /// /// A new containing the contents of the passed in /// . /// - public static async Task LoadAsync(Stream stream, LoadOptions options, CancellationToken token) + public static async Task LoadAsync(Stream stream, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); using (XmlReader r = XmlReader.Create(stream, rs)) { - return await LoadAsync(r, options, token).ConfigureAwait(false); + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); } } @@ -729,18 +731,18 @@ public static XElement Load(TextReader textReader, LoadOptions options) /// /// A set of . /// - /// + /// /// A cancellation token. /// /// A new containing the contents of the passed in /// . /// - public static async Task LoadAsync(TextReader textReader, LoadOptions options, CancellationToken token) + public static async Task LoadAsync(TextReader textReader, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); using (XmlReader r = XmlReader.Create(textReader, rs)) { - return await LoadAsync(r, options, token).ConfigureAwait(false); + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); } } @@ -797,23 +799,23 @@ public static XElement Load(XmlReader reader, LoadOptions options) /// /// A set of . /// - /// + /// /// A cancellation token. /// /// A new containing the contents of the passed /// in . /// - public static async Task LoadAsync(XmlReader reader, LoadOptions options, CancellationToken token) + public static async Task LoadAsync(XmlReader reader, LoadOptions options, CancellationToken cancellationToken) { if (reader == null) throw new ArgumentNullException("reader"); - token.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (await reader.MoveToContentAsync().ConfigureAwait(false) != XmlNodeType.Element) throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ExpectedNodeType, XmlNodeType.Element, reader.NodeType)); XElement e = new XElement(new AsyncConstructionSentry()); - await e.ReadElementFromAsync(reader, options, token).ConfigureAwait(false); + await e.ReadElementFromAsync(reader, options, cancellationToken).ConfigureAwait(false); - token.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); await reader.MoveToContentAsync().ConfigureAwait(false); if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile); From 987761ecb2d3c64f828d73ee45e9871d112ce744 Mon Sep 17 00:00:00 2001 From: scalablecory Date: Thu, 15 Jan 2015 19:38:01 -0600 Subject: [PATCH 06/13] Add SaveAsync() and WriteToAsync() methods to XElement, XDocument, XNode. --- .../System/Xml/Linq/XComment.cs | 17 +++ .../System/Xml/Linq/XContainer.cs | 35 ++++++ .../System/Xml/Linq/XDocument.cs | 115 ++++++++++++++++++ .../System/Xml/Linq/XDocumentType.cs | 19 +++ .../System/Xml/Linq/XElement.cs | 96 ++++++++++++++- .../System/Xml/Linq/XLinq.cs | 82 ++++++++++++- .../System/Xml/Linq/XNode.cs | 9 ++ .../System/Xml/Linq/XProcessingInstruction.cs | 17 +++ .../System/Xml/Linq/XText.cs | 31 +++++ 9 files changed, 416 insertions(+), 5 deletions(-) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XComment.cs b/src/System.Xml.XDocument/System/Xml/Linq/XComment.cs index 2e8fd4c341d8..f13c4056b8f2 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XComment.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XComment.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Threading; +using System.Threading.Tasks; namespace System.Xml.Linq { /// @@ -92,6 +94,21 @@ public override void WriteTo(XmlWriter writer) writer.WriteComment(value); } + /// + /// Write this to the passed in . + /// + /// + /// The to write this to. + /// + /// A cancellation token. + public override async Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) throw new ArgumentNullException("writer"); + + cancellationToken.ThrowIfCancellationRequested(); + await writer.WriteCommentAsync(value).ConfigureAwait(false); + } + internal override XNode CloneNode() { return new XComment(this); diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs index 61bd71c6798d..34b3682fdaaa 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs @@ -1163,6 +1163,41 @@ internal void WriteContentTo(XmlWriter writer) } } + internal async Task WriteContentToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (content != null) + { + string sContent = content as string; + + if (sContent != null) + { + cancellationToken.ThrowIfCancellationRequested(); + + Task tWrite; + + if (this is XDocument) + { + tWrite = writer.WriteWhitespaceAsync(sContent); + } + else + { + tWrite = writer.WriteStringAsync(sContent); + } + + await tWrite.ConfigureAwait(false); + } + else + { + XNode n = (XNode)content; + do + { + n = n.next; + await n.WriteToAsync(writer, cancellationToken).ConfigureAwait(false); + } while (n != content); + } + } + } + static void AddContentToList(List list, object content) { IEnumerable e = content is string ? null : content as IEnumerable; diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs index 840138f42596..22504844f068 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs @@ -252,6 +252,9 @@ public static XDocument Load(string uri, LoadOptions options) public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + using (XmlReader r = XmlReader.Create(uri, rs)) { return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); @@ -332,6 +335,9 @@ public static XDocument Load(Stream stream, LoadOptions options) public static async Task LoadAsync(Stream stream, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + using (XmlReader r = XmlReader.Create(stream, rs)) { return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); @@ -412,6 +418,9 @@ public static XDocument Load(TextReader textReader, LoadOptions options) public static async Task LoadAsync(TextReader textReader, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + using (XmlReader r = XmlReader.Create(textReader, rs)) { return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); @@ -628,6 +637,40 @@ public void Save(Stream stream, SaveOptions options) } } + /// + /// Output this to a . + /// + /// + /// The to output the XML to. + /// + /// + /// If SaveOptions.DisableFormatting is enabled the output is not indented. + /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed. + /// + /// A cancellation token. + public async Task SaveAsync(Stream stream, SaveOptions options, CancellationToken cancellationToken) + { + XmlWriterSettings ws = GetXmlWriterSettings(options); + + ws.Async = true; + + if (declaration != null && !string.IsNullOrEmpty(declaration.Encoding)) + { + try + { + ws.Encoding = Encoding.GetEncoding(declaration.Encoding); + } + catch (ArgumentException) + { + } + } + + using (XmlWriter w = XmlWriter.Create(stream, ws)) + { + await WriteToAsync(w, cancellationToken).ConfigureAwait(false); + } + } + /// /// Output this to the passed in . /// @@ -666,6 +709,29 @@ public void Save(TextWriter textWriter, SaveOptions options) } } + /// + /// Output this to a . + /// + /// + /// The to output the XML to. + /// + /// + /// If SaveOptions.DisableFormatting is enabled the output is not indented. + /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed. + /// + /// A cancellation token. + public async Task SaveAsync(TextWriter textWriter, SaveOptions options, CancellationToken cancellationToken) + { + XmlWriterSettings ws = GetXmlWriterSettings(options); + + ws.Async = true; + + using (XmlWriter w = XmlWriter.Create(textWriter, ws)) + { + await WriteToAsync(w, cancellationToken).ConfigureAwait(false); + } + } + /// /// Output this to an . /// @@ -677,6 +743,19 @@ public void Save(XmlWriter writer) WriteTo(writer); } + /// + /// Output this to an . + /// + /// + /// The to output the XML to. + /// + /// + /// A cancellation token. + /// + public async Task SaveAsync(XmlWriter writer, CancellationToken cancellationToken) + { + await WriteToAsync(writer, cancellationToken).ConfigureAwait(false); + } /// /// Output this 's underlying XML tree to the @@ -706,6 +785,42 @@ public override void WriteTo(XmlWriter writer) writer.WriteEndDocument(); } + /// + /// Output this 's underlying XML tree to the + /// passed in . + /// + /// + /// + /// The to output the content of this + /// . + /// + /// A cancellation token. + public override async Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) throw new ArgumentNullException("writer"); + + cancellationToken.ThrowIfCancellationRequested(); + + Task tStart; + + if (declaration != null && declaration.Standalone == "yes") + { + tStart = writer.WriteStartDocumentAsync(true); + } + else if (declaration != null && declaration.Standalone == "no") + { + tStart = writer.WriteStartDocumentAsync(false); + } + else + { + tStart = writer.WriteStartDocumentAsync(); + } + + await tStart.ConfigureAwait(false); + await WriteContentToAsync(writer, cancellationToken).ConfigureAwait(false); + await writer.WriteEndDocumentAsync().ConfigureAwait(false); + } + internal override void AddAttribute(XAttribute a) { throw new ArgumentException(SR.Argument_AddAttribute); diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XDocumentType.cs b/src/System.Xml.XDocument/System/Xml/Linq/XDocumentType.cs index 3ccbd36a2842..2378e5789e42 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XDocumentType.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XDocumentType.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Threading; +using System.Threading.Tasks; namespace System.Xml.Linq { /// @@ -142,6 +144,23 @@ public override void WriteTo(XmlWriter writer) writer.WriteDocType(name, publicId, systemId, internalSubset); } + /// + /// Write this to the passed in . + /// + /// + /// The to write this to. + /// + /// + /// A cancellation token. + /// + public override async Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) throw new ArgumentNullException("writer"); + + cancellationToken.ThrowIfCancellationRequested(); + await writer.WriteDocTypeAsync(name, publicId, systemId, internalSubset).ConfigureAwait(false); + } + internal override XNode CloneNode() { return new XDocumentType(this); diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs index 3d1bc233f8d8..15ee909ce9a8 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs @@ -582,6 +582,9 @@ public static XElement Load(string uri, LoadOptions options) public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + using (XmlReader r = XmlReader.Create(uri, rs)) { return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); @@ -661,6 +664,9 @@ public static XElement Load(Stream stream, LoadOptions options) public static async Task LoadAsync(Stream stream, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + using (XmlReader r = XmlReader.Create(stream, rs)) { return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); @@ -740,6 +746,9 @@ public static XElement Load(TextReader textReader, LoadOptions options) public static async Task LoadAsync(TextReader textReader, LoadOptions options, CancellationToken cancellationToken) { XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + using (XmlReader r = XmlReader.Create(textReader, rs)) { return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); @@ -1033,6 +1042,29 @@ public void Save(Stream stream, SaveOptions options) } } + /// + /// Output this to a . + /// + /// + /// The to output the XML to. + /// + /// + /// If SaveOptions.DisableFormatting is enabled the output is not indented. + /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed. + /// + /// A cancellation token. + public async Task SaveAsync(Stream stream, SaveOptions options, CancellationToken cancellationToken) + { + XmlWriterSettings ws = GetXmlWriterSettings(options); + + ws.Async = true; + + using (XmlWriter w = XmlWriter.Create(stream, ws)) + { + await SaveAsync(w, cancellationToken).ConfigureAwait(false); + } + } + /// /// Output this to the passed in . /// @@ -1071,6 +1103,29 @@ public void Save(TextWriter textWriter, SaveOptions options) } } + /// + /// Output this to a . + /// + /// + /// The to output the XML to. + /// + /// + /// If SaveOptions.DisableFormatting is enabled the output is not indented. + /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed. + /// + /// A cancellation token. + public async Task SaveAsync(TextWriter textWriter, SaveOptions options, CancellationToken cancellationToken) + { + XmlWriterSettings ws = GetXmlWriterSettings(options); + + ws.Async = true; + + using (XmlWriter w = XmlWriter.Create(textWriter, ws)) + { + await SaveAsync(w, cancellationToken).ConfigureAwait(false); + } + } + /// /// Output this to an . /// @@ -1085,6 +1140,26 @@ public void Save(XmlWriter writer) writer.WriteEndDocument(); } + /// + /// Output this to an . + /// + /// + /// The to output the XML to. + /// + /// A cancellation token. + public async Task SaveAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) throw new ArgumentNullException("writer"); + + cancellationToken.ThrowIfCancellationRequested(); + await writer.WriteStartDocumentAsync().ConfigureAwait(false); + + await WriteToAsync(writer, cancellationToken).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + await writer.WriteEndDocumentAsync().ConfigureAwait(false); + } + /// /// Sets the value of an attribute. The value is assigned to the attribute with the given /// name. If no attribute with the given name exists, a new attribute is added. If the @@ -1195,6 +1270,19 @@ public override void WriteTo(XmlWriter writer) new ElementWriter(writer).WriteElement(this); } + /// + /// Write this to the passed in . + /// + /// + /// The to write this to. + /// + /// A cancellation token. + public override async Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) throw new ArgumentNullException("writer"); + await new ElementWriter(writer).WriteElementAsync(this, cancellationToken).ConfigureAwait(false); + } + /// /// Cast the value of this to a . /// @@ -1861,19 +1949,19 @@ void ReadElementFrom(XmlReader r, LoadOptions o) r.Read(); } - async Task ReadElementFromAsync(XmlReader r, LoadOptions o, CancellationToken token) + async Task ReadElementFromAsync(XmlReader r, LoadOptions o, CancellationToken cancellationTokentoken) { ReadElementFromImpl(r, o); if (!r.IsEmptyElement) { - token.ThrowIfCancellationRequested(); + cancellationTokentoken.ThrowIfCancellationRequested(); await r.ReadAsync().ConfigureAwait(false); - await ReadContentFromAsync(r, o, token).ConfigureAwait(false); + await ReadContentFromAsync(r, o, cancellationTokentoken).ConfigureAwait(false); } - token.ThrowIfCancellationRequested(); + cancellationTokentoken.ThrowIfCancellationRequested(); await r.ReadAsync().ConfigureAwait(false); } diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XLinq.cs b/src/System.Xml.XDocument/System/Xml/Linq/XLinq.cs index 4f5adc42b55e..db99cc260efe 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XLinq.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XLinq.cs @@ -2,7 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; - +using System.Threading; +using System.Threading.Tasks; using IEnumerable = System.Collections.IEnumerable; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; @@ -243,6 +244,52 @@ public void WriteElement(XElement e) } } + public async Task WriteElementAsync(XElement e, CancellationToken cancellationToken) + { + PushAncestors(e); + XElement root = e; + XNode n = e; + while (true) + { + e = n as XElement; + if (e != null) + { + await WriteStartElementAsync(e, cancellationToken).ConfigureAwait(false); + if (e.content == null) + { + await WriteEndElementAsync(cancellationToken).ConfigureAwait(false); + } + else + { + string s = e.content as string; + if (s != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await writer.WriteStringAsync(s).ConfigureAwait(false); + + await WriteFullEndElementAsync(cancellationToken).ConfigureAwait(false); + } + else + { + n = ((XNode)e.content).next; + continue; + } + } + } + else + { + await n.WriteToAsync(writer, cancellationToken).ConfigureAwait(false); + } + while (n != root && n == n.parent.content) + { + n = n.parent; + await WriteFullEndElementAsync(cancellationToken).ConfigureAwait(false); + } + if (n == root) break; + n = n.next; + } + } + string GetPrefixOfNamespace(XNamespace ns, bool allowDefaultNamespace) { string namespaceName = ns.NamespaceName; @@ -298,12 +345,26 @@ void WriteEndElement() resolver.PopScope(); } + Task WriteEndElementAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + resolver.PopScope(); + return writer.WriteEndElementAsync(); + } + void WriteFullEndElement() { writer.WriteFullEndElement(); resolver.PopScope(); } + Task WriteFullEndElementAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + resolver.PopScope(); + return writer.WriteFullEndElementAsync(); + } + void WriteStartElement(XElement e) { PushElement(e); @@ -322,6 +383,25 @@ void WriteStartElement(XElement e) } while (a != e.lastAttr); } } + + async Task WriteStartElementAsync(XElement e, CancellationToken cancellationToken) + { + PushElement(e); + XNamespace ns = e.Name.Namespace; + await writer.WriteStartElementAsync(GetPrefixOfNamespace(ns, true), e.Name.LocalName, ns.NamespaceName).ConfigureAwait(false); + XAttribute a = e.lastAttr; + if (a != null) + { + do + { + a = a.next; + ns = a.Name.Namespace; + string localName = a.Name.LocalName; + string namespaceName = ns.NamespaceName; + await writer.WriteAttributeStringAsync(GetPrefixOfNamespace(ns, false), localName, namespaceName.Length == 0 && localName == "xmlns" ? XNamespace.xmlnsPrefixNamespace : namespaceName, a.Value).ConfigureAwait(false); + } while (a != e.lastAttr); + } + } } internal struct NamespaceResolver diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XNode.cs b/src/System.Xml.XDocument/System/Xml/Linq/XNode.cs index fba43e37c3de..f6d969f36b4c 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XNode.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XNode.cs @@ -7,6 +7,8 @@ using CultureInfo = System.Globalization.CultureInfo; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; using StringBuilder = System.Text.StringBuilder; +using System.Threading; +using System.Threading.Tasks; namespace System.Xml.Linq { @@ -558,6 +560,13 @@ public static bool DeepEquals(XNode n1, XNode n2) /// The to write the current node into. public abstract void WriteTo(XmlWriter writer); + /// + /// Write the current node to an . + /// + /// The to write the current node into. + /// A cancellation token. + public abstract Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken); + internal virtual void AppendText(StringBuilder sb) { } diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XProcessingInstruction.cs b/src/System.Xml.XDocument/System/Xml/Linq/XProcessingInstruction.cs index 1791e1a3952e..6bb0799ae980 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XProcessingInstruction.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XProcessingInstruction.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Threading; +using System.Threading.Tasks; namespace System.Xml.Linq { /// @@ -118,6 +120,21 @@ public override void WriteTo(XmlWriter writer) writer.WriteProcessingInstruction(target, data); } + /// + /// Writes this to the passed in . + /// + /// + /// The to write this to. + /// + /// A cancellation token. + public override async Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) throw new ArgumentNullException("writer"); + + cancellationToken.ThrowIfCancellationRequested(); + await writer.WriteProcessingInstructionAsync(target, data).ConfigureAwait(false); + } + internal override XNode CloneNode() { return new XProcessingInstruction(this); diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XText.cs b/src/System.Xml.XDocument/System/Xml/Linq/XText.cs index d04ceb0c6f64..b5d17c711dc5 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XText.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XText.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Threading; +using System.Threading.Tasks; using StringBuilder = System.Text.StringBuilder; namespace System.Xml.Linq @@ -89,6 +91,35 @@ public override void WriteTo(XmlWriter writer) } } + /// + /// Write this to the given . + /// + /// + /// The to write this to. + /// + /// + /// A cancellation token. + /// + public override async Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) throw new ArgumentNullException("writer"); + + cancellationToken.ThrowIfCancellationRequested(); + + Task t; + + if (parent is XDocument) + { + t = writer.WriteWhitespaceAsync(text); + } + else + { + t = writer.WriteStringAsync(text); + } + + await t.ConfigureAwait(false); + } + internal override void AppendText(StringBuilder sb) { sb.Append(text); From b4c3d551fdbcd6ca8c0423a913489c8803e0913f Mon Sep 17 00:00:00 2001 From: scalablecory Date: Sun, 18 Jan 2015 22:58:37 -0600 Subject: [PATCH 07/13] Add XNode.ReadFromAsync. --- .../System/Xml/Linq/XElement.cs | 11 +++- .../System/Xml/Linq/XNode.cs | 58 +++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs index 15ee909ce9a8..8a9c39097771 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs @@ -138,7 +138,7 @@ internal XElement(XmlReader r) { } - internal XElement(AsyncConstructionSentry s) + XElement(AsyncConstructionSentry s) { } @@ -147,7 +147,14 @@ internal XElement(XmlReader r, LoadOptions o) ReadElementFrom(r, o); } - internal struct AsyncConstructionSentry + internal static async Task CreateAsync(XmlReader r, CancellationToken cancellationToken) + { + XElement xe = new XElement(new AsyncConstructionSentry()); + await xe.ReadElementFromAsync(r, LoadOptions.None, cancellationToken).ConfigureAwait(false); + return xe; + } + + struct AsyncConstructionSentry { } diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XNode.cs b/src/System.Xml.XDocument/System/Xml/Linq/XNode.cs index f6d969f36b4c..64acb8798a4b 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XNode.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XNode.cs @@ -462,6 +462,64 @@ public static XNode ReadFrom(XmlReader reader) } } + /// + /// Creates an from an . + /// The runtime type of the node is determined by the node type + /// () of the first node encountered + /// in the reader. + /// + /// An positioned at the node to read into this . + /// A cancellation token. + /// An that contains the nodes read from the reader. + /// + /// Thrown if the is not positioned on a recognized node type. + /// + public static async Task ReadFromAsync(XmlReader reader, CancellationToken cancellationToken) + { + if (reader == null) throw new ArgumentNullException("reader"); + if (reader.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + + XNode ret; + + switch (reader.NodeType) + { + case XmlNodeType.Text: + case XmlNodeType.SignificantWhitespace: + case XmlNodeType.Whitespace: + ret = new XText(reader.Value); + break; + case XmlNodeType.CDATA: + ret = new XCData(reader.Value); + break; + case XmlNodeType.Comment: + ret = new XComment(reader.Value); + break; + case XmlNodeType.DocumentType: + var name = reader.Name; + var publicId = reader.GetAttribute("PUBLIC"); + var systemId = reader.GetAttribute("SYSTEM"); + var internalSubset = reader.Value; + + ret = new XDocumentType(name, publicId, systemId, internalSubset); + break; + case XmlNodeType.Element: + return await XElement.CreateAsync(reader, cancellationToken).ConfigureAwait(false); + case XmlNodeType.ProcessingInstruction: + var target = reader.Name; + var data = reader.Value; + + ret = new XProcessingInstruction(target, data); + break; + default: + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnexpectedNodeType, reader.NodeType)); + } + + cancellationToken.ThrowIfCancellationRequested(); + await reader.ReadAsync().ConfigureAwait(false); + + return ret; + } + /// /// Removes this XNode from the underlying XML tree. /// From c7ed7ddfd637d1bb9a2ba7fc4a7f03d6bcc159f0 Mon Sep 17 00:00:00 2001 From: scalablecory Date: Sun, 18 Jan 2015 23:04:56 -0600 Subject: [PATCH 08/13] Remove LoadAsync overloads taking a Uri. --- .../System/Xml/Linq/XDocument.cs | 39 ------------------- .../System/Xml/Linq/XElement.cs | 39 ------------------- 2 files changed, 78 deletions(-) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs index 22504844f068..9ffa61734d2d 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs @@ -222,45 +222,6 @@ public static XDocument Load(string uri, LoadOptions options) } } - /// - /// Create a new based on the contents of the file - /// referenced by the URI parameter passed in. Optionally, whitespace can be preserved. - /// - /// - /// - /// This method uses the method to create - /// an to read the raw XML into an underlying - /// XML tree. If LoadOptions.PreserveWhitespace is enabled then - /// the property - /// is set to false. - /// - /// - /// A string representing the URI of the file to be loaded into a new . - /// - /// - /// A set of . - /// - /// - /// A cancellation token. - /// - /// - /// An initialized with the contents of the file referenced - /// in the passed uri parameter. If LoadOptions.PreserveWhitespace is enabled then - /// all whitespace will be preserved. - /// - [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "Back-compat with System.Xml.")] - public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken cancellationToken) - { - XmlReaderSettings rs = GetXmlReaderSettings(options); - - rs.Async = true; - - using (XmlReader r = XmlReader.Create(uri, rs)) - { - return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); - } - } - /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs index 8a9c39097771..dfe74f798e8e 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs @@ -559,45 +559,6 @@ public static XElement Load(string uri, LoadOptions options) } } - /// - /// Create a new based on the contents of the file - /// referenced by the URI parameter passed in. Optionally, whitespace can be preserved. - /// - /// - /// - /// - /// This method uses the method to create - /// an to read the raw XML into an underlying - /// XML tree. If LoadOptions.PreserveWhitespace is enabled then - /// the property - /// is set to false. - /// - /// - /// A string representing the URI of the file to be loaded into a new . - /// - /// - /// A set of . - /// - /// - /// A cancellation token. - /// - /// An initialized with the contents of the file referenced - /// in the passed uri parameter. If LoadOptions.PreserveWhitespace is enabled then - /// significant whitespace will be preserved. - /// - [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "Back-compat with System.Xml.")] - public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken cancellationToken) - { - XmlReaderSettings rs = GetXmlReaderSettings(options); - - rs.Async = true; - - using (XmlReader r = XmlReader.Create(uri, rs)) - { - return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); - } - } - /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. From e7b728884aba703fd97ba792a73e0ee0a0577905 Mon Sep 17 00:00:00 2001 From: Cory Nelson Date: Wed, 21 Jan 2015 10:32:39 -0600 Subject: [PATCH 09/13] Resolve corner case that could cancel ReadContentFromAsync after the work has been completed. --- .../System/Xml/Linq/XContainer.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs index 34b3682fdaaa..ba51615310a1 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs @@ -876,7 +876,15 @@ internal async Task ReadContentFromAsync(XmlReader r, CancellationToken cancella if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); ContentReader cr = new ContentReader(this); - while (!cancellationToken.IsCancellationRequested && cr.ReadContentFrom(this, r) && await r.ReadAsync().ConfigureAwait(false)) ; + while (true) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!cr.ReadContentFrom(this, r) || !await r.ReadAsync().ConfigureAwait(false)) + { + break; + } + } } internal async Task ReadContentFromAsync(XmlReader r, LoadOptions o, CancellationToken cancellationToken) @@ -889,9 +897,15 @@ internal async Task ReadContentFromAsync(XmlReader r, LoadOptions o, Cancellatio if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); ContentReader cr = new ContentReader(this); - while (!cancellationToken.IsCancellationRequested && cr.ReadContentFrom(this, r, o) && await r.ReadAsync().ConfigureAwait(false)) ; + while (true) + { + cancellationToken.ThrowIfCancellationRequested(); - cancellationToken.ThrowIfCancellationRequested(); + if (!cr.ReadContentFrom(this, r, o) || !await r.ReadAsync().ConfigureAwait(false)) + { + break; + } + } } /// From 8aee397b5c275a3da1f6bbd9c3a2aeb1228f21cb Mon Sep 17 00:00:00 2001 From: Cory Nelson Date: Wed, 21 Jan 2015 10:48:55 -0600 Subject: [PATCH 10/13] Remove URI-sourced LoadAsync overloads per review request. --- .../System/Xml/Linq/XDocument.cs | 39 ------------------- .../System/Xml/Linq/XElement.cs | 39 ------------------- 2 files changed, 78 deletions(-) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs index 22504844f068..9ffa61734d2d 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs @@ -222,45 +222,6 @@ public static XDocument Load(string uri, LoadOptions options) } } - /// - /// Create a new based on the contents of the file - /// referenced by the URI parameter passed in. Optionally, whitespace can be preserved. - /// - /// - /// - /// This method uses the method to create - /// an to read the raw XML into an underlying - /// XML tree. If LoadOptions.PreserveWhitespace is enabled then - /// the property - /// is set to false. - /// - /// - /// A string representing the URI of the file to be loaded into a new . - /// - /// - /// A set of . - /// - /// - /// A cancellation token. - /// - /// - /// An initialized with the contents of the file referenced - /// in the passed uri parameter. If LoadOptions.PreserveWhitespace is enabled then - /// all whitespace will be preserved. - /// - [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "Back-compat with System.Xml.")] - public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken cancellationToken) - { - XmlReaderSettings rs = GetXmlReaderSettings(options); - - rs.Async = true; - - using (XmlReader r = XmlReader.Create(uri, rs)) - { - return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); - } - } - /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs index 8a9c39097771..dfe74f798e8e 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs @@ -559,45 +559,6 @@ public static XElement Load(string uri, LoadOptions options) } } - /// - /// Create a new based on the contents of the file - /// referenced by the URI parameter passed in. Optionally, whitespace can be preserved. - /// - /// - /// - /// - /// This method uses the method to create - /// an to read the raw XML into an underlying - /// XML tree. If LoadOptions.PreserveWhitespace is enabled then - /// the property - /// is set to false. - /// - /// - /// A string representing the URI of the file to be loaded into a new . - /// - /// - /// A set of . - /// - /// - /// A cancellation token. - /// - /// An initialized with the contents of the file referenced - /// in the passed uri parameter. If LoadOptions.PreserveWhitespace is enabled then - /// significant whitespace will be preserved. - /// - [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "Back-compat with System.Xml.")] - public static async Task LoadAsync(string uri, LoadOptions options, CancellationToken cancellationToken) - { - XmlReaderSettings rs = GetXmlReaderSettings(options); - - rs.Async = true; - - using (XmlReader r = XmlReader.Create(uri, rs)) - { - return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); - } - } - /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. From 97a985d4ded0adb68b12c46ae854fa8d3cdf1fa9 Mon Sep 17 00:00:00 2001 From: scalablecory Date: Sat, 24 Jan 2015 22:34:59 -0600 Subject: [PATCH 11/13] Group up using statements. --- src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs | 4 ++-- src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs index ba51615310a1..39bda62f9b25 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs @@ -2,13 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Debug = System.Diagnostics.Debug; using IEnumerable = System.Collections.IEnumerable; using StringBuilder = System.Text.StringBuilder; using Interlocked = System.Threading.Interlocked; -using System.Threading.Tasks; -using System.Threading; namespace System.Xml.Linq { diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs index 9ffa61734d2d..07697f113eef 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs @@ -2,11 +2,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.IO; +using System.Threading; +using System.Threading.Tasks; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; using Encoding = System.Text.Encoding; -using System.Threading; -using System.Threading.Tasks; namespace System.Xml.Linq { From 4e95482b0c21d4c3d02320634dd23524eb66f1f6 Mon Sep 17 00:00:00 2001 From: scalablecory Date: Sat, 24 Jan 2015 22:37:03 -0600 Subject: [PATCH 12/13] remove UTF-8 BOM. (Why did VS add this in the first place?) --- .../System.Xml.XDocument.csproj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/System.Xml.XDocument/System.Xml.XDocument.csproj b/src/System.Xml.XDocument/System.Xml.XDocument.csproj index e15da69484c3..654b5ed171fd 100644 --- a/src/System.Xml.XDocument/System.Xml.XDocument.csproj +++ b/src/System.Xml.XDocument/System.Xml.XDocument.csproj @@ -1,13 +1,13 @@ - + - - + + - + --> + <_WindowsKitBinPath>$(MSBuildProgramFiles32)\Windows Kits\8.1\bin\x86 <_WindowsPhoneKitBinPath>$(MSBuildProgramFiles32)\Windows Phone Kits\8.1\bin $(_WindowsKitBinPath)\makepri.exe @@ -16,8 +16,8 @@ $(_WindowsPhoneKitBinPath)\x86\MrmEnvironmentExtDl.dll $(_WindowsPhoneKitBinPath)\x64\MrmEnvironmentExtDl.dll - - + + Debug AnyCPU From eddcfc8a51856804ea03aab737a710210a62968d Mon Sep 17 00:00:00 2001 From: Cory Nelson Date: Tue, 27 Jan 2015 13:06:25 -0600 Subject: [PATCH 13/13] Give class fields more descriptive names and conform to the design guidelines. --- .../System/Xml/Linq/XContainer.cs | 132 +++++++++--------- 1 file changed, 68 insertions(+), 64 deletions(-) diff --git a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs index 39bda62f9b25..8f4655462616 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs @@ -913,69 +913,68 @@ internal async Task ReadContentFromAsync(XmlReader r, LoadOptions o, Cancellatio /// sealed class ContentReader { - XContainer c; - XNode n; - NamespaceCache eCache = new NamespaceCache(); - NamespaceCache aCache = new NamespaceCache(); - string baseUri; - IXmlLineInfo li; + XContainer _currentContainer; + NamespaceCache _eCache = new NamespaceCache(); + NamespaceCache _aCache = new NamespaceCache(); + string _baseUri; + IXmlLineInfo _lineInfo; - public ContentReader(XContainer c) + public ContentReader(XContainer rootContainer) { - this.c = c; + _currentContainer = rootContainer; } - public ContentReader(XContainer @this, XmlReader r, LoadOptions o) + public ContentReader(XContainer rootContainer, XmlReader r, LoadOptions o) { - c = @this; - baseUri = (o & LoadOptions.SetBaseUri) != 0 ? r.BaseURI : null; - li = (o & LoadOptions.SetLineInfo) != 0 ? r as IXmlLineInfo : null; + _currentContainer = rootContainer; + _baseUri = (o & LoadOptions.SetBaseUri) != 0 ? r.BaseURI : null; + _lineInfo = (o & LoadOptions.SetLineInfo) != 0 ? r as IXmlLineInfo : null; } - public bool ReadContentFrom(XContainer @this, XmlReader r) + public bool ReadContentFrom(XContainer rootContainer, XmlReader r) { switch (r.NodeType) { case XmlNodeType.Element: - XElement e = new XElement(eCache.Get(r.NamespaceURI).GetName(r.LocalName)); + XElement e = new XElement(_eCache.Get(r.NamespaceURI).GetName(r.LocalName)); if (r.MoveToFirstAttribute()) { do { - e.AppendAttributeSkipNotify(new XAttribute(aCache.Get(r.Prefix.Length == 0 ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value)); + e.AppendAttributeSkipNotify(new XAttribute(_aCache.Get(r.Prefix.Length == 0 ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value)); } while (r.MoveToNextAttribute()); r.MoveToElement(); } - c.AddNodeSkipNotify(e); + _currentContainer.AddNodeSkipNotify(e); if (!r.IsEmptyElement) { - c = e; + _currentContainer = e; } break; case XmlNodeType.EndElement: - if (c.content == null) + if (_currentContainer.content == null) { - c.content = string.Empty; + _currentContainer.content = string.Empty; } - if (c == @this) return false; - c = c.parent; + if (_currentContainer == rootContainer) return false; + _currentContainer = _currentContainer.parent; break; case XmlNodeType.Text: case XmlNodeType.SignificantWhitespace: case XmlNodeType.Whitespace: - c.AddStringSkipNotify(r.Value); + _currentContainer.AddStringSkipNotify(r.Value); break; case XmlNodeType.CDATA: - c.AddNodeSkipNotify(new XCData(r.Value)); + _currentContainer.AddNodeSkipNotify(new XCData(r.Value)); break; case XmlNodeType.Comment: - c.AddNodeSkipNotify(new XComment(r.Value)); + _currentContainer.AddNodeSkipNotify(new XComment(r.Value)); break; case XmlNodeType.ProcessingInstruction: - c.AddNodeSkipNotify(new XProcessingInstruction(r.Name, r.Value)); + _currentContainer.AddNodeSkipNotify(new XProcessingInstruction(r.Name, r.Value)); break; case XmlNodeType.DocumentType: - c.AddNodeSkipNotify(new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value)); + _currentContainer.AddNodeSkipNotify(new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value)); break; case XmlNodeType.EntityReference: if (!r.CanResolveEntity) throw new InvalidOperationException(SR.InvalidOperation_UnresolvedEntityReference); @@ -990,92 +989,94 @@ public bool ReadContentFrom(XContainer @this, XmlReader r) return true; } - public bool ReadContentFrom(XContainer @this, XmlReader r, LoadOptions o) + public bool ReadContentFrom(XContainer rootContainer, XmlReader r, LoadOptions o) { - string uri = r.BaseURI; + XNode newNode = null; + string baseUri = r.BaseURI; + switch (r.NodeType) { case XmlNodeType.Element: { - XElement e = new XElement(eCache.Get(r.NamespaceURI).GetName(r.LocalName)); - if (baseUri != null && baseUri != uri) + XElement e = new XElement(_eCache.Get(r.NamespaceURI).GetName(r.LocalName)); + if (_baseUri != null && _baseUri != baseUri) { - e.SetBaseUri(uri); + e.SetBaseUri(baseUri); } - if (li != null && li.HasLineInfo()) + if (_lineInfo != null && _lineInfo.HasLineInfo()) { - e.SetLineInfo(li.LineNumber, li.LinePosition); + e.SetLineInfo(_lineInfo.LineNumber, _lineInfo.LinePosition); } if (r.MoveToFirstAttribute()) { do { - XAttribute a = new XAttribute(aCache.Get(r.Prefix.Length == 0 ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value); - if (li != null && li.HasLineInfo()) + XAttribute a = new XAttribute(_aCache.Get(r.Prefix.Length == 0 ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value); + if (_lineInfo != null && _lineInfo.HasLineInfo()) { - a.SetLineInfo(li.LineNumber, li.LinePosition); + a.SetLineInfo(_lineInfo.LineNumber, _lineInfo.LinePosition); } e.AppendAttributeSkipNotify(a); } while (r.MoveToNextAttribute()); r.MoveToElement(); } - c.AddNodeSkipNotify(e); + _currentContainer.AddNodeSkipNotify(e); if (!r.IsEmptyElement) { - c = e; - if (baseUri != null) + _currentContainer = e; + if (_baseUri != null) { - baseUri = uri; + _baseUri = baseUri; } } break; } case XmlNodeType.EndElement: { - if (c.content == null) + if (_currentContainer.content == null) { - c.content = string.Empty; + _currentContainer.content = string.Empty; } // Store the line info of the end element tag. // Note that since we've got EndElement the current container must be an XElement - XElement e = c as XElement; + XElement e = _currentContainer as XElement; Debug.Assert(e != null, "EndElement recieved but the current container is not an element."); - if (e != null && li != null && li.HasLineInfo()) + if (e != null && _lineInfo != null && _lineInfo.HasLineInfo()) { - e.SetEndElementLineInfo(li.LineNumber, li.LinePosition); + e.SetEndElementLineInfo(_lineInfo.LineNumber, _lineInfo.LinePosition); } - if (c == @this) return false; - if (baseUri != null && c.HasBaseUri) + if (_currentContainer == rootContainer) return false; + if (_baseUri != null && _currentContainer.HasBaseUri) { - baseUri = c.parent.BaseUri; + _baseUri = _currentContainer.parent.BaseUri; } - c = c.parent; + _currentContainer = _currentContainer.parent; break; } case XmlNodeType.Text: case XmlNodeType.SignificantWhitespace: case XmlNodeType.Whitespace: - if ((baseUri != null && baseUri != uri) || - (li != null && li.HasLineInfo())) + if ((_baseUri != null && _baseUri != baseUri) || + (_lineInfo != null && _lineInfo.HasLineInfo())) { - n = new XText(r.Value); + newNode = new XText(r.Value); } else { - c.AddStringSkipNotify(r.Value); + _currentContainer.AddStringSkipNotify(r.Value); } break; case XmlNodeType.CDATA: - n = new XCData(r.Value); + newNode = new XCData(r.Value); break; case XmlNodeType.Comment: - n = new XComment(r.Value); + newNode = new XComment(r.Value); break; case XmlNodeType.ProcessingInstruction: - n = new XProcessingInstruction(r.Name, r.Value); + newNode = new XProcessingInstruction(r.Name, r.Value); break; case XmlNodeType.DocumentType: - n = new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value); + newNode = new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value); break; case XmlNodeType.EntityReference: if (!r.CanResolveEntity) throw new InvalidOperationException(SR.InvalidOperation_UnresolvedEntityReference); @@ -1086,18 +1087,21 @@ public bool ReadContentFrom(XContainer @this, XmlReader r, LoadOptions o) default: throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnexpectedNodeType, r.NodeType)); } - if (n != null) + + if (newNode != null) { - if (baseUri != null && baseUri != uri) + if (_baseUri != null && _baseUri != baseUri) { - n.SetBaseUri(uri); + newNode.SetBaseUri(baseUri); } - if (li != null && li.HasLineInfo()) + + if (_lineInfo != null && _lineInfo.HasLineInfo()) { - n.SetLineInfo(li.LineNumber, li.LinePosition); + newNode.SetLineInfo(_lineInfo.LineNumber, _lineInfo.LinePosition); } - c.AddNodeSkipNotify(n); - n = null; + + _currentContainer.AddNodeSkipNotify(newNode); + newNode = null; } return true;