diff --git a/src/System.Xml.XDocument/System.Xml.XDocument.csproj b/src/System.Xml.XDocument/System.Xml.XDocument.csproj index 46122d3781f7..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 @@ -61,6 +61,7 @@ + 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 0e7f9974d4d7..8f4655462616 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XContainer.cs @@ -2,6 +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 Debug = System.Diagnostics.Debug; using IEnumerable = System.Collections.IEnumerable; @@ -851,53 +853,128 @@ 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; - NamespaceCache eCache = new NamespaceCache(); - NamespaceCache aCache = new NamespaceCache(); - do + + 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 cancellationToken) + { + if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + + ContentReader cr = new ContentReader(this); + 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) + { + if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == 0) + { + await ReadContentFromAsync(r, cancellationToken).ConfigureAwait(false); + return; + } + if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + + ContentReader cr = new ContentReader(this); + while (true) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!cr.ReadContentFrom(this, r, o) || !await r.ReadAsync().ConfigureAwait(false)) + { + break; + } + } + } + + /// + /// Contains the inner loop shared between ReadContentFrom / ReadContentFromAsync. + /// + sealed class ContentReader + { + XContainer _currentContainer; + NamespaceCache _eCache = new NamespaceCache(); + NamespaceCache _aCache = new NamespaceCache(); + string _baseUri; + IXmlLineInfo _lineInfo; + + public ContentReader(XContainer rootContainer) + { + _currentContainer = rootContainer; + } + + public ContentReader(XContainer rootContainer, XmlReader r, LoadOptions o) + { + _currentContainer = rootContainer; + _baseUri = (o & LoadOptions.SetBaseUri) != 0 ? r.BaseURI : null; + _lineInfo = (o & LoadOptions.SetLineInfo) != 0 ? r as IXmlLineInfo : null; + } + + 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; - 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); @@ -908,109 +985,98 @@ 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 - { - string uri = r.BaseURI; + + public bool ReadContentFrom(XContainer rootContainer, XmlReader r, LoadOptions o) + { + 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; - 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); @@ -1021,20 +1087,25 @@ internal void ReadContentFrom(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; } - } while (r.Read()); + + return true; + } } internal void RemoveNode(XNode n) @@ -1110,6 +1181,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 e54a8c0b7d10..07697f113eef 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XDocument.cs @@ -2,6 +2,8 @@ // 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; @@ -267,6 +269,42 @@ 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 cancellationToken) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + + using (XmlReader r = XmlReader.Create(stream, rs)) + { + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); + } + } + /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. @@ -314,6 +352,42 @@ 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 cancellationToken) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + + using (XmlReader r = XmlReader.Create(textReader, rs)) + { + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); + } + } + /// /// Create a new containing the contents of the /// passed in . @@ -350,6 +424,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 cancellationToken) + { + if (reader == null) throw new ArgumentNullException("reader"); + if (reader.ReadState == ReadState.Initial) + { + cancellationToken.ThrowIfCancellationRequested(); + await reader.ReadAsync().ConfigureAwait(false); + } + + XDocument d = InitLoad(reader, options); + 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); + 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 +494,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; } @@ -478,6 +598,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 . /// @@ -516,6 +670,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 . /// @@ -527,6 +704,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 @@ -556,6 +746,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 f6fd30462fc8..dfe74f798e8e 100644 --- a/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs +++ b/src/System.Xml.XDocument/System/Xml/Linq/XElement.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using CultureInfo = System.Globalization.CultureInfo; using IEnumerable = System.Collections.IEnumerable; @@ -136,11 +138,26 @@ internal XElement(XmlReader r) { } + XElement(AsyncConstructionSentry s) + { + } + internal XElement(XmlReader r, LoadOptions o) { ReadElementFrom(r, o); } + 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 + { + } + /// /// Gets the first attribute of an element. /// @@ -588,6 +605,42 @@ 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 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); + } + } + /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. @@ -635,6 +688,41 @@ 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 cancellationToken) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + + using (XmlReader r = XmlReader.Create(textReader, rs)) + { + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); + } + } + /// /// Create a new containing the contents of the /// passed in . @@ -677,6 +765,40 @@ 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 cancellationToken) + { + if (reader == null) throw new ArgumentNullException("reader"); + + 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, cancellationToken).ConfigureAwait(false); + + cancellationToken.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. @@ -888,6 +1010,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 . /// @@ -926,6 +1071,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 . /// @@ -940,6 +1108,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 @@ -1050,6 +1238,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 . /// @@ -1704,6 +1905,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 cancellationTokentoken) + { + ReadElementFromImpl(r, o); + + if (!r.IsEmptyElement) + { + cancellationTokentoken.ThrowIfCancellationRequested(); + await r.ReadAsync().ConfigureAwait(false); + + await ReadContentFromAsync(r, o, cancellationTokentoken).ConfigureAwait(false); + } + + cancellationTokentoken.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 +1970,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) 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..64acb8798a4b 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 { @@ -460,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. /// @@ -558,6 +618,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);