diff options
author | Dean Ellis <dellis1972@googlemail.com> | 2015-11-26 13:49:35 +0300 |
---|---|---|
committer | Dean Ellis <dellis1972@googlemail.com> | 2015-11-26 14:29:30 +0300 |
commit | 86522f62f00587281dbb7a51aa44d3175f33d8b1 (patch) | |
tree | 9cbb7b960d2214fc3ad4f6981d43c000f11a43f7 /mcs/class | |
parent | 8327116c331f6dd9591f482766da2e9e877fec11 (diff) |
[msbuild] Added XmlPeek
XmlPeek was missing from Mono. Adding it based on the
open source msbuild implementation with a few minor
changes because of missing support classes.
Diffstat (limited to 'mcs/class')
5 files changed, 375 insertions, 0 deletions
diff --git a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-net_4_5.csproj b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-net_4_5.csproj index dac54afb509..9918829d3ad 100644 --- a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-net_4_5.csproj +++ b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-net_4_5.csproj @@ -162,6 +162,7 @@ <Compile Include="Microsoft.Build.Tasks\Warning.cs" />
<Compile Include="Microsoft.Build.Tasks\WriteCodeFragment.cs" />
<Compile Include="Microsoft.Build.Tasks\WriteLinesToFile.cs" />
+ <Compile Include="Microsoft.Build.Tasks\XmlPeek.cs" />
<Compile Include="Mono.XBuild.Tasks.GenerateResourceInternal\PoResourceReader.cs" />
<Compile Include="Mono.XBuild.Tasks.GenerateResourceInternal\PoResourceWriter.cs" />
<Compile Include="Mono.XBuild.Tasks.GenerateResourceInternal\TxtResourceReader.cs" />
diff --git a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-net_4_x.csproj b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-net_4_x.csproj index a3479d79693..9bd62c80a9d 100644 --- a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-net_4_x.csproj +++ b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-net_4_x.csproj @@ -162,6 +162,7 @@ <Compile Include="Microsoft.Build.Tasks\Warning.cs" />
<Compile Include="Microsoft.Build.Tasks\WriteCodeFragment.cs" />
<Compile Include="Microsoft.Build.Tasks\WriteLinesToFile.cs" />
+ <Compile Include="Microsoft.Build.Tasks\XmlPeek.cs" />
<Compile Include="Mono.XBuild.Tasks.GenerateResourceInternal\PoResourceReader.cs" />
<Compile Include="Mono.XBuild.Tasks.GenerateResourceInternal\PoResourceWriter.cs" />
<Compile Include="Mono.XBuild.Tasks.GenerateResourceInternal\TxtResourceReader.cs" />
diff --git a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-xbuild_12.csproj b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-xbuild_12.csproj index 92566e79f64..642da43767b 100644 --- a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-xbuild_12.csproj +++ b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-xbuild_12.csproj @@ -159,6 +159,7 @@ <Compile Include="Microsoft.Build.Tasks\VCBuild.cs" />
<Compile Include="Microsoft.Build.Tasks\Warning.cs" />
<Compile Include="Microsoft.Build.Tasks\WriteLinesToFile.cs" />
+ <Compile Include="Microsoft.Build.Tasks\XmlPeek.cs" />
<Compile Include="Mono.XBuild.Tasks.GenerateResourceInternal\PoResourceReader.cs" />
<Compile Include="Mono.XBuild.Tasks.GenerateResourceInternal\PoResourceWriter.cs" />
<Compile Include="Mono.XBuild.Tasks.GenerateResourceInternal\TxtResourceReader.cs" />
diff --git a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks.dll.sources b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks.dll.sources index 2261d7af65e..8830df5151b 100644 --- a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks.dll.sources +++ b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks.dll.sources @@ -112,6 +112,7 @@ Microsoft.Build.Tasks/VCBuild.cs Microsoft.Build.Tasks/Warning.cs Microsoft.Build.Tasks/WriteCodeFragment.cs Microsoft.Build.Tasks/WriteLinesToFile.cs +Microsoft.Build.Tasks/XmlPeek.cs Mono.XBuild.Tasks.GenerateResourceInternal/PoResourceReader.cs Mono.XBuild.Tasks.GenerateResourceInternal/PoResourceWriter.cs Mono.XBuild.Tasks.GenerateResourceInternal/TxtResourceReader.cs diff --git a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/XmlPeek.cs b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/XmlPeek.cs new file mode 100644 index 00000000000..1e8cc21d4f3 --- /dev/null +++ b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/XmlPeek.cs @@ -0,0 +1,371 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +//----------------------------------------------------------------------- +// </copyright> +// <summary>Returns the value specified by XPath.</summary> +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security; +using System.Security.Permissions; +using System.Text; +using System.Xml; +using System.Xml.Xsl; +using System.Xml.XPath; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.Build.Tasks +{ + /// <summary> + /// A task that returns values as specified by XPath Query + /// from an XML file. + /// </summary> + public class XmlPeek : TaskExtension + { + #region Members + + /// <summary> + /// The XML input as a file path. + /// </summary> + private ITaskItem _xmlInputPath; + + /// <summary> + /// The XML input as a string. + /// </summary> + private string _xmlContent; + + /// <summary> + /// The XPath Query. + /// </summary> + private string _query; + + /// <summary> + /// The results that this task will return. + /// </summary> + private ITaskItem[] _result; + + /// <summary> + /// The namespaces for XPath query's prefixes. + /// </summary> + private string _namespaces; + + #endregion + + #region Properties + + /// <summary> + /// The XML input as a file path. + /// </summary> + public ITaskItem XmlInputPath { + get { + return _xmlInputPath; + } + + set { + _xmlInputPath = value; + } + } + + /// <summary> + /// The XML input as a string. + /// </summary> + public string XmlContent { + get { + return _xmlContent; + } + + set { + _xmlContent = value; + } + } + + /// <summary> + /// The XPath Query. + /// </summary> + public string Query { + get { + if (_query == null) + throw new ArgumentNullException ("Query"); + return _query; + } + + set { + _query = value; + } + } + + /// <summary> + /// The results returned by this task. + /// </summary> + [Output] + public ITaskItem[] Result { + get { + return _result; + } + } + + /// <summary> + /// The namespaces for XPath query's prefixes. + /// </summary> + public string Namespaces { + get { + return _namespaces; + } + + set { + _namespaces = value; + } + } + + #endregion + + + internal static bool IsCriticalException (Exception e) + { + if (e is StackOverflowException + || e is OutOfMemoryException + || e is AccessViolationException) { + return true; + } + return false; + } + + /// <summary> + /// Executes the XMLPeek task. + /// </summary> + /// <returns>true if transformation succeeds.</returns> + public override bool Execute () + { + XmlInput xmlinput; + if (_query == null) + throw new ArgumentNullException ("Query"); + + try { + xmlinput = new XmlInput (_xmlInputPath, _xmlContent); + } catch (Exception e) { + if (IsCriticalException (e)) { + throw; + } + + Log.LogErrorWithCodeFromResources ("XmlPeek.ArgumentError", e.Message); + return false; + } + + XPathDocument xpathdoc; + try { + // Load the XPath Document + using (XmlReader xr = xmlinput.CreateReader ()) { + xpathdoc = new XPathDocument (xr); + xr.Close (); + } + } catch (Exception e) { + if (IsCriticalException (e)) { + throw; + } + + Log.LogErrorWithCodeFromResources ("XmlPeekPoke.InputFileError", _xmlInputPath.ItemSpec, e.Message); + return false; + } finally { + xmlinput.CloseReader (); + } + + XPathNavigator nav = xpathdoc.CreateNavigator (); + XPathExpression expr = null; + try { + // Create the expression from query + expr = nav.Compile (_query); + } catch (Exception e) { + if (IsCriticalException (e)) { + throw; + } + + Log.LogErrorWithCodeFromResources ("XmlPeekPoke.XPathError", _query, e.Message); + return false; + } + + // Create the namespace manager and parse the input. + XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager (nav.NameTable); + + try { + LoadNamespaces (ref xmlNamespaceManager, _namespaces); + } catch (Exception e) { + if (IsCriticalException (e)) { + throw; + } + + Log.LogErrorWithCodeFromResources ("XmlPeek.NamespacesError", e.Message); + return false; + } + + try { + expr.SetContext (xmlNamespaceManager); + } catch (XPathException e) { + Log.LogErrorWithCodeFromResources ("XmlPeek.XPathContextError", e.Message); + return false; + } + + XPathNodeIterator iter = nav.Select (expr); + + List<string> peekValues = new List<string> (); + while (iter.MoveNext ()) { + if (iter.Current.NodeType == XPathNodeType.Attribute + || iter.Current.NodeType == XPathNodeType.Text) { + peekValues.Add (iter.Current.Value); + } else { + peekValues.Add (iter.Current.OuterXml); + } + } + + _result = new ITaskItem[peekValues.Count]; + int i = 0; + foreach (string item in peekValues) { + _result [i++] = new TaskItem (item); + + // This can be logged a lot, so low importance + Log.LogMessageFromResources (MessageImportance.Low, "XmlPeek.Found", item); + } + + if (_result.Length == 0) { + // Logged no more than once per execute of this task + Log.LogMessageFromResources ("XmlPeek.NotFound"); + } + + return true; + } + + /// <summary> + /// Loads the namespaces specified at Namespaces parameter to XmlNSManager. + /// </summary> + /// <param name="namespaceManager">The namespace manager to load namespaces to.</param> + /// <param name="namepaces">The namespaces as XML snippet.</param> + private void LoadNamespaces (ref XmlNamespaceManager namespaceManager, string namepaces) + { + XmlDocument doc = new XmlDocument (); + try { + XmlReaderSettings settings = new XmlReaderSettings (); + settings.DtdProcessing = DtdProcessing.Ignore; + + using (XmlReader reader = XmlReader.Create (new StringReader ("<Namespaces>" + namepaces + "</Namespaces>"), settings)) { + doc.Load (reader); + } + } catch (XmlException xe) { + throw new ArgumentException ("The specified Namespaces attribute is not a well-formed XML fragment.", xe); + } + + XmlNodeList xnl = doc.SelectNodes ("/Namespaces/*[local-name() = 'Namespace']"); + for (int i = 0; i < xnl.Count; i++) { + XmlNode xn = xnl [i]; + + if (xn.Attributes ["Prefix"] == null) { + throw new ArgumentException (string.Format ("The specified Namespaces attribute doesn't have attribute \"{0}\".", "Name")); + } + + if (xn.Attributes ["Uri"] == null) { + throw new ArgumentException (string.Format ("The specified Namespaces attribute doesn't have attribute \"{0}\".", "Uri")); + } + + namespaceManager.AddNamespace (xn.Attributes ["Prefix"].Value, xn.Attributes ["Uri"].Value); + } + } + + /// <summary> + /// This class prepares XML input from XMLInputPath and XMLContent parameters + /// </summary> + internal class XmlInput + { + /// <summary> + /// What XML input type are we at. + /// </summary> + private XmlModes _xmlMode; + + /// <summary> + /// This either contains the raw Xml or the path to Xml file. + /// </summary> + private string _data; + + /// <summary> + /// Filestream used to read XML. + /// </summary> + private FileStream _fs; + + /// <summary> + /// Constructor. + /// Only one parameter should be non null or will throw ArgumentException. + /// </summary> + /// <param name="xmlInputPath">The path to XML file or null.</param> + /// <param name="xmlContent">The raw XML.</param> + public XmlInput (ITaskItem xmlInputPath, string xmlContent) + { + if (xmlInputPath != null && xmlContent != null) { + throw new ArgumentException ("Only one of XmlContent or XmlInputPaths arguments can be set."); + } else if (xmlInputPath == null && xmlContent == null) { + throw new ArgumentException ("One of XmlContent or XmlInputPaths arguments must be set."); + } + + if (xmlInputPath != null) { + _xmlMode = XmlModes.XmlFile; + _data = xmlInputPath.ItemSpec; + } else { + _xmlMode = XmlModes.Xml; + _data = xmlContent; + } + } + + /// <summary> + /// Possible accepted types of XML input. + /// </summary> + public enum XmlModes + { + /// <summary> + /// If the mode is a XML file. + /// </summary> + XmlFile, + + /// <summary> + /// If the mode is a raw XML. + /// </summary> + Xml + } + + /// <summary> + /// Returns the current mode of the XmlInput + /// </summary> + public XmlModes XmlMode { + get { + return _xmlMode; + } + } + + /// <summary> + /// Creates correct reader based on the input type. + /// </summary> + /// <returns>The XmlReader object</returns> + public XmlReader CreateReader () + { + if (_xmlMode == XmlModes.XmlFile) { + _fs = new FileStream (_data, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + return XmlReader.Create (_fs); + } else { // xmlModes.Xml + return XmlReader.Create (new StringReader (_data)); + } + } + + /// <summary> + /// Closes the reader. + /// </summary> + public void CloseReader () + { + if (_fs != null) { + _fs.Close (); + _fs = null; + } + } + } + } +} |