PageRenderTime 266ms CodeModel.GetById 81ms app.highlight 127ms RepoModel.GetById 44ms app.codeStats 0ms

/Utilities/Xml/XmlNode.cs

#
C# | 1728 lines | 970 code | 156 blank | 602 comment | 73 complexity | 94db6e76e327e360636061ae7e6678ca MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file