/Solutions/Main3.5/Framework/Xml/XmlFile.cs

# · C# · 796 lines · 558 code · 67 blank · 171 comment · 99 complexity · 3e9da9915e02381ce051f2017682ef47 MD5 · raw file

  1. //-----------------------------------------------------------------------
  2. // <copyright file="XmlFile.cs">(c) http://www.codeplex.com/MSBuildExtensionPack. This source is subject to the Microsoft Permissive License. See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx. All other rights reserved.</copyright>
  3. // Portions of this task are based on the http://www.codeplex.com/sdctasks. This source is subject to the Microsoft Permissive License. See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx. All other rights reserved.
  4. //-----------------------------------------------------------------------
  5. namespace MSBuild.ExtensionPack.Xml
  6. {
  7. using System;
  8. using System.Globalization;
  9. using System.Xml;
  10. using Microsoft.Build.Framework;
  11. using Microsoft.Build.Utilities;
  12. /// <summary>
  13. /// <b>Valid TaskActions are:</b>
  14. /// <para><i>AddAttribute</i> (<b>Required: </b>File, Element or XPath, Key, Value <b>Optional:</b> Namespaces, RetryCount)</para>
  15. /// <para><i>AddElement</i> (<b>Required: </b>File, Element and ParentElement or Element and XPath, <b>Optional:</b> Prefix, Key, Value, Namespaces, RetryCount, InnerText, InnerXml, InsertBeforeXPath / InsertAfterXPath)</para>
  16. /// <para><i>ReadAttribute</i> (<b>Required: </b>File, XPath <b>Optional:</b> Namespaces <b>Output:</b> Value)</para>
  17. /// <para><i>ReadElements</i> (<b>Required: </b>File, XPath <b>Optional:</b> Namespaces <b>Output: </b> Elements). Attributes are added as metadata</para>
  18. /// <para><i>ReadElementText</i> (<b>Required: </b>File, XPath <b>Optional:</b> Namespaces <b>Output:</b> Value)</para>
  19. /// <para><i>ReadElementXml</i> (<b>Required: </b>File, XPath <b>Optional:</b> Namespaces <b>Output:</b> Value)</para>
  20. /// <para><i>RemoveAttribute</i> (<b>Required: </b>File, Key, Element or XPath <b>Optional:</b> Namespaces, RetryCount)</para>
  21. /// <para><i>RemoveElement</i> (<b>Required: </b>File, Element and ParentElement or Element and XPath <b>Optional:</b> Namespaces, RetryCount)</para>
  22. /// <para><i>UpdateAttribute</i> (<b>Required: </b>File, XPath <b>Optional:</b> Namespaces, Key, Value, RetryCount)</para>
  23. /// <para><i>UpdateElement</i> (<b>Required: </b>File, XPath <b>Optional:</b> Namespaces, InnerText, InnerXml, RetryCount)</para>
  24. /// <para><b>Remote Execution Support:</b> NA</para>
  25. /// </summary>
  26. /// <example>
  27. /// <code lang="xml"><![CDATA[
  28. /// <Project ToolsVersion="3.5" DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  29. /// <PropertyGroup>
  30. /// <TPath>$(MSBuildProjectDirectory)\..\MSBuild.ExtensionPack.tasks</TPath>
  31. /// <TPath Condition="Exists('$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks')">$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks</TPath>
  32. /// </PropertyGroup>
  33. /// <Import Project="$(TPath)"/>
  34. /// <ItemGroup>
  35. /// <ConfigSettingsToDeploy Include="c:\machine.config">
  36. /// <Action>RemoveElement</Action>
  37. /// <Element>processModel</Element>
  38. /// <ParentElement>/configuration/system.web</ParentElement>
  39. /// </ConfigSettingsToDeploy>
  40. /// <ConfigSettingsToDeploy Include="c:\machine.config">
  41. /// <Action>AddElement</Action>
  42. /// <Element>processModel</Element>
  43. /// <ParentElement>/configuration/system.web</ParentElement>
  44. /// </ConfigSettingsToDeploy>
  45. /// <ConfigSettingsToDeploy Include="c:\machine.config">
  46. /// <Action>AddAttribute</Action>
  47. /// <Key>enable</Key>
  48. /// <ValueToAdd>true</ValueToAdd>
  49. /// <Element>/configuration/system.web/processModel</Element>
  50. /// </ConfigSettingsToDeploy>
  51. /// <ConfigSettingsToDeploy Include="c:\machine.config">
  52. /// <Action>AddAttribute</Action>
  53. /// <Key>timeout</Key>
  54. /// <ValueToAdd>Infinite</ValueToAdd>
  55. /// <Element>/configuration/system.web/processModel</Element>
  56. /// </ConfigSettingsToDeploy>
  57. /// <ConfigSettingsToDeploy Include="c:\machine.config">
  58. /// <Action>RemoveAttribute</Action>
  59. /// <Key>timeout</Key>
  60. /// <Element>/configuration/system.web/processModel</Element>
  61. /// </ConfigSettingsToDeploy>
  62. /// <XMLConfigElementsToAdd Include="c:\machine.config">
  63. /// <XPath>/configuration/configSections</XPath>
  64. /// <Name>section</Name>
  65. /// <KeyAttributeName>name</KeyAttributeName>
  66. /// <KeyAttributeValue>enterpriseLibrary.ConfigurationSource</KeyAttributeValue>
  67. /// </XMLConfigElementsToAdd>
  68. /// <XMLConfigElementsToAdd Include="c:\machine.config">
  69. /// <XPath>/configuration</XPath>
  70. /// <Name>enterpriseLibrary.ConfigurationSource</Name>
  71. /// <KeyAttributeName>selectedSource</KeyAttributeName>
  72. /// <KeyAttributeValue>MyKeyAttribute</KeyAttributeValue>
  73. /// </XMLConfigElementsToAdd>
  74. /// <XMLConfigElementsToAdd Include="c:\machine.config">
  75. /// <XPath>/configuration/enterpriseLibrary.ConfigurationSource</XPath>
  76. /// <Name>sources</Name>
  77. /// </XMLConfigElementsToAdd>
  78. /// <XMLConfigElementsToAdd Include="c:\machine.config">
  79. /// <XPath>/configuration/enterpriseLibrary.ConfigurationSource/sources</XPath>
  80. /// <Name>add</Name>
  81. /// <KeyAttributeName>name</KeyAttributeName>
  82. /// <KeyAttributeValue>MyKeyAttribute</KeyAttributeValue>
  83. /// </XMLConfigElementsToAdd>
  84. /// <XMLConfigAttributesToAdd Include="c:\machine.config">
  85. /// <XPath>/configuration/configSections/section[@name='enterpriseLibrary.ConfigurationSource']</XPath>
  86. /// <Name>type</Name>
  87. /// <Value>Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationSourceSection, Microsoft.Practices.EnterpriseLibrary.Common, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</Value>
  88. /// </XMLConfigAttributesToAdd>
  89. /// <XMLConfigAttributesToAdd Include="c:\machine.config">
  90. /// <XPath>/configuration/enterpriseLibrary.ConfigurationSource/sources/add[@name='MyKeyAttribute']</XPath>
  91. /// <Name>type</Name>
  92. /// <Value>MyKeyAttribute.Common, MyKeyAttribute.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fb2f49125f05d89</Value>
  93. /// </XMLConfigAttributesToAdd>
  94. /// <XMLConfigElementsToDelete Include="c:\machine.config">
  95. /// <XPath>/configuration/configSections/section[@name='enterpriseLibrary.ConfigurationSource']</XPath>
  96. /// </XMLConfigElementsToDelete>
  97. /// <XMLConfigElementsToDelete Include="c:\machine.config">
  98. /// <XPath>/configuration/enterpriseLibrary.ConfigurationSource[@selectedSource='MyKeyAttribute']</XPath>
  99. /// </XMLConfigElementsToDelete>
  100. /// </ItemGroup>
  101. /// <Target Name="Default">
  102. /// <!-- Work through some manipulations that don't use XPath-->
  103. /// <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="%(ConfigSettingsToDeploy.Action)" File="%(ConfigSettingsToDeploy.Identity)" Key="%(ConfigSettingsToDeploy.Key)" Value="%(ConfigSettingsToDeploy.ValueToAdd)" Element="%(ConfigSettingsToDeploy.Element)" ParentElement="%(ConfigSettingsToDeploy.ParentElement)" Condition="'%(ConfigSettingsToDeploy.Identity)'!=''"/>
  104. /// <!-- Work through some manipulations that use XPath-->
  105. /// <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="RemoveElement" File="%(XMLConfigElementsToDelete.Identity)" XPath="%(XMLConfigElementsToDelete.XPath)" Condition="'%(XMLConfigElementsToDelete.Identity)'!=''"/>
  106. /// <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="AddElement" File="%(XMLConfigElementsToAdd.Identity)" Key="%(XMLConfigElementsToAdd.KeyAttributeName)" Value="%(XMLConfigElementsToAdd.KeyAttributeValue)" Element="%(XMLConfigElementsToAdd.Name)" XPath="%(XMLConfigElementsToAdd.XPath)" Condition="'%(XMLConfigElementsToAdd.Identity)'!=''"/>
  107. /// <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="AddAttribute" File="%(XMLConfigAttributesToAdd.Identity)" Key="%(XMLConfigAttributesToAdd.Name)" Value="%(XMLConfigAttributesToAdd.Value)" XPath="%(XMLConfigAttributesToAdd.XPath)" Condition="'%(XMLConfigAttributesToAdd.Identity)'!=''"/>
  108. /// <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="UpdateElement" File="c:\machine.config" XPath="/configuration/configSections/section[@name='system.data']" InnerText="NewValue"/>
  109. /// <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="UpdateAttribute" File="c:\machine.config" XPath="/configuration/configSections/section[@name='system.data']" Key="SomeAttribute" Value="NewValue"/>
  110. /// </Target>
  111. /// <!-- The following illustrates Namespace usage -->
  112. /// <ItemGroup>
  113. /// <Namespaces Include="Mynamespace">
  114. /// <Prefix>me</Prefix>
  115. /// <Uri>http://mynamespace</Uri>
  116. /// </Namespaces>
  117. /// <XMLConfigElementsToDelete1 Include="c:\test.xml">
  118. /// <XPath>//me:MyNodes/sources</XPath>
  119. /// </XMLConfigElementsToDelete1>
  120. /// <XMLConfigElementsToAdd1 Include="c:\test.xml">
  121. /// <XPath>//me:MyNodes</XPath>
  122. /// <Name>sources</Name>
  123. /// </XMLConfigElementsToAdd1>
  124. /// </ItemGroup>
  125. /// <Target Name="DefaultWithNameSpace">
  126. /// <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="RemoveElement" Namespaces="@(Namespaces)" File="%(XMLConfigElementsToDelete1.Identity)" XPath="%(XMLConfigElementsToDelete1.XPath)" Condition="'%(XMLConfigElementsToDelete1.Identity)'!=''"/>
  127. /// <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="AddElement" Namespaces="@(Namespaces)" File="%(XMLConfigElementsToAdd1.Identity)" Key="%(XMLConfigElementsToAdd1.KeyAttributeName)" Value="%(XMLConfigElementsToAdd1.KeyAttributeValue)" Element="%(XMLConfigElementsToAdd1.Name)" XPath="%(XMLConfigElementsToAdd1.XPath)" Condition="'%(XMLConfigElementsToAdd1.Identity)'!=''"/>
  128. /// </Target>
  129. /// </Project>
  130. /// ]]></code>
  131. /// </example>
  132. [HelpUrl("http://www.msbuildextensionpack.com/help/3.5.10.0/html/4009fe8c-73c1-154f-ee8c-e9fda7f5fd96.htm")]
  133. public class XmlFile : BaseTask
  134. {
  135. private const string AddAttributeTaskAction = "AddAttribute";
  136. private const string AddElementTaskAction = "AddElement";
  137. private const string ReadAttributeTaskAction = "ReadAttribute";
  138. private const string ReadElementsTaskAction = "ReadElements";
  139. private const string ReadElementTextTaskAction = "ReadElementText";
  140. private const string ReadElementXmlTaskAction = "ReadElementXml";
  141. private const string RemoveAttributeTaskAction = "RemoveAttribute";
  142. private const string RemoveElementTaskAction = "RemoveElement";
  143. private const string UpdateAttributeTaskAction = "UpdateAttribute";
  144. private const string UpdateElementTaskAction = "UpdateElement";
  145. private XmlDocument xmlFileDoc;
  146. private XmlNamespaceManager namespaceManager;
  147. private XmlNodeList elements;
  148. private int retryCount = 5;
  149. [DropdownValue(AddAttributeTaskAction)]
  150. [DropdownValue(AddElementTaskAction)]
  151. [DropdownValue(ReadAttributeTaskAction)]
  152. [DropdownValue(ReadElementTextTaskAction)]
  153. [DropdownValue(ReadElementXmlTaskAction)]
  154. [DropdownValue(RemoveAttributeTaskAction)]
  155. [DropdownValue(RemoveElementTaskAction)]
  156. [DropdownValue(UpdateAttributeTaskAction)]
  157. [DropdownValue(UpdateElementTaskAction)]
  158. [DropdownValue(ReadElementsTaskAction)]
  159. public override string TaskAction
  160. {
  161. get { return base.TaskAction; }
  162. set { base.TaskAction = value; }
  163. }
  164. /// <summary>
  165. /// Sets the element. For AddElement, if the element exists, it's InnerText / InnerXml will be updated
  166. /// </summary>
  167. [TaskAction(AddAttributeTaskAction, true)]
  168. [TaskAction(AddElementTaskAction, true)]
  169. [TaskAction(RemoveAttributeTaskAction, true)]
  170. [TaskAction(RemoveElementTaskAction, true)]
  171. public string Element { get; set; }
  172. /// <summary>
  173. /// Sets the InnerText.
  174. /// </summary>
  175. [TaskAction(AddElementTaskAction, false)]
  176. [TaskAction(UpdateElementTaskAction, false)]
  177. public string InnerText { get; set; }
  178. /// <summary>
  179. /// Sets the InnerXml.
  180. /// </summary>
  181. [TaskAction(AddElementTaskAction, false)]
  182. [TaskAction(UpdateElementTaskAction, false)]
  183. public string InnerXml { get; set; }
  184. /// <summary>
  185. /// Sets the Prefix used for an added element, prefix must exists in Namespaces.
  186. /// </summary>
  187. [TaskAction(AddElementTaskAction, false)]
  188. public string Prefix { get; set; }
  189. /// <summary>
  190. /// Sets the parent element.
  191. /// </summary>
  192. [TaskAction(AddElementTaskAction, true)]
  193. [TaskAction(RemoveElementTaskAction, true)]
  194. public string ParentElement { get; set; }
  195. /// <summary>
  196. /// Sets the Attribute key.
  197. /// </summary>
  198. [TaskAction(AddAttributeTaskAction, false)]
  199. [TaskAction(RemoveAttributeTaskAction, true)]
  200. [TaskAction(UpdateAttributeTaskAction, false)]
  201. public string Key { get; set; }
  202. /// <summary>
  203. /// Gets or Sets the Attribute key value. Also stores the result of any Read TaskActions
  204. /// </summary>
  205. [TaskAction(AddAttributeTaskAction, true)]
  206. [TaskAction(UpdateAttributeTaskAction, false)]
  207. [Output]
  208. public string Value { get; set; }
  209. /// <summary>
  210. /// Gets the elements selected using ReadElements
  211. /// </summary>
  212. [TaskAction(ReadElementsTaskAction, true)]
  213. [Output]
  214. public ITaskItem[] Elements { get; set; }
  215. /// <summary>
  216. /// Sets the file.
  217. /// </summary>
  218. [Required]
  219. [TaskAction(AddAttributeTaskAction, true)]
  220. [TaskAction(AddElementTaskAction, true)]
  221. [TaskAction(ReadAttributeTaskAction, true)]
  222. [TaskAction(ReadElementTextTaskAction, true)]
  223. [TaskAction(ReadElementXmlTaskAction, true)]
  224. [TaskAction(RemoveAttributeTaskAction, true)]
  225. [TaskAction(RemoveElementTaskAction, true)]
  226. [TaskAction(ReadElementsTaskAction, true)]
  227. public ITaskItem File { get; set; }
  228. /// <summary>
  229. /// Specifies the XPath to be used
  230. /// </summary>
  231. [TaskAction(AddAttributeTaskAction, false)]
  232. [TaskAction(AddElementTaskAction, false)]
  233. [TaskAction(ReadAttributeTaskAction, true)]
  234. [TaskAction(ReadElementTextTaskAction, true)]
  235. [TaskAction(ReadElementXmlTaskAction, true)]
  236. [TaskAction(RemoveAttributeTaskAction, false)]
  237. [TaskAction(RemoveElementTaskAction, false)]
  238. [TaskAction(UpdateElementTaskAction, false)]
  239. [TaskAction(ReadElementsTaskAction, true)]
  240. public string XPath { get; set; }
  241. /// <summary>
  242. /// Specifies the XPath to be used to control where a new element is added. The Xpath must resolve to single node.
  243. /// </summary>
  244. [TaskAction(AddElementTaskAction, false)]
  245. public string InsertBeforeXPath { get; set; }
  246. /// <summary>
  247. /// Specifies the XPath to be used to control where a new element is added. The Xpath must resolve to single node.
  248. /// </summary>
  249. [TaskAction(AddElementTaskAction, false)]
  250. public string InsertAfterXPath { get; set; }
  251. /// <summary>
  252. /// TaskItems specifiying "Prefix" and "Uri" attributes for use with the specified XPath
  253. /// </summary>
  254. [TaskAction(AddAttributeTaskAction, false)]
  255. [TaskAction(AddElementTaskAction, false)]
  256. [TaskAction(ReadAttributeTaskAction, false)]
  257. [TaskAction(ReadElementTextTaskAction, false)]
  258. [TaskAction(ReadElementXmlTaskAction, false)]
  259. [TaskAction(RemoveAttributeTaskAction, false)]
  260. [TaskAction(RemoveElementTaskAction, false)]
  261. [TaskAction(UpdateAttributeTaskAction, false)]
  262. [TaskAction(UpdateElementTaskAction, false)]
  263. [TaskAction(ReadElementsTaskAction, false)]
  264. public ITaskItem[] Namespaces { get; set; }
  265. /// <summary>
  266. /// Sets a value indicating how many times to retry saving the file, e.g. if files are temporarily locked. Default is 5. The retry occurs every 5 seconds.
  267. /// </summary>
  268. [TaskAction(AddAttributeTaskAction, false)]
  269. [TaskAction(AddElementTaskAction, false)]
  270. [TaskAction(RemoveAttributeTaskAction, false)]
  271. [TaskAction(RemoveElementTaskAction, false)]
  272. [TaskAction(UpdateAttributeTaskAction, false)]
  273. [TaskAction(UpdateElementTaskAction, false)]
  274. public int RetryCount
  275. {
  276. get { return this.retryCount; }
  277. set { this.retryCount = value; }
  278. }
  279. /// <summary>
  280. /// Performs the action of this task.
  281. /// </summary>
  282. protected override void InternalExecute()
  283. {
  284. if (!System.IO.File.Exists(this.File.ItemSpec))
  285. {
  286. this.Log.LogError(string.Format(CultureInfo.CurrentCulture, "File not found: {0}", this.File.ItemSpec));
  287. return;
  288. }
  289. this.xmlFileDoc = new XmlDocument();
  290. try
  291. {
  292. this.xmlFileDoc.Load(this.File.ItemSpec);
  293. }
  294. catch (Exception ex)
  295. {
  296. this.LogTaskWarning(ex.Message);
  297. bool loaded = false;
  298. int count = 1;
  299. while (!loaded && count <= this.RetryCount)
  300. {
  301. this.LogTaskMessage(MessageImportance.High, string.Format(CultureInfo.InvariantCulture, "Load failed, trying again in 5 seconds. Attempt {0} of {1}", count, this.RetryCount));
  302. System.Threading.Thread.Sleep(5000);
  303. count++;
  304. try
  305. {
  306. this.xmlFileDoc.Load(this.File.ItemSpec);
  307. loaded = true;
  308. }
  309. catch
  310. {
  311. this.LogTaskWarning(ex.Message);
  312. }
  313. }
  314. if (loaded != true)
  315. {
  316. throw;
  317. }
  318. }
  319. if (!string.IsNullOrEmpty(this.XPath))
  320. {
  321. this.namespaceManager = this.GetNamespaceManagerForDoc();
  322. this.elements = this.xmlFileDoc.SelectNodes(this.XPath, this.namespaceManager);
  323. }
  324. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "XmlFile: {0}", this.File.ItemSpec));
  325. switch (this.TaskAction)
  326. {
  327. case AddElementTaskAction:
  328. this.AddElement();
  329. break;
  330. case AddAttributeTaskAction:
  331. this.AddAttribute();
  332. break;
  333. case ReadAttributeTaskAction:
  334. this.ReadAttribute();
  335. break;
  336. case ReadElementsTaskAction:
  337. this.ReadElements();
  338. break;
  339. case ReadElementTextTaskAction:
  340. case ReadElementXmlTaskAction:
  341. this.ReadElement();
  342. break;
  343. case RemoveAttributeTaskAction:
  344. this.RemoveAttribute();
  345. break;
  346. case RemoveElementTaskAction:
  347. this.RemoveElement();
  348. break;
  349. case UpdateElementTaskAction:
  350. this.UpdateElement();
  351. break;
  352. case UpdateAttributeTaskAction:
  353. this.UpdateAttribute();
  354. break;
  355. default:
  356. this.Log.LogError(string.Format(CultureInfo.CurrentCulture, "Invalid TaskAction passed: {0}", this.TaskAction));
  357. return;
  358. }
  359. }
  360. private void ReadElements()
  361. {
  362. if (string.IsNullOrEmpty(this.XPath))
  363. {
  364. this.Log.LogError("XPath is Required");
  365. return;
  366. }
  367. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Read Elements: {0}", this.XPath));
  368. XmlNodeList nodelist = this.xmlFileDoc.SelectNodes(this.XPath, this.namespaceManager);
  369. if (nodelist != null)
  370. {
  371. this.Elements = new ITaskItem[nodelist.Count];
  372. int i = 0;
  373. foreach (XmlNode node in nodelist)
  374. {
  375. ITaskItem newItem = new TaskItem(node.Name);
  376. if (node.Attributes != null)
  377. {
  378. foreach (XmlAttribute a in node.Attributes)
  379. {
  380. newItem.SetMetadata(a.Name, a.Value);
  381. }
  382. }
  383. this.Elements[i] = newItem;
  384. i++;
  385. }
  386. }
  387. }
  388. private void ReadElement()
  389. {
  390. if (string.IsNullOrEmpty(this.XPath))
  391. {
  392. this.Log.LogError("XPath is Required");
  393. return;
  394. }
  395. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Read Element: {0}", this.XPath));
  396. XmlNode node = this.xmlFileDoc.SelectSingleNode(this.XPath, this.namespaceManager);
  397. if (node != null && node.NodeType == XmlNodeType.Element)
  398. {
  399. this.Value = this.TaskAction == ReadElementTextTaskAction ? node.InnerText : node.InnerXml;
  400. }
  401. }
  402. private void ReadAttribute()
  403. {
  404. if (string.IsNullOrEmpty(this.XPath))
  405. {
  406. this.Log.LogError("XPath is Required");
  407. return;
  408. }
  409. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Read Attribute: {0}", this.XPath));
  410. XmlNode node = this.xmlFileDoc.SelectSingleNode(this.XPath, this.namespaceManager);
  411. if (node != null && node.NodeType == XmlNodeType.Attribute)
  412. {
  413. this.Value = node.Value;
  414. }
  415. }
  416. private void UpdateElement()
  417. {
  418. if (string.IsNullOrEmpty(this.XPath))
  419. {
  420. this.Log.LogError("XPath is Required");
  421. return;
  422. }
  423. if (string.IsNullOrEmpty(this.InnerXml))
  424. {
  425. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Update Element: {0}. InnerText: {1}", this.XPath, this.InnerText));
  426. if (this.elements != null && this.elements.Count > 0)
  427. {
  428. foreach (XmlNode element in this.elements)
  429. {
  430. element.InnerText = this.InnerText;
  431. }
  432. this.TrySave();
  433. }
  434. return;
  435. }
  436. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Update Element: {0}. InnerXml: {1}", this.XPath, this.InnerXml));
  437. if (this.elements != null && this.elements.Count > 0)
  438. {
  439. foreach (XmlNode element in this.elements)
  440. {
  441. element.InnerXml = this.InnerXml;
  442. }
  443. this.TrySave();
  444. }
  445. }
  446. private void UpdateAttribute()
  447. {
  448. if (string.IsNullOrEmpty(this.XPath))
  449. {
  450. this.Log.LogError("XPath is Required");
  451. return;
  452. }
  453. if (string.IsNullOrEmpty(this.Key))
  454. {
  455. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Update Attribute: {0}. Value: {1}", this.XPath, this.Value));
  456. XmlNode node = this.xmlFileDoc.SelectSingleNode(this.XPath, this.namespaceManager);
  457. if (node != null && node.NodeType == XmlNodeType.Attribute)
  458. {
  459. node.Value = this.Value;
  460. }
  461. }
  462. else
  463. {
  464. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Update Attribute: {0} @ {1}. Value: {2}", this.Key, this.XPath, this.Value));
  465. if (this.elements != null && this.elements.Count > 0)
  466. {
  467. foreach (XmlNode element in this.elements)
  468. {
  469. XmlAttribute attNode = element.Attributes.GetNamedItem(this.Key) as XmlAttribute;
  470. if (attNode != null)
  471. {
  472. attNode.Value = this.Value;
  473. }
  474. }
  475. }
  476. }
  477. this.TrySave();
  478. }
  479. private void RemoveAttribute()
  480. {
  481. if (string.IsNullOrEmpty(this.XPath))
  482. {
  483. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Remove Attribute: {0}", this.Key));
  484. XmlNode elementNode = this.xmlFileDoc.SelectSingleNode(this.Element);
  485. if (elementNode == null)
  486. {
  487. Log.LogError(string.Format(CultureInfo.CurrentUICulture, "Element not found: {0}", this.Element));
  488. return;
  489. }
  490. XmlAttribute attNode = elementNode.Attributes.GetNamedItem(this.Key) as XmlAttribute;
  491. if (attNode != null)
  492. {
  493. elementNode.Attributes.Remove(attNode);
  494. this.TrySave();
  495. }
  496. }
  497. else
  498. {
  499. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Remove Attribute: {0}", this.Key));
  500. if (this.elements != null && this.elements.Count > 0)
  501. {
  502. foreach (XmlNode element in this.elements)
  503. {
  504. XmlAttribute attNode = element.Attributes.GetNamedItem(this.Key) as XmlAttribute;
  505. if (attNode != null)
  506. {
  507. element.Attributes.Remove(attNode);
  508. this.TrySave();
  509. }
  510. }
  511. }
  512. }
  513. }
  514. private void AddAttribute()
  515. {
  516. if (string.IsNullOrEmpty(this.XPath))
  517. {
  518. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Set Attribute: {0}={1}", this.Key, this.Value));
  519. XmlNode elementNode = this.xmlFileDoc.SelectSingleNode(this.Element);
  520. if (elementNode == null)
  521. {
  522. Log.LogError(string.Format(CultureInfo.CurrentUICulture, "Element not found: {0}", this.Element));
  523. return;
  524. }
  525. XmlAttribute attNode = elementNode.Attributes.GetNamedItem(this.Key) as XmlAttribute;
  526. if (attNode == null)
  527. {
  528. attNode = this.xmlFileDoc.CreateAttribute(this.Key);
  529. attNode.Value = this.Value;
  530. elementNode.Attributes.Append(attNode);
  531. }
  532. else
  533. {
  534. attNode.Value = this.Value;
  535. }
  536. this.TrySave();
  537. }
  538. else
  539. {
  540. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Set Attribute: {0}={1}", this.Key, this.Value));
  541. if (this.elements != null && this.elements.Count > 0)
  542. {
  543. foreach (XmlNode element in this.elements)
  544. {
  545. XmlNode attrib = element.Attributes[this.Key] ?? element.Attributes.Append(this.xmlFileDoc.CreateAttribute(this.Key));
  546. attrib.Value = this.Value;
  547. }
  548. this.TrySave();
  549. }
  550. }
  551. }
  552. private XmlNamespaceManager GetNamespaceManagerForDoc()
  553. {
  554. XmlNamespaceManager localnamespaceManager = new XmlNamespaceManager(this.xmlFileDoc.NameTable);
  555. // If we have had namespace declarations specified add them to the Namespace Mgr for the XML Document.
  556. if (this.Namespaces != null && this.Namespaces.Length > 0)
  557. {
  558. foreach (ITaskItem item in this.Namespaces)
  559. {
  560. string prefix = item.GetMetadata("Prefix");
  561. string uri = item.GetMetadata("Uri");
  562. localnamespaceManager.AddNamespace(prefix, uri);
  563. }
  564. }
  565. return localnamespaceManager;
  566. }
  567. private void AddElement()
  568. {
  569. if (string.IsNullOrEmpty(this.XPath))
  570. {
  571. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Add Element: {0}", this.Element));
  572. XmlNode parentNode = this.xmlFileDoc.SelectSingleNode(this.ParentElement);
  573. if (parentNode == null)
  574. {
  575. Log.LogError("ParentElement not found: " + this.ParentElement);
  576. return;
  577. }
  578. // Ensure node does not already exist
  579. XmlNode newNode = this.xmlFileDoc.SelectSingleNode(this.ParentElement + "/" + this.Element);
  580. if (newNode == null)
  581. {
  582. newNode = this.CreateElement();
  583. if (!string.IsNullOrEmpty(this.Key))
  584. {
  585. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Add Attribute: {0} to: {1}", this.Key, this.Element));
  586. XmlAttribute attNode = this.xmlFileDoc.CreateAttribute(this.Key);
  587. attNode.Value = this.Value;
  588. newNode.Attributes.Append(attNode);
  589. }
  590. if (string.IsNullOrEmpty(this.InsertAfterXPath) && string.IsNullOrEmpty(this.InsertBeforeXPath))
  591. {
  592. parentNode.AppendChild(newNode);
  593. }
  594. else if (!string.IsNullOrEmpty(this.InsertAfterXPath))
  595. {
  596. parentNode.InsertAfter(newNode, parentNode.SelectSingleNode(this.InsertAfterXPath));
  597. }
  598. else if (!string.IsNullOrEmpty(this.InsertBeforeXPath))
  599. {
  600. parentNode.InsertBefore(newNode, parentNode.SelectSingleNode(this.InsertBeforeXPath));
  601. }
  602. this.TrySave();
  603. }
  604. else
  605. {
  606. if (!string.IsNullOrEmpty(this.InnerText))
  607. {
  608. newNode.InnerText = this.InnerText;
  609. }
  610. else if (!string.IsNullOrEmpty(this.InnerXml))
  611. {
  612. newNode.InnerXml = this.InnerXml;
  613. }
  614. this.TrySave();
  615. }
  616. }
  617. else
  618. {
  619. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Add Element: {0}", this.XPath));
  620. if (this.elements != null && this.elements.Count > 0)
  621. {
  622. foreach (XmlNode element in this.elements)
  623. {
  624. XmlNode newNode = this.CreateElement();
  625. if (!string.IsNullOrEmpty(this.Key))
  626. {
  627. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Add Attribute: {0} to: {1}", this.Key, this.Element));
  628. XmlAttribute attNode = this.xmlFileDoc.CreateAttribute(this.Key);
  629. attNode.Value = this.Value;
  630. newNode.Attributes.Append(attNode);
  631. }
  632. element.AppendChild(newNode);
  633. }
  634. this.TrySave();
  635. }
  636. }
  637. }
  638. private XmlNode CreateElement()
  639. {
  640. XmlNode newNode;
  641. if (string.IsNullOrEmpty(this.Prefix))
  642. {
  643. newNode = this.xmlFileDoc.CreateElement(this.Element, this.xmlFileDoc.DocumentElement.NamespaceURI);
  644. }
  645. else
  646. {
  647. string prefixNamespace = this.namespaceManager.LookupNamespace(this.Prefix);
  648. if (string.IsNullOrEmpty(prefixNamespace))
  649. {
  650. Log.LogError("Prefix not defined in Namespaces in parameters: " + this.Prefix);
  651. return null;
  652. }
  653. newNode = this.xmlFileDoc.CreateElement(this.Prefix, this.Element, prefixNamespace);
  654. }
  655. if (!string.IsNullOrEmpty(this.InnerText))
  656. {
  657. newNode.InnerText = this.InnerText;
  658. }
  659. else if (!string.IsNullOrEmpty(this.InnerXml))
  660. {
  661. newNode.InnerXml = this.InnerXml;
  662. }
  663. return newNode;
  664. }
  665. private void RemoveElement()
  666. {
  667. if (string.IsNullOrEmpty(this.XPath))
  668. {
  669. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Remove Element: {0}", this.Element));
  670. XmlNode parentNode = this.xmlFileDoc.SelectSingleNode(this.ParentElement);
  671. if (parentNode == null)
  672. {
  673. Log.LogError("ParentElement not found: " + this.ParentElement);
  674. return;
  675. }
  676. XmlNode nodeToRemove = this.xmlFileDoc.SelectSingleNode(this.ParentElement + "/" + this.Element);
  677. if (nodeToRemove != null)
  678. {
  679. parentNode.RemoveChild(nodeToRemove);
  680. this.TrySave();
  681. }
  682. }
  683. else
  684. {
  685. this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Remove Element: {0}", this.XPath));
  686. if (this.elements != null && this.elements.Count > 0)
  687. {
  688. foreach (XmlNode element in this.elements)
  689. {
  690. element.ParentNode.RemoveChild(element);
  691. }
  692. this.TrySave();
  693. }
  694. }
  695. }
  696. private void TrySave()
  697. {
  698. try
  699. {
  700. this.xmlFileDoc.Save(this.File.ItemSpec);
  701. }
  702. catch (Exception ex)
  703. {
  704. this.LogTaskWarning(ex.Message);
  705. bool saved = false;
  706. int count = 1;
  707. while (!saved && count <= this.RetryCount)
  708. {
  709. this.LogTaskMessage(MessageImportance.High, string.Format(CultureInfo.InvariantCulture, "Save failed, trying again in 5 seconds. Attempt {0} of {1}", count, this.RetryCount));
  710. System.Threading.Thread.Sleep(5000);
  711. count++;
  712. try
  713. {
  714. this.xmlFileDoc.Save(this.File.ItemSpec);
  715. saved = true;
  716. }
  717. catch
  718. {
  719. this.LogTaskWarning(ex.Message);
  720. }
  721. }
  722. if (saved != true)
  723. {
  724. throw;
  725. }
  726. }
  727. }
  728. }
  729. }