PageRenderTime 58ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Xml/XmlNode.cs

#
C# | 1728 lines | 970 code | 156 blank | 602 comment | 73 complexity | 94db6e76e327e360636061ae7e6678ca MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Text;
  6. using System.Xml.Linq;
  7. using Delta.Utilities.Helpers;
  8. using NUnit.Framework;
  9. namespace Delta.Utilities.Xml
  10. {
  11. /// <summary>
  12. /// Our own XmlNode format which provides only required information and
  13. /// offers a couple of useful functions. The main idea is avoiding that the
  14. /// user handle complicated xml functions, instead giving a very simple
  15. /// class which wraps the properties and function we think are important
  16. /// and is presented in a very easy way to use.
  17. /// </summary>
  18. public class XmlNode
  19. {
  20. #region GetNamespaceName (Static)
  21. /// <summary>
  22. /// Helper method to construct NamespaceName from given
  23. /// namespaceName and nodeName.
  24. /// </summary>
  25. /// <param name="namespaceName">Namespace name</param>
  26. /// <param name="nodeName">Node name</param>
  27. /// <returns>Namespace name</returns>
  28. public static string GetNamespaceName(string namespaceName,
  29. string nodeName)
  30. {
  31. return GetNamespaceName(XNamespace.Get(namespaceName), nodeName);
  32. }
  33. /// <summary>
  34. /// Helper method to construct NamespaceName from given
  35. /// namespace and nodeName.
  36. /// </summary>
  37. /// <param name="xNamespace">Namespace</param>
  38. /// <param name="nodeName">Node name</param>
  39. /// <returns>Namespace name</returns>
  40. public static string GetNamespaceName(XNamespace xNamespace,
  41. string nodeName)
  42. {
  43. return (xNamespace + nodeName).ToString();
  44. }
  45. #endregion
  46. #region FromFile (Static)
  47. /// <summary>
  48. /// Loads the XML file under the given path and returns the root node or
  49. /// 'null' if the loading fails. AutoSetNamespaceOnChildren is set to
  50. /// false.
  51. /// </summary>
  52. /// <param name="xmlFilePath">Xml file path</param>
  53. /// <returns>the root node or 'null' if the loading fails</returns>
  54. public static XmlNode FromFile(string xmlFilePath)
  55. {
  56. string error;
  57. return FromFile(xmlFilePath, false, out error);
  58. }
  59. /// <summary>
  60. /// Loads the XML file under the given path and returns the root node or
  61. /// 'null' if the loading fails.
  62. /// </summary>
  63. /// <param name="xmlFilePath">Xml file path</param>
  64. /// <param name="autoSetNamespaceOnChildren">Flag if the namespace should
  65. /// be set to the children automatically.</param>
  66. /// <returns>the root node or 'null' if the loading fails</returns>
  67. public static XmlNode FromFile(string xmlFilePath,
  68. bool autoSetNamespaceOnChildren, out string error)
  69. {
  70. try
  71. {
  72. XDocument xDoc = XDocument.Load(FileHelper.Open(
  73. xmlFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
  74. var node = new XmlNode(xDoc.Root, autoSetNamespaceOnChildren);
  75. error = "";
  76. return node;
  77. }
  78. catch (Exception ex)
  79. {
  80. error = "XmlNode.FromFile: Couldn't load XML file '" +
  81. xmlFilePath + "' because:\n" + ex.Message;
  82. Log.Warning(error);
  83. return null;
  84. }
  85. }
  86. #endregion
  87. #region FromSnippet (Static)
  88. /// <summary>
  89. /// Loads and Xml node from an Xml snippet.
  90. /// </summary>
  91. /// <param name="xmlAsText">Xml as text</param>
  92. /// <returns>Xml node</returns>
  93. public static XmlNode FromSnippet(string xmlAsText)
  94. {
  95. string error;
  96. return FromSnippet(xmlAsText, out error);
  97. }
  98. /// <summary>
  99. /// Loads and Xml node from an Xml snippet.
  100. /// </summary>
  101. /// <param name="xmlAsText">Xml as text</param>
  102. /// <param name="error">Error string if anything has gone wrong</param>
  103. /// <returns>Filled XmlNode from xmlAsText</returns>
  104. public static XmlNode FromSnippet(string xmlAsText, out string error)
  105. {
  106. if (String.IsNullOrEmpty(xmlAsText))
  107. {
  108. error =
  109. "The given XML snippet is empty, unable to return anything " +
  110. "useful (will return empty node with the name '-InvalidXml-').";
  111. Log.Warning("XmlNode LoadSnippet failed: " + error);
  112. return new XmlNode("-InvalidXml-");
  113. }
  114. // Safety-check that we don't start with "trash" like new-line at the
  115. // beginning what XLinq don't like
  116. int cutOffIndex = xmlAsText.IndexOf('<');
  117. // if xml source doesn't start with '<' then we have "trash"
  118. if (cutOffIndex > 0)
  119. {
  120. xmlAsText = xmlAsText.Substring(cutOffIndex);
  121. }
  122. // Just using the "Parse()" function from .NET
  123. // http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument.parse.aspx
  124. XDocument xDocument = XDocument.Parse(xmlAsText);
  125. if (xDocument == null ||
  126. xDocument.Root == null)
  127. {
  128. error =
  129. "The given text snippet '" + xmlAsText + "' is not valid XML" +
  130. " (will return empty node with the name '-InvalidXml-').";
  131. Log.Warning("XmlNode LoadSnippet failed: " + error);
  132. return new XmlNode("-InvalidXml-");
  133. }
  134. // Everything is ok
  135. error = "";
  136. return new XmlNode(xDocument.Root);
  137. }
  138. #endregion
  139. #region FromMemoryStream (Static)
  140. /// <summary>
  141. /// Helper method to load xml data from a memory stream, which must contain
  142. /// the same data as a saved xml file (see the Save() method). To load
  143. /// snippet strings use the FromSnippet method instead.
  144. /// </summary>
  145. /// <param name="xmlData">Xml data in a memory stream</param>
  146. /// <returns>Loaded XmlNode</returns>
  147. public static XmlNode FromMemoryStream(MemoryStream xmlData)
  148. {
  149. xmlData.Seek(0, SeekOrigin.Begin);
  150. XDocument xDoc = XDocument.Load(xmlData);
  151. return new XmlNode(xDoc.Root, false);
  152. }
  153. #endregion
  154. #region Name (Public)
  155. /// <summary>
  156. /// Name of the xml root node.
  157. /// </summary>
  158. public string Name
  159. {
  160. get
  161. {
  162. return xNodeRoot.Name.LocalName;
  163. }
  164. }
  165. #endregion
  166. #region Value (Public)
  167. /// <summary>
  168. /// Value of the xml root node
  169. /// </summary>
  170. public string Value
  171. {
  172. get
  173. {
  174. return xNodeRoot.Value;
  175. }
  176. set
  177. {
  178. xNodeRoot.SetValue(value == null
  179. ? ""
  180. : value);
  181. }
  182. }
  183. #endregion
  184. #region Parent (Public)
  185. /// <summary>
  186. /// Returns the parent node of this node or 'null' if there is no parent
  187. /// (anymore).
  188. /// </summary>
  189. public XmlNode Parent
  190. {
  191. get;
  192. private set;
  193. }
  194. #endregion
  195. #region Children (Public)
  196. /// <summary>
  197. /// Returns children nodes from current parent.
  198. /// </summary>
  199. public XmlNode[] Children
  200. {
  201. get;
  202. private set;
  203. }
  204. #endregion
  205. #region FilePath (Public)
  206. /// <summary>
  207. /// The (absolute) file path from where the xml was loaded (is 'None' if
  208. /// the XML was created dynamically).
  209. /// </summary>
  210. public string FilePath
  211. {
  212. get
  213. {
  214. return xNodeRoot.Document.BaseUri;
  215. }
  216. }
  217. #endregion
  218. #region WillChildInheritNamespace (Public)
  219. /// <summary>
  220. /// If disabled (by default all XmlNodes have this disabled):
  221. /// You have to manually set the namespace on every child node you add,
  222. /// else it will be empty namespace.
  223. ///
  224. /// If enabled:
  225. /// Added children will automatically use the same namespace as parent node
  226. ///
  227. /// You may set the namespace this way:
  228. ///
  229. /// XmlNode projectNode =
  230. /// new XmlNode(
  231. /// XmlNode.GetNamespaceName(
  232. /// CsprojXml.Namespace + CsprojXml.ProjectKeyword),
  233. /// true);
  234. ///
  235. /// or
  236. ///
  237. /// node.AddChild(XmlNode.GetNamespaceName("some namespace", "Tag Name"))
  238. /// </summary>
  239. public bool WillChildInheritNamespace
  240. {
  241. get;
  242. set;
  243. }
  244. #endregion
  245. #region Private
  246. #region xNodeRoot (Private)
  247. /// <summary>
  248. /// Main element that contain all the info related with the xml node.
  249. /// Be aware that from this variable we get all the information from
  250. /// the xml file we are working with.
  251. /// </summary>
  252. private readonly XElement xNodeRoot;
  253. #endregion
  254. #endregion
  255. #region Constructors
  256. /// <summary>
  257. /// We only allow to create a XmlNode internally and we rely on
  258. /// XmlHelper to create it when necessary. As expected, to start off
  259. /// we assign a root node that contains everything we need.
  260. /// </summary>
  261. /// <param name="setNodeRoot">represents the root node</param>
  262. internal XmlNode(XElement setNodeRoot)
  263. : this(setNodeRoot, null)
  264. {
  265. }
  266. /// <summary>
  267. /// We only allow to create a XmlNode internally and we rely on
  268. /// XmlHelper to create it when necessary. As expected, to start off
  269. /// we assign a root node that contains everything we need.
  270. /// </summary>
  271. /// <param name="setNodeRoot">represents the root node</param>
  272. /// <param name="autoSetNamespaceOnChildren">Flag if the namespace should
  273. /// be set to the children automatically.</param>
  274. internal XmlNode(XElement setNodeRoot, bool autoSetNamespaceOnChildren)
  275. : this(setNodeRoot, null, autoSetNamespaceOnChildren)
  276. {
  277. }
  278. /// <summary>
  279. /// We only allow to create a XmlNode internally and we rely on
  280. /// XmlHelper to create it when necessary. As expected, to start off
  281. /// we assign a root node that contains everything we need.
  282. /// </summary>
  283. /// <param name="setNodeRoot">represents the root node</param>
  284. /// <param name="setParent">the parent node</param>
  285. internal XmlNode(XElement setNodeRoot, XmlNode setParent)
  286. : this(setNodeRoot, setParent, false)
  287. {
  288. }
  289. /// <summary>
  290. /// We only allow to create a XmlNode internally and we rely on
  291. /// XmlHelper to create it when necessary. As expected, to start off
  292. /// we assign a root node that contains everything we need.
  293. /// </summary>
  294. /// <param name="setNodeRoot">represents the root node</param>
  295. /// <param name="setParent">the parent node</param>
  296. /// <param name="autoSetNamespaceOnChildren">Flag if the namespace should
  297. /// be set to the children automatically.</param>
  298. internal XmlNode(XElement setNodeRoot, XmlNode setParent,
  299. bool autoSetNamespaceOnChildren)
  300. {
  301. WillChildInheritNamespace = autoSetNamespaceOnChildren;
  302. // Check that we got a valid root node.
  303. if (setNodeRoot == null)
  304. {
  305. throw new ArgumentNullException("setNodeRoot");
  306. }
  307. xNodeRoot = setNodeRoot;
  308. // We need to read the immediate children once and assign it to the
  309. // corresponding property for caching
  310. var descendants = new List<XElement>(xNodeRoot.Elements());
  311. var childList = new List<XmlNode>();
  312. for (int i = 0; i < descendants.Count; i++)
  313. {
  314. childList.Add(new XmlNode(descendants[i], this,
  315. autoSetNamespaceOnChildren));
  316. }
  317. Children = childList.ToArray();
  318. // and "link" to the parent node if there is one
  319. Parent = setParent;
  320. }
  321. /// <summary>
  322. /// Creates an XML root node with the given name, but no children yet.
  323. /// When createWithXmlHeader is true an Xml declaration is added at top!
  324. /// </summary>
  325. /// <param name="createWithXmlHeader">Create with xml header</param>
  326. /// <param name="setNamespace">Set namespace</param>
  327. /// <param name="setRootName">Set root node name</param>
  328. public XmlNode(string setNamespace, string setRootName,
  329. bool createWithXmlHeader)
  330. : this(GetNamespaceName(setNamespace, setRootName),
  331. createWithXmlHeader)
  332. {
  333. }
  334. /// <summary>
  335. /// Creates an XML root node with the given name, but no children yet.
  336. /// When createWithXmlHeader is true an Xml declaration is added at top!
  337. /// </summary>
  338. /// <param name="createWithXmlHeader">create with xml header</param>
  339. /// <param name="setNamespace">set namespace</param>
  340. /// <param name="setRootName">set root name</param>
  341. public XmlNode(XNamespace setNamespace, string setRootName,
  342. bool createWithXmlHeader)
  343. : this(GetNamespaceName(setNamespace, setRootName),
  344. createWithXmlHeader)
  345. {
  346. }
  347. /// <summary>
  348. /// Creates an XML root node with the given name, but no children yet.
  349. /// When createWithXmlHeader is true an Xml declaration is added at top!
  350. /// </summary>
  351. /// <param name="createWithXmlHeader">create with xml heater</param>
  352. /// <param name="setRootName">set root name</param>
  353. public XmlNode(string setRootName, bool createWithXmlHeader)
  354. {
  355. if (String.IsNullOrEmpty(setRootName))
  356. {
  357. Log.Warning("The XmlNode you want to create has no name.");
  358. setRootName = "<Empty>";
  359. }
  360. // Always make sure that we don't have white spaces in the name..
  361. setRootName = setRootName.Replace(" ", "");
  362. // Create then the native XML root node
  363. xNodeRoot = new XElement(setRootName);
  364. // and the belonging XML document
  365. if (createWithXmlHeader)
  366. {
  367. var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
  368. xNodeRoot);
  369. }
  370. else
  371. {
  372. var xDoc = new XDocument();
  373. xDoc.Add(xNodeRoot);
  374. }
  375. // Finally just "say" that we don't have any children (empty array)
  376. Children = new XmlNode[0];
  377. }
  378. /// <summary>
  379. /// Create xml node root node with the given name (no xml header)
  380. /// </summary>
  381. /// <param name="setRootName">set root name</param>
  382. public XmlNode(string setRootName)
  383. : this(setRootName, false)
  384. {
  385. }
  386. #endregion
  387. #region AddChild (Public)
  388. /// <summary>
  389. /// Adds a new child with given name and returns it to be filled.
  390. /// If ChildWillInheritNamespace is true, it will inherit the namespace of
  391. /// this node (Namespace part in given name will be ignored).
  392. /// </summary>
  393. /// <param name="name">Name for the new child</param>
  394. /// <returns>The newly added child as an XmlNode</returns>
  395. public XmlNode AddChild(string name)
  396. {
  397. name = ConstructName(name);
  398. var xElement = new XElement(name);
  399. var node = new XmlNode(xElement, WillChildInheritNamespace);
  400. node.Parent = this;
  401. var childs = new List<XmlNode>(Children);
  402. childs.Add(node);
  403. xNodeRoot.Add(xElement);
  404. Children = childs.ToArray();
  405. return node;
  406. }
  407. /// <summary>
  408. /// Adds a new child with given name and returns it to be filled.
  409. /// If ChildWillInheritNamespace is true, it will inherit the namespace of
  410. /// this node (Namespace part in given name will be ignored).
  411. /// </summary>
  412. /// <param name="name">Name</param>
  413. /// <param name="value">Value</param>
  414. /// <returns>The newly added child as an XmlNode</returns>
  415. public XmlNode AddChild(string name, string value)
  416. {
  417. name = ConstructName(name);
  418. var xElement = new XElement(name);
  419. var node = new XmlNode(xElement, WillChildInheritNamespace);
  420. node.Parent = this;
  421. node.Value = value;
  422. xElement.Value = value;
  423. xNodeRoot.Add(xElement);
  424. var childs = new List<XmlNode>(Children);
  425. childs.Add(node);
  426. Children = childs.ToArray();
  427. return node;
  428. }
  429. /// <summary>
  430. /// Add child from XmlNode. Note: This will create a copy of the XmlNode
  431. /// to make sure we do not modify the original XmlNode that could come
  432. /// from another tree or document.
  433. /// </summary>
  434. /// <param name="childNode">Child node we want to add</param>
  435. public XmlNode AddChild(XmlNode childNode)
  436. {
  437. string newName = ConstructName(childNode.Name);
  438. var xElement = new XElement(newName);
  439. var node = new XmlNode(xElement, WillChildInheritNamespace);
  440. node.Parent = this;
  441. xNodeRoot.Add(xElement);
  442. var childs = new List<XmlNode>(Children);
  443. childs.Add(node);
  444. Children = childs.ToArray();
  445. // Copy the value and all attributes, and also all children recursively
  446. node.Value = childNode.Value;
  447. Dictionary<string, string> attributes = childNode.GetAllAttributes();
  448. foreach (KeyValuePair<string, string> attributePair in attributes)
  449. {
  450. node.AddAttribute(attributePair.Key, attributePair.Value);
  451. }
  452. foreach (XmlNode copyChild in childNode.Children)
  453. {
  454. node.AddChild(copyChild);
  455. }
  456. return node;
  457. }
  458. #endregion
  459. #region AddAttribute (Public)
  460. /// <summary>
  461. /// Add attribute
  462. /// </summary>
  463. /// <param name="name">name</param>
  464. /// <param name="value">value</param>
  465. public void AddAttribute(string name, string value)
  466. {
  467. xNodeRoot.SetAttributeValue(name, value);
  468. }
  469. /// <summary>
  470. /// Add attribute
  471. /// </summary>
  472. public void AddAttribute(string attributeNamespace, string name,
  473. string value)
  474. {
  475. xNodeRoot.SetAttributeValue(XName.Get(name, attributeNamespace), value);
  476. }
  477. #endregion
  478. #region GetValueAs (Public)
  479. /// <summary>
  480. /// Get value as any supported datatype that is passed to this generic
  481. /// method. Uses StringHelper.FromInvariantString to convert.
  482. /// </summary>
  483. /// <param name="defaultValue">Default value</param>
  484. /// <returns>Value string converted to type T</returns>
  485. public T GetValueAs<T>(T defaultValue)
  486. {
  487. return Value.FromInvariantString(defaultValue,
  488. "The value '" + Value + "' of XmlNode '" + Name + "' couldn't be " +
  489. "converted to type '" + typeof(T) + "'.");
  490. }
  491. #endregion
  492. #region GetAttribute (Public)
  493. /// <summary>
  494. /// Look for the value of an attribute (specified as parameter) in the
  495. /// root node. It returns the first attribute which matches with the
  496. /// given parameters. If desired we can do an depth search in the child
  497. /// nodes. It is optional due to performance costs (it has to be carefully
  498. /// used). By default the search is NOT done recursively.
  499. /// </summary>
  500. /// <param name="name">Attribute name to search</param>
  501. /// <param name="searchRecursively">
  502. /// Allow search recursively in the child nodes (defaults to false).
  503. /// </param>
  504. /// <returns>
  505. /// If found the attribute value, otherwise an empty string.
  506. /// </returns>
  507. public string GetAttribute(string name, bool searchRecursively = false)
  508. {
  509. // Check that we got a valid root node.
  510. if (xNodeRoot == null)
  511. {
  512. throw new NullReferenceException(
  513. "Need valid rootNode for GetAttribute.");
  514. }
  515. // Do we have to search internally?
  516. if (searchRecursively)
  517. {
  518. // Use an appropriate auxiliary function which attempts to look
  519. // for the required attribute.
  520. XAttribute attribute = FindFirstDescendantAttribute(
  521. xNodeRoot.DescendantsAndSelf(), name);
  522. // If the search was a success, just send back the value.
  523. return
  524. attribute != null
  525. ? attribute.Value
  526. : "";
  527. }
  528. // Get the first attribute which matches with the specified name
  529. var attributes =
  530. new List<XAttribute>(xNodeRoot.Attributes());
  531. for (int i = 0; i < attributes.Count; i++)
  532. {
  533. if (attributes[i].Name.LocalName == name)
  534. {
  535. return attributes[i].Value;
  536. }
  537. }
  538. return "";
  539. }
  540. #endregion
  541. #region GetAllAttributes (Public)
  542. /// <summary>
  543. /// Get all attributes, use this method if you want better performance
  544. /// than calling GetAttribute or GetAttributeAs, which both do a lot of
  545. /// checking, support recursion (which is slow) and will do all the
  546. /// conversion for you (again slow). This is the fastest way to get to
  547. /// attribute values, but you will have to convert types yourself.
  548. /// </summary>
  549. /// <returns>
  550. /// Dictionary with all attributes and values as strings.
  551. /// </returns>
  552. public Dictionary<string, string> GetAllAttributes()
  553. {
  554. var result = new Dictionary<string, string>(
  555. StringComparer.InvariantCultureIgnoreCase);
  556. // Get the first attribute which matches with the specified name
  557. foreach (XAttribute attribute in xNodeRoot.Attributes())
  558. {
  559. result.Add(attribute.Name.LocalName, attribute.Value);
  560. }
  561. return result;
  562. }
  563. #endregion
  564. #region GetAttributeAs (Public)
  565. /// <summary>
  566. /// Get attribute as any supported datatype that is passed to this generic
  567. /// method. Note: This currently throws warnings if an attribute is missing
  568. /// or has no value (empty string), use GetAttribute instead of you don't
  569. /// want all this extra checking and warnings (this method is slow).
  570. /// Uses StringHelper.FromInvariantString to convert.
  571. /// </summary>
  572. /// <param name="name">Attribute name to search for</param>
  573. /// <param name="defaultValue">
  574. /// Default value if the attribute was not found.
  575. /// </param>
  576. /// <param name="extraWarningTextIfAnythingGoesWrong">
  577. /// Extra warning in case anything goes wrong to really help the user to
  578. /// identify the problem with a related message (e.g. font content name).
  579. /// By default empty string (""), which means no errors will be reported.
  580. /// </param>
  581. /// <returns>
  582. /// Filled T value or defaultValue if attribute was not found or type
  583. /// conversion is not supported (a log warning will also be outputted).
  584. /// </returns>
  585. public T GetAttributeAs<T>(string name, T defaultValue,
  586. string extraWarningTextIfAnythingGoesWrong = "")
  587. {
  588. return GetAttribute(name).FromInvariantString(defaultValue,
  589. extraWarningTextIfAnythingGoesWrong);
  590. }
  591. #endregion
  592. #region GetChild (Public)
  593. /// <summary>
  594. /// Look for a node with the name specified as parameter. It returns the
  595. /// first node which matchs with the given parameter.
  596. /// Note: The search is recursively.
  597. /// </summary>
  598. /// <param name="childName">Node name to search</param>
  599. /// <returns>Node</returns>
  600. public XmlNode GetChild(string childName)
  601. {
  602. return GetChild(childName, true);
  603. }
  604. /// <summary>
  605. /// Look for a node with the name specified as parameter. It returns the
  606. /// first node which matchs with the given parameter.
  607. /// </summary>
  608. /// <param name="childName">Node name to search</param>
  609. /// <param name="searchRecursively">
  610. /// Allow search recursively in the child nodes?
  611. /// </param>
  612. public XmlNode GetChild(string childName, bool searchRecursively)
  613. {
  614. // Just iterate over all (directly) childs
  615. for (int index = 0; index < Children.Length; index++)
  616. {
  617. // At first directly look for the first one where the name matches
  618. XmlNode child = Children[index];
  619. if (StringHelper.Compare(child.Name, childName))
  620. {
  621. return child;
  622. }
  623. // And if doesn't match then just look (recursively) at his childs
  624. if (searchRecursively)
  625. {
  626. XmlNode childOfChild = child.GetChild(childName);
  627. if (childOfChild != null)
  628. {
  629. return childOfChild;
  630. }
  631. }
  632. }
  633. // If we don't have find one, then there isn't one...
  634. return null;
  635. }
  636. /// <summary>
  637. /// Look for a node with the name specified as parameter and which
  638. /// contains a particular attribute. This function is thought for when
  639. /// we have several nodes with the same name to allow the user to
  640. /// get one with a concrete attribute. It returns the first node which
  641. /// matchs with the attribute name and value.
  642. /// Note: The search is recursively.
  643. /// </summary>
  644. /// <param name="attributeName">Attribute to search</param>
  645. /// <param name="attributeValue">Attribute value to search</param>
  646. /// <returns>Found child node or null if nothing was found</returns>
  647. public XmlNode GetChild(string attributeName, string attributeValue)
  648. {
  649. return GetChild(null, attributeName, attributeValue);
  650. }
  651. /// <summary>
  652. /// Look for a node with the name specified as parameter and which
  653. /// contains a particular attribute. This function is thought for when
  654. /// we have several nodes with the same name to allow the user to
  655. /// get one with a concrete attribute. It returns the first node which
  656. /// matchs with the attribute name and value.
  657. /// Note: The search is recursively.
  658. /// </summary>
  659. /// <param name="childName">
  660. /// Node name to search, can be null for all kind of Xml child nodes.
  661. /// </param>
  662. /// <param name="attributeName">Attribute to search</param>
  663. /// <param name="attributeValue">Attribute value to search</param>
  664. /// <returns>Found child node or null if nothing was found</returns>
  665. public XmlNode GetChild(string childName, string attributeName,
  666. string attributeValue)
  667. {
  668. // If there is no node name given, then we iterate over all child nodes...
  669. bool skipNameCheck = String.IsNullOrEmpty(childName);
  670. // Just iterate over all children
  671. for (int num = 0; num < Children.Length; num++)
  672. {
  673. // At first directly look for the first one where the name matches
  674. XmlNode child = Children[num];
  675. if ((skipNameCheck ||
  676. StringHelper.Compare(child.Name, childName)) &&
  677. StringHelper.Compare(child.GetAttribute(attributeName),
  678. attributeValue))
  679. {
  680. return child;
  681. } // if
  682. // And if doesn't match then just look (recursively) at his children
  683. XmlNode childOfChild = child.GetChild(childName, attributeName,
  684. attributeValue);
  685. if (childOfChild != null)
  686. {
  687. return childOfChild;
  688. } // if
  689. } // for
  690. // If we don't have find one, then there isn't one...
  691. return null;
  692. }
  693. /// <summary>
  694. /// Look for a node with the name specified as parameter and which
  695. /// contains two particular attributes. This function is thought for when
  696. /// we have several nodes with the same name to allow the user to
  697. /// get one with a concrete attribute. It returns the first node which
  698. /// matches the attribute names and values.
  699. /// Note: The search is recursively.
  700. /// </summary>
  701. /// <param name="childName">
  702. /// Node name to search, can be null for all kind of Xml child nodes.
  703. /// </param>
  704. /// <param name="attribute1Name">First attribute to search</param>
  705. /// <param name="attribute1Value">First attribute value to search</param>
  706. /// <param name="attribute2Name">Second attribute to search</param>
  707. /// <param name="attribute2Value">Second attribute value to search</param>
  708. /// <returns>Found child node or null if nothing was found</returns>
  709. public XmlNode GetChild(string childName, string attribute1Name,
  710. string attribute1Value, string attribute2Name, string attribute2Value)
  711. {
  712. // If there is no node name given, then we iterate over all child nodes.
  713. bool skipNameCheck = String.IsNullOrEmpty(childName);
  714. // Just iterate over all childs
  715. for (int i = 0; i < Children.Length; i++)
  716. {
  717. // At first directly look for the first one where the name matches
  718. XmlNode child = Children[i];
  719. if ((skipNameCheck ||
  720. StringHelper.Compare(child.Name, childName)) &&
  721. StringHelper.Compare(child.GetAttribute(attribute1Name),
  722. attribute1Value) &&
  723. StringHelper.Compare(child.GetAttribute(attribute2Name),
  724. attribute2Value))
  725. {
  726. return child;
  727. }
  728. // And if doesn't match then just look (recursively) at his children
  729. XmlNode childOfChild = child.GetChild(childName, attribute1Name,
  730. attribute1Value, attribute2Name, attribute2Value);
  731. if (childOfChild != null)
  732. {
  733. return childOfChild;
  734. }
  735. }
  736. // If we don't have find one, then there isn't one.
  737. return null;
  738. }
  739. /// <summary>
  740. /// Look for a node with the name specified as parameter and which
  741. /// contains two particular attributes. This function is thought for when
  742. /// we have several nodes with the same name to allow the user to
  743. /// get one with a concrete attribute. It returns the first node which
  744. /// matches all the attribute names and values.
  745. /// Note: The search is recursively.
  746. /// </summary>
  747. /// <param name="childName">
  748. /// Node name to search, can be null for all kind of Xml child nodes.
  749. /// </param>
  750. /// <param name="attribute1Name">First attribute to search</param>
  751. /// <param name="attribute1Value">First attribute value to search</param>
  752. /// <param name="attribute2Name">Second attribute to search</param>
  753. /// <param name="attribute2Value">Second attribute value to search</param>
  754. /// <param name="attribute3Name">Third attribute to search</param>
  755. /// <param name="attribute3Value">Third attribute value to search</param>
  756. /// <returns>Found child node or null if nothing was found</returns>
  757. public XmlNode GetChild(string childName, string attribute1Name,
  758. string attribute1Value, string attribute2Name, string attribute2Value,
  759. string attribute3Name, string attribute3Value)
  760. {
  761. // If there is no node name given, then we iterate over all child nodes.
  762. bool skipNameCheck = String.IsNullOrEmpty(childName);
  763. // Just iterate over all childs
  764. for (int i = 0; i < Children.Length; i++)
  765. {
  766. // At first directly look for the first one where the name matches
  767. XmlNode child = Children[i];
  768. if ((skipNameCheck ||
  769. StringHelper.Compare(child.Name, childName)) &&
  770. StringHelper.Compare(child.GetAttribute(attribute1Name),
  771. attribute1Value) &&
  772. StringHelper.Compare(child.GetAttribute(attribute2Name),
  773. attribute2Value) &&
  774. StringHelper.Compare(child.GetAttribute(attribute3Name),
  775. attribute3Value))
  776. {
  777. return child;
  778. }
  779. // And if doesn't match then just look (recursively) at his children
  780. XmlNode childOfChild = child.GetChild(childName,
  781. attribute1Name, attribute1Value,
  782. attribute2Name, attribute2Value,
  783. attribute3Name, attribute3Value);
  784. if (childOfChild != null)
  785. {
  786. return childOfChild;
  787. }
  788. }
  789. // If we don't have find one, then there isn't one.
  790. return null;
  791. }
  792. #endregion
  793. #region GetChildCount (Public)
  794. /// <summary>
  795. /// Gets the number of child that match with the given name.
  796. /// </summary>
  797. /// <param name="childName">Child name</param>
  798. /// <returns>number</returns>
  799. public int GetChildCount(string childName)
  800. {
  801. int foundCount = 0;
  802. for (int i = 0; i < Children.Length; i++)
  803. {
  804. // Check this case-insensitive
  805. if (StringHelper.Compare(Children[i].Name, childName))
  806. {
  807. foundCount++;
  808. }
  809. }
  810. return foundCount;
  811. }
  812. #endregion
  813. #region GetTotalNodeCount
  814. /// <summary>
  815. /// Helper method to get the total count of nodes inclusive all child
  816. /// nodes.
  817. /// </summary>
  818. /// <returns>Number of nodes, always at least one (this node)</returns>
  819. public int GetTotalNodeCount()
  820. {
  821. int ret = 1;
  822. for (int index = 0; index < Children.Length; index++)
  823. {
  824. ret += Children[index].GetTotalNodeCount();
  825. }
  826. return ret;
  827. }
  828. #endregion
  829. #region GetLastChild (Public)
  830. /// <summary>
  831. /// Gets last child that matches with the given name.
  832. /// Note: The search is recursively.
  833. /// </summary>
  834. /// <param name="childName">Child name</param>
  835. public XmlNode GetLastChild(string childName)
  836. {
  837. // Just iterate over all (directly) childs
  838. for (int i = Children.Length - 1; i >= 0; i--)
  839. {
  840. // At first directly look for the first one where the name matches
  841. XmlNode child = Children[i];
  842. // Check this case-insensitive
  843. if (StringHelper.Compare(child.Name, childName))
  844. {
  845. return child;
  846. }
  847. // and if doesn't match then just look (recursively) at his childs
  848. else
  849. {
  850. XmlNode childOfChild = child.GetLastChild(childName);
  851. if (childOfChild != null)
  852. {
  853. return childOfChild;
  854. }
  855. }
  856. }
  857. // If we don't have find one, then there isn't one...
  858. return null;
  859. }
  860. /// <summary>
  861. /// Gets last child that matches with the given attribute name and value.
  862. /// Note: The search is recursively.
  863. /// </summary>
  864. /// <param name="attributeName">Attribute name</param>
  865. /// <param name="attributeValue">Attribute value</param>
  866. /// <returns>XmlNode of the last child or null if not found.</returns>
  867. public XmlNode GetLastChild(string attributeName, string attributeValue)
  868. {
  869. return GetLastChild(null, attributeName, attributeValue);
  870. }
  871. /// <summary>
  872. /// Gets last child that matches with the given name.
  873. /// Note: The search is recursively.
  874. /// </summary>
  875. /// <param name="childName">Node name to search</param>
  876. /// <param name="attributeName">Attribute to search</param>
  877. /// <param name="attributeValue">Attribute value to search</param>
  878. /// <returns>XmlNode of the last child or null if not found.</returns>
  879. public XmlNode GetLastChild(string childName, string attributeName,
  880. string attributeValue)
  881. {
  882. // If there is no node name given, then we iterate over all child nodes...
  883. bool skipNameCheck = String.IsNullOrEmpty(childName);
  884. // Just iterate over all childs
  885. for (int i = Children.Length - 1; i >= 0; i--)
  886. {
  887. // At first directly look for the first one where the name matches
  888. XmlNode child = Children[i];
  889. // Check this case-insensitive
  890. if ((skipNameCheck ||
  891. StringHelper.Compare(child.Name, childName)) &&
  892. child.GetAttribute(attributeName) == attributeValue)
  893. {
  894. return child;
  895. }
  896. // And if doesn't match then just look (recursively) at his childs
  897. XmlNode childOfChild = child.GetLastChild(childName, attributeName,
  898. attributeValue);
  899. if (childOfChild != null)
  900. {
  901. return childOfChild;
  902. }
  903. }
  904. // If we don't have find one, then there isn't one...
  905. return null;
  906. }
  907. #endregion
  908. #region GetAllChildren (Public)
  909. /// <summary>
  910. /// Look for all nodes with the name specified as parameter and returns
  911. /// them in an array (non recursive implementation yet).
  912. /// <para />
  913. /// <b>Note:</b> Will always return a valid array, in worst case just an
  914. /// empty one.
  915. /// </summary>
  916. /// <param name="childName"></param>
  917. /// <returns>
  918. /// Array of XmlNodes from all the children of this node.
  919. /// </returns>
  920. public XmlNode[] GetAllChildren(string childName)
  921. {
  922. // first see how many children with this name exist
  923. int count = GetChildCount(childName);
  924. // create array and set nodes
  925. var foundNodes = new XmlNode[count];
  926. for (int index = 0, j = 0; index < Children.Length; index++)
  927. {
  928. if (Children[index].Name == childName)
  929. {
  930. foundNodes[j++] = Children[index];
  931. }
  932. }
  933. return foundNodes;
  934. }
  935. #endregion
  936. #region RemoveChild (Public)
  937. /// <summary>
  938. /// Remove child node from this xml node. Basically just does a Children
  939. /// remove, but since Children is an array it is not that easy. Note:
  940. /// This will not check recursively, only a direct children is removed.
  941. /// </summary>
  942. /// <param name="childNodeToRemove">Child XmlNode we want to remove</param>
  943. /// <returns>True if a child node was removed, false otherwise</returns>
  944. public bool RemoveChild(XmlNode childNodeToRemove)
  945. {
  946. for (int childNum = 0; childNum < Children.Length; childNum++)
  947. {
  948. if (Children[childNum] == childNodeToRemove)
  949. {
  950. // Remove this children entry (only here we need to modify the
  951. // Children array, if nothing was found there is no reason to change)
  952. childNodeToRemove.xNodeRoot.Remove();
  953. var newChildren = new List<XmlNode>();
  954. foreach (XmlNode child in Children)
  955. {
  956. if (child != childNodeToRemove)
  957. {
  958. newChildren.Add(child);
  959. }
  960. }
  961. Children = newChildren.ToArray();
  962. // Found children and removed it!
  963. return true;
  964. }
  965. }
  966. // No children to remove was found
  967. return false;
  968. }
  969. #endregion
  970. #region RemoveChildRecursively (Public)
  971. /// <summary>
  972. /// Remove xml child node recursively and return if we found and removed
  973. /// the given child node.
  974. /// </summary>
  975. /// <param name="childNodeToRemove">Child XmlNode we want to remove</param>
  976. /// <returns>True if a child node was removed, false otherwise</returns>
  977. public bool RemoveChildRecursively(XmlNode childNodeToRemove)
  978. {
  979. if (RemoveChild(childNodeToRemove))
  980. {
  981. return true;
  982. }
  983. // Child not found here, try children, but only if they have children too
  984. foreach (XmlNode child in Children)
  985. {
  986. if (child.Children.Length > 0)
  987. {
  988. bool removedChild = child.RemoveChildRecursively(childNodeToRemove);
  989. if (removedChild)
  990. {
  991. return true;
  992. }
  993. }
  994. }
  995. // Not found, then nothing was removed
  996. return false;
  997. }
  998. #endregion
  999. #region RemoveAllChildren (Public)
  1000. /// <summary>
  1001. /// Removes all children with given childName. (Not recursively)
  1002. /// </summary>
  1003. /// <param name="childName">child name</param>
  1004. /// <returns>Number of removed children</returns>
  1005. public int RemoveAllChildren(string childName)
  1006. {
  1007. int removedChildren = 0;
  1008. int childCount = Children.Length;
  1009. var survivingChildren = new List<XmlNode>();
  1010. for (int index = 0; index < childCount; index++)
  1011. {
  1012. if (Children[index].Name != childName)
  1013. {
  1014. survivingChildren.Add(Children[index]);
  1015. }
  1016. else
  1017. {
  1018. Children[index].xNodeRoot.Remove();
  1019. removedChildren++;
  1020. }
  1021. }
  1022. Children = survivingChildren.ToArray();
  1023. return removedChildren;
  1024. }
  1025. #endregion
  1026. #region RemoveAllAttributes (Public)
  1027. /// <summary>
  1028. /// Removes all attributes of this node (Not recursively).
  1029. /// </summary>
  1030. /// <returns>Number of removed attributes</returns>
  1031. public void RemoveAllAttributes()
  1032. {
  1033. xNodeRoot.RemoveAttributes();
  1034. }
  1035. #endregion
  1036. #region Remove (Public)
  1037. /// <summary>
  1038. /// Remove this child node from its parent. Useful to unlink stuff.
  1039. /// </summary>
  1040. public void Remove()
  1041. {
  1042. if (Parent != null)
  1043. {
  1044. Parent.RemoveChild(this);
  1045. }
  1046. }
  1047. #endregion
  1048. #region GetWholeDocumentXmlText (Public)
  1049. /// <summary>
  1050. /// Get the whole xml document as formatted xml text.
  1051. /// </summary>
  1052. public string GetWholeDocumentXmlText()
  1053. {
  1054. return xNodeRoot.Document.ToString();
  1055. }
  1056. #endregion
  1057. #region GetThisNodeXmlText (Public)
  1058. /// <summary>
  1059. /// Returns the xml text from this node and its children. Use
  1060. /// GetWholeDocumentXmlText to get the xml of the whole file or use
  1061. /// the Name property and GetAttributes for just this node without the
  1062. /// children (this will still return all the children and if we are at
  1063. /// the document level, this returns the same as GetWholeDocumentXmlText.
  1064. /// </summary>
  1065. public string GetThisNodeXmlText()
  1066. {
  1067. return xNodeRoot.ToString();
  1068. }
  1069. #endregion
  1070. #region Save (Public)
  1071. /// <summary>
  1072. /// Save whole xml file (does not matter from which xml node we call this).
  1073. /// In case saving is not possible this method will output a warning in
  1074. /// the log and return false, but does not throw an exception.
  1075. /// </summary>
  1076. /// <param name="filePath">Where to save the ContentMetaData.xml</param>
  1077. /// <returns>
  1078. /// True if saving the file was successful, false (and a log warning)
  1079. /// otherwise.
  1080. /// </returns>
  1081. public bool Save(string filePath)
  1082. {
  1083. try
  1084. {
  1085. using (FileStream stream = FileHelper.Open(
  1086. filePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
  1087. {
  1088. if (stream == null)
  1089. {
  1090. return false;
  1091. }
  1092. var fileWriter = new StreamWriter(stream, Encoding.UTF8);
  1093. xNodeRoot.Document.Save(fileWriter);
  1094. fileWriter.Flush();
  1095. }
  1096. return true;
  1097. }
  1098. catch (Exception ex)
  1099. {
  1100. Log.Warning("Failed to save '" + filePath + "': " + ex);
  1101. return false;
  1102. }
  1103. }
  1104. /// <summary>
  1105. /// Save whole xml file (does not matter from which xml node we call this)
  1106. /// into a memory stream, which contains the same data as if we would have
  1107. /// saved this to a file with the other Save overload. Can be loaded again
  1108. /// with FromMemoryStream.
  1109. /// </summary>
  1110. /// <returns>
  1111. /// The newly created memory stream containing the saved UTF8 encoded xml.
  1112. /// </returns>
  1113. public MemoryStream Save()
  1114. {
  1115. try
  1116. {
  1117. var xmlStream = new MemoryStream();
  1118. var fileWriter = new StreamWriter(xmlStream,
  1119. Encoding.UTF8);
  1120. xNodeRoot.Document.Save(fileWriter);
  1121. // And return the result
  1122. return xmlStream;
  1123. }
  1124. catch (Exception ex)
  1125. {
  1126. Log.Warning("Failed to save xml to memory stream: " + ex);
  1127. return new MemoryStream();
  1128. }
  1129. }
  1130. #endregion
  1131. #region ToString (Public)
  1132. /// <summary>
  1133. /// To string, will return a limited string with max. 200 characters to
  1134. /// get an overview what this XmlNode is about. Use GetXmlText to grab
  1135. /// the full xml text.
  1136. /// </summary>
  1137. public override string ToString()
  1138. {
  1139. return "XmlNode=" + Name + ": " +
  1140. GetThisNodeXmlText().MaxStringLength(200,
  1141. StringHelper.CutModes.EndWithDots);
  1142. }
  1143. #endregion
  1144. #region Methods (Private)
  1145. #region ConstructName
  1146. /// <summary>
  1147. /// if WillChildInheritNamespace is true, it applies the parent
  1148. /// namespace to given name, ignoring any given namespace.
  1149. /// </summary>
  1150. /// <param name="name">name</param>
  1151. /// <returns>Namespace name if this child inherits it, otherwise name
  1152. /// </returns>
  1153. private string ConstructName(string name)
  1154. {
  1155. if (WillChildInheritNamespace)
  1156. {
  1157. //Enforce parent namespace
  1158. name = GetNamespaceName(xNodeRoot.Name.Namespace,
  1159. XName.Get(name).LocalName);
  1160. }
  1161. return name;
  1162. }
  1163. #endregion
  1164. #region FindFirstDescendantAttribute
  1165. /// <summary>
  1166. /// Find the first descendant attribute in a list of Elements. It
  1167. /// searchs in depth through the descendant nodes, searching for a
  1168. /// coincidence with the name. The first match stops inmediatly the
  1169. /// depth-search and returns the attribute.
  1170. /// </summary>
  1171. /// <param name="Elements">Elements to proceed with the search</param>
  1172. /// <param name="name">Name of the searched attribute</param>
  1173. /// <returns>null</returns>
  1174. private XAttribute FindFirstDescendantAttribute(
  1175. IEnumerable<XElement> Elements, string name)
  1176. {
  1177. // Iterates through all the list
  1178. foreach (XElement element in Elements)
  1179. {
  1180. // It is possible that each element node contains more than one
  1181. // attribute
  1182. // Get the first attribute which matchs with the specified name
  1183. var attributes =
  1184. new List<XAttribute>(element.Attributes());
  1185. for (int i = 0; i < attributes.Count; i++)
  1186. {
  1187. // As specified in the description, it is always returned the
  1188. // first match in the attribute list.
  1189. if (attributes[i].Name.LocalName == name)
  1190. {
  1191. return attributes[i];
  1192. }
  1193. }
  1194. // If it isn't found then call recursively the function until the
  1195. // success or end of branch.
  1196. XAttribute attr =
  1197. FindFirstDescendantAttribute(element.Descendants(), name);
  1198. if (attr != null)
  1199. {
  1200. return attr;
  1201. }
  1202. }
  1203. return null;
  1204. }
  1205. #endregion
  1206. #endregion
  1207. /// <summary>
  1208. /// Tests
  1209. /// </summary>
  1210. internal class XmlNodeTests
  1211. {
  1212. #region Helpers
  1213. #region TestXmlDocumentPath
  1214. private const string TestXmlDocumentPath =
  1215. @"C:\Windows\System32\icsxml\ipcfg.xml";
  1216. #endregion
  1217. #region TestXmlText
  1218. private const string TestXmlText =
  1219. @"
  1220. <Root xmlns='urn:schemas-upnp-org:service-1-0' testAttribute='test'>
  1221. <Customers CustomerID='ANTON' Age='31'>
  1222. <CompanyName>Antonio Moreno Taquería</CompanyName>
  1223. <ContactName>Antonio Moreno</ContactName>
  1224. <ContactTitle>Owner</ContactTitle>
  1225. <Dead>true</Dead>
  1226. <Phone>(5) 555-3932</Phone>
  1227. <FullAddress>
  1228. <Address>Mataderos 2312</Address>
  1229. <City>México D.F.</City>
  1230. <PostalCode>05023</PostalCode>
  1231. <Country>Mexico</Country>
  1232. </FullAddress>
  1233. <TestGetXmlAttribute TestingID='ANTON' />
  1234. </Customers>
  1235. <Customers CustomerID='ALFKI'>
  1236. <CompanyName>Alfreds Futterkiste</CompanyName>
  1237. <ContactName>Maria Anders</ContactName>
  1238. <ContactTitle>Sales Representative</ContactTitle>
  1239. <Phone>030-0074321</Phone>
  1240. <Fax>030-0076545</Fax>
  1241. <FullAddress>
  1242. <Address>Obere Str. 57</Address>
  1243. <City>Berlin</City>
  1244. <PostalCode>12209</PostalCode>
  1245. <Country>Germany</Country>
  1246. </FullAddress>
  1247. </Customers>
  1248. </Root>";
  1249. #endregion
  1250. #region testRootNode
  1251. /// <summary>
  1252. /// Test root node
  1253. /// </summary>
  1254. private static XmlNode testRootNode;
  1255. #endregion
  1256. #region TestXml
  1257. /// <summary>
  1258. /// Test xml
  1259. /// </summary>
  1260. public static XmlNode TestXml
  1261. {
  1262. get
  1263. {
  1264. //while (unitTestData.XmlData == null)
  1265. //{
  1266. // // Just wait for data
  1267. // System.Threading.Thread.Sleep(0);
  1268. //}
  1269. //return unitTestData.XmlData as DeltaXmlNode;
  1270. if (testRootNode == null)
  1271. {
  1272. /*obs, creating a file is baaaaaaad and slow and poluting!
  1273. FileHelper.CreateTextFile("Customers.xml", TestXmlText);
  1274. testRootNode = XmlHelper.LoadFile("Customers.xml");
  1275. */
  1276. testRootNode = XmlNode.FromSnippet(TestXmlText);
  1277. }
  1278. return testRootNode;
  1279. }
  1280. }
  1281. #endregion
  1282. #region FromSnippet
  1283. public static void FromSnippet()
  1284. {
  1285. XmlNode xml = XmlNode.FromSnippet(TestXmlText);
  1286. Assert.Equal("Root", xml.Name);
  1287. // And convert it again with help of GetXmlText
  1288. xml = XmlNode.FromSnippet(xml.GetWholeDocumentXmlText());
  1289. Assert.Equal("Root", xml.Name);
  1290. }
  1291. #endregion
  1292. #endregion
  1293. #region CreateXmlNode (Static)
  1294. /// <summary>
  1295. /// Create xml node. Note: Too slow for a dynamic unit test.
  1296. /// </summary>
  1297. [Test]
  1298. public static void CreateXmlNode()
  1299. {
  1300. // We only need to load a xml file sample and check that the
  1301. // properties are the expected one.
  1302. XmlNode rootNode = TestXml;
  1303. Assert.NotNull(rootNode);
  1304. Assert.Equal("Root", rootNode.Name);
  1305. }
  1306. #endregion
  1307. #region FromFile (Static)
  1308. /// <summary>
  1309. /// Load file. Note: Too slow for a dynamic unit test.
  1310. /// </summary>
  1311. [Test]
  1312. public static void FromFile()
  1313. {
  1314. Assert.NotNull(XmlNode.FromFile(TestXmlDocumentPath));
  1315. }
  1316. #endregion
  1317. #region RemoveAllChildren (Static)
  1318. /// <summary>
  1319. /// Remove all children
  1320. /// </summary>
  1321. [Test]
  1322. public static void RemoveAllChildren()
  1323. {
  1324. // First load a xml file
  1325. XmlNode rootNode = TestXml;
  1326. // Retrieve first contactName, and make sure it has the right value
  1327. Assert.Equal("Antonio Moreno", rootNode.GetChild("ContactName").Value);
  1328. // Retrieve first Customers node, and remove all ContactName nodes
  1329. XmlNode customerNode = rootNode.GetChild("Customers");
  1330. customerNode.RemoveAllChildren("ContactName");
  1331. // Now check first contactName again
  1332. Assert.Equal("Maria Anders", rootNode.GetChild("ContactName").Value);
  1333. // Check the result, the first customer Antonio should not have
  1334. // a ContactName anymore.
  1335. Log.Test("Resulting xml=" + rootNode.GetThisNodeXmlText());
  1336. }
  1337. #endregion
  1338. #region RemoveChild (Static)
  1339. /// <summary>
  1340. /// Test the RemoveChild functionality
  1341. /// </summary>
  1342. [Test]
  1343. public static void RemoveChild()
  1344. {
  1345. XmlNode rootNode = XmlNode.FromSnippet("<BlaBla><Blub /></BlaBla>");
  1346. Log.Test("Initial xml=" + rootNode.GetThisNodeXmlText());
  1347. Assert.Equal(1, rootNode.Children.Length);
  1348. rootNode.RemoveChild(rootNode.Children[0]);
  1349. Assert.Equal(0, rootNode.Children.Length);
  1350. Log.Test("Resulting xml=" + rootNode.GetThisNodeXmlText());
  1351. // Does it also work when we add something with a string?
  1352. rootNode.AddChild("Blub");
  1353. Log.Test("Added blub xml=" + rootNode.GetThisNodeXmlText());
  1354. //rootNode.RemoveChild(rootNode.Children[0]);
  1355. rootNode.Children[0].Remove();
  1356. Log.Test("Removed blub again xml=" + rootNode.GetThisNodeXmlText());
  1357. // Next check if the same works when constructing XmlNodes
  1358. XmlNode newNode = XmlNode.FromSnippet("<HiThere />");
  1359. rootNode.AddChild(newNode);
  1360. Log.Test("Added HiThere XmlNode=" + rootNode.GetThisNodeXmlText());
  1361. rootNode.Children[0].Remove();
  1362. Log.Test("Final xml=" + rootNode.GetThisNodeXmlText());
  1363. }
  1364. #endregion
  1365. #region GetXmlAttribute (Static)
  1366. /// <summary>
  1367. /// Get xml attribute.
  1368. /// </summary>
  1369. [Test]
  1370. public static void GetXmlAttribute()
  1371. {
  1372. // First load a xml file
  1373. XmlNode rootNode = TestXml;
  1374. Assert.NotNull(rootNode);
  1375. // Lets get an attribute with and without child search. The first test
  1376. // is expected to get a empty string since there aren't any matches.
  1377. string attribute = rootNode.GetAttribute("TestingID", false);
  1378. Assert.Equal("", attribute);
  1379. // The second test is expected to get any result.
  1380. attribute = rootNode.GetAttribute("TestingID", true);
  1381. Assert.Equal("ANTON", attribute);
  1382. }
  1383. #endregion
  1384. #region ReplaceAttribute (Static)
  1385. /// <summary>
  1386. /// Replace attribute.
  1387. /// </summary>
  1388. [Test]
  1389. public static void ReplaceAttribute()
  1390. {
  1391. // First load a xml file
  1392. XmlNode rootNode = TestXml;
  1393. Assert.NotNull(rootNode);
  1394. // Lets get an attribute
  1395. string attribute = rootNode.GetAttribute("testAttribute");
  1396. Assert.Equal("test", attribute);
  1397. // And change it
  1398. rootNode.AddAttribute("testAttribute", "new");
  1399. attribute = rootNode.GetAttribute("testAttribute");
  1400. Assert.Equal("new", attribute);
  1401. // Finally check output!
  1402. //seems to work fine: Log.Test(rootNode.GetWholeDocumentXmlText());
  1403. }
  1404. #endregion
  1405. #region GetChildNode (Static)
  1406. /// <summary>
  1407. /// Get child node. Note: Too slow for a dynamic unit test.
  1408. /// </summary>
  1409. [Test]
  1410. public static void GetChildNode()
  1411. {
  1412. // First load a xml file
  1413. XmlNode rootNode = TestXml;
  1414. // We will look for an child node and check its attribute in order to
  1415. // confirm that the search is working correctly. The first test is
  1416. // expected to get the first node which matches with the Customers tag.
  1417. XmlNode childNode1 = rootNode.GetChild("Customers");
  1418. Assert.NotNull(childNode1);
  1419. Assert.Equal("Customers", childNode1.Name);
  1420. Assert.Equal("ANTON", childNode1.GetAttribute("CustomerID"));
  1421. // The second test looks for an concrete node...
  1422. XmlNode childNode2 = rootNode.GetChild("Customers",
  1423. "CustomerID", "ALFKI");
  1424. Assert.NotNull(childNode2);
  1425. Assert.Equal("Customers", childNode2.Name);
  1426. Assert.Equal("ALFKI", childNode2.GetAttribute("CustomerID"));
  1427. }
  1428. #endregion
  1429. #region Parent (Static)
  1430. /// <summary>
  1431. /// Parent. Note: Too slow for a dynamic unit test.
  1432. /// </summary>
  1433. [Test]
  1434. public static void Parent()
  1435. {
  1436. Assert.NotNull(TestXml);
  1437. Assert.Null(TestXml.Parent);
  1438. Assert.NotEqual(0, TestXml.Children.Length);
  1439. XmlNode childOne = TestXml.Children[0];
  1440. Assert.NotNull(childOne);
  1441. Assert.NotNull(childOne.Parent);
  1442. Assert.Equal("Root", childOne.Parent.Name);
  1443. Assert.Equal(7, childOne.Children.Length);
  1444. XmlNode childChildSix = childOne.Children[5];
  1445. Assert.NotNull(childChildSix);
  1446. Assert.NotNull(childChildSix.Parent);
  1447. Assert.Equal("Customers", childChildSix.Parent.Name);
  1448. Assert.Equal(4, childChildSix.Children.Length);
  1449. XmlNode childChildChildOne = childChildSix.Children[0];
  1450. Assert.NotNull(childChildChildOne);
  1451. Assert.NotNull(childChildChildOne.Parent);
  1452. Assert.Equal("FullAddress", childChildChildOne.Parent.Name);
  1453. }
  1454. #endregion
  1455. #region Childs (Static)
  1456. /// <summary>
  1457. /// Get children. Note: Too slow for a dynamic unit test.
  1458. /// </summary>
  1459. [Test]
  1460. public static void Childs()
  1461. {
  1462. // First load a xml file
  1463. XmlNode rootNode = TestXml;
  1464. // The first test is expected to get the first node which matches
  1465. // with an concrete Customers tag.
  1466. XmlNode childNode = rootNode.GetChild("Customers",
  1467. "CustomerID", "ANTON");
  1468. Assert.NotNull(childNode);
  1469. // Now we can check if the property is working correctly by looking
  1470. // at the number of elements
  1471. Assert.Equal(7, childNode.Children.Length);
  1472. Assert.Equal("CompanyName", childNode.Children[0].Name);
  1473. Assert.Equal("ContactName", childNode.Children[1].Name);
  1474. Assert.Equal("ContactTitle", childNode.Children[2].Name);
  1475. Assert.Equal("Dead", childNode.Children[3].Name);
  1476. Assert.Equal("Phone", childNode.Children[4].Name);
  1477. Assert.Equal("FullAddress", childNode.Children[5].Name);
  1478. }
  1479. #endregion
  1480. #region GetValueAs (Static)
  1481. /// <summary>
  1482. /// Get xml Value. Note: Too slow for a dynamic unit test.
  1483. /// </summary>
  1484. [Test]
  1485. public static void GetValueAs()
  1486. {
  1487. // First load a xml file
  1488. XmlNode rootNode = TestXml;
  1489. Assert.NotNull(rootNode);
  1490. // Lets test the integer conversion function to get the value in the
  1491. // specified format.
  1492. XmlNode node = rootNode.GetChild("Customers", "CustomerID", "ANTON");
  1493. Assert.Equal(5023, node.GetChild("PostalCode").GetValueAs(100));
  1494. // We try to access a field which doesn't have the expected type
  1495. //this produces a warning, we don't want that:
  1496. //Assert.Equal(100, node.GetChild("ContactTitle").GetValueAs(100));
  1497. // Lets test the double conversion function to get the value in the
  1498. // specified format.
  1499. Assert.Equal(5023.0, node.GetChild("FullAddress").
  1500. GetChild("PostalCode").GetValueAs(125.5));
  1501. // We try to access a field which doesn't have the expected type
  1502. //Assert.Equal(100.0,
  1503. // node.GetChild("ContactTitle").GetValueAs(100.0));
  1504. // Lets test the float conversion function to get the value in the
  1505. // specified format.
  1506. Assert.Equal(5023.0f, node.GetChild("FullAddress").
  1507. GetChild("PostalCode").GetValueAs(125.5f));
  1508. // We try to access a field which doesn't have the expected type
  1509. //Assert.Equal(100.0f,
  1510. // node.GetChild("ContactTitle").GetValueAs(100.0f));
  1511. // Lets test the boolean conversion function to get the value in the
  1512. // specified format.
  1513. Assert.True(node.GetChild("Dead").GetValueAs(false));
  1514. // We try to access a field which doesn't have the expected type
  1515. //Assert.False(node.GetChild("ContactTitle").GetValueAs(false));
  1516. }
  1517. #endregion
  1518. #region GetAttributeAs (Static)
  1519. /// <summary>
  1520. /// Get attribute. Note: Too slow for a dynamic unit test.
  1521. /// </summary>
  1522. [Test]
  1523. public static void GetAttributeAs()
  1524. {
  1525. XmlNode antonNode = TestXml.GetChild("Customers", "CustomerID",
  1526. "ANTON");
  1527. Assert.NotNull(antonNode);
  1528. Assert.Equal(31, antonNode.GetAttributeAs("Age", 0));
  1529. Assert.NotNull(antonNode);
  1530. Assert.Equal(31, antonNode.GetAttributeAs("Age", 0.0));
  1531. Assert.NotNull(antonNode);
  1532. Assert.Equal(31, antonNode.GetAttributeAs("Age", 0.0f));
  1533. }
  1534. #endregion
  1535. #region WillChildInheritNamespace (Static)
  1536. /// <summary>
  1537. /// Get namespace name
  1538. /// </summary>
  1539. [Test]
  1540. public static void WillChildInheritNamespace()
  1541. {
  1542. XmlNode rootNode = TestXml;
  1543. //Retrieve NamespaceName of rootNode
  1544. string namespaceName = rootNode.xNodeRoot.Name.NamespaceName;
  1545. string testNamespaceName = "testNamespace";
  1546. string emptyNamespaceName = "";
  1547. //Enable WillChildInheritNamespace
  1548. rootNode.WillChildInheritNamespace = true;
  1549. //Add child, inherit namespace
  1550. XmlNode childNode = rootNode.AddChild("TestNode1");
  1551. Assert.Equal(childNode.xNodeRoot.Name.NamespaceName,
  1552. namespaceName);
  1553. Assert.True(childNode.WillChildInheritNamespace);
  1554. //Disable WillChildInheritNamespace
  1555. rootNode.WillChildInheritNamespace = false;
  1556. //Add child with different namespace
  1557. XmlNode childNodeNS = rootNode.AddChild(
  1558. GetNamespaceName(testNamespaceName, "TestNode2"));
  1559. Assert.Equal(childNodeNS.xNodeRoot.Name.NamespaceName,
  1560. testNamespaceName);
  1561. Assert.False(childNodeNS.WillChildInheritNamespace);
  1562. //Add child with empty namespace
  1563. childNodeNS = rootNode.AddChild(
  1564. GetNamespaceName(emptyNamespaceName, "TestNode3"));
  1565. Assert.Equal(childNodeNS.xNodeRoot.Name.NamespaceName,
  1566. emptyNamespaceName);
  1567. Assert.False(childNodeNS.WillChildInheritNamespace);
  1568. }
  1569. #endregion
  1570. }
  1571. }
  1572. }