| /******************************************************************************* | 
 |  * You may amend and distribute as you like, but don't remove this header! | 
 |  * | 
 |  * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. | 
 |  * See http://www.codeplex.com/EPPlus for details. | 
 |  * | 
 |  * Copyright (C) 2011  Jan Källman | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   | 
 |  * See the GNU Lesser General Public License for more details. | 
 |  * | 
 |  * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php | 
 |  * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html | 
 |  * | 
 |  * All code and executables are provided "as is" with no warranty either express or implied.  | 
 |  * The author accepts no liability for any damage or loss of business that this product may cause. | 
 |  * | 
 |  * Code change notes: | 
 |  *  | 
 |  * Author							Change						         Date | 
 |  * ****************************************************************************** | 
 |  * Jan Källman		    Initial Release		         2009-10-01 | 
 |  * Jan Källman		    License changed GPL-->LGPL 2011-12-27 | 
 |  * Eyal Seagull       Add "CreateComplexNode"    2012-04-03 | 
 |  * Eyal Seagull       Add "DeleteTopNode"        2012-04-13 | 
 |  *******************************************************************************/ | 
 |  | 
 | using System; | 
 | using System.Collections.Generic; | 
 | using System.Text; | 
 | using System.Xml; | 
 | using OfficeOpenXml.Style; | 
 | using System.Globalization; | 
 | using System.IO; | 
 | namespace OfficeOpenXml | 
 | { | 
 | 	/// <summary> | 
 | 	/// Help class containing XML functions.  | 
 | 	/// Can be Inherited  | 
 | 	/// </summary> | 
 | 	public abstract class XmlHelper | 
 | 	{ | 
 | 		internal delegate int ChangedEventHandler(StyleBase sender, Style.StyleChangeEventArgs e); | 
 |  | 
 | 		internal XmlHelper(XmlNamespaceManager nameSpaceManager) | 
 | 		{ | 
 | 			TopNode = null; | 
 | 			NameSpaceManager = nameSpaceManager; | 
 | 		} | 
 |  | 
 | 		internal XmlHelper(XmlNamespaceManager nameSpaceManager, XmlNode topNode) | 
 | 		{ | 
 | 			TopNode = topNode; | 
 | 			NameSpaceManager = nameSpaceManager; | 
 | 		} | 
 | 		//internal bool ChangedFlag; | 
 | 		internal XmlNamespaceManager NameSpaceManager { get; set; } | 
 | 		internal XmlNode TopNode { get; set; } | 
 | 		string[] _schemaNodeOrder = null; | 
 | 		/// <summary> | 
 | 		/// Schema order list | 
 | 		/// </summary> | 
 | 		internal string[] SchemaNodeOrder | 
 | 		{ | 
 | 			get | 
 | 			{ | 
 | 				return _schemaNodeOrder; | 
 | 			} | 
 | 			set | 
 | 			{ | 
 | 				_schemaNodeOrder = value; | 
 | 			} | 
 | 		} | 
 | 		internal XmlNode CreateNode(string path) | 
 | 		{ | 
 | 			if (path == "") | 
 | 				return TopNode; | 
 | 			else | 
 | 				return CreateNode(path, false); | 
 | 		} | 
 | 		internal XmlNode CreateNode(string path, bool insertFirst) | 
 | 		{ | 
 | 			XmlNode node = TopNode; | 
 | 			XmlNode prependNode = null; | 
 | 			foreach (string subPath in path.Split('/')) | 
 | 			{ | 
 | 				XmlNode subNode = node.SelectSingleNode(subPath, NameSpaceManager); | 
 | 				if (subNode == null) | 
 | 				{ | 
 | 					string nodeName; | 
 | 					string nodePrefix; | 
 |  | 
 | 					string nameSpaceURI = ""; | 
 | 					string[] nameSplit = subPath.Split(':'); | 
 |  | 
 | 					if (SchemaNodeOrder != null && subPath[0] != '@') | 
 | 					{ | 
 | 						insertFirst = false; | 
 | 						prependNode = GetPrependNode(subPath, node); | 
 | 					} | 
 |  | 
 | 					if (nameSplit.Length > 1) | 
 | 					{ | 
 | 						nodePrefix = nameSplit[0]; | 
 | 						if (nodePrefix[0] == '@') nodePrefix = nodePrefix.Substring(1, nodePrefix.Length - 1); | 
 | 						nameSpaceURI = NameSpaceManager.LookupNamespace(nodePrefix); | 
 | 						nodeName = nameSplit[1]; | 
 | 					} | 
 | 					else | 
 | 					{ | 
 | 						nodePrefix = ""; | 
 | 						nameSpaceURI = ""; | 
 | 						nodeName = nameSplit[0]; | 
 | 					} | 
 | 					if (subPath.StartsWith("@")) | 
 | 					{ | 
 | 						XmlAttribute addedAtt = node.OwnerDocument.CreateAttribute(subPath.Substring(1, subPath.Length - 1), nameSpaceURI);  //nameSpaceURI | 
 | 						node.Attributes.Append(addedAtt); | 
 | 					} | 
 | 					else | 
 | 					{ | 
 | 						if (nodePrefix == "") | 
 | 						{ | 
 | 							subNode = node.OwnerDocument.CreateElement(nodeName, nameSpaceURI); | 
 | 						} | 
 | 						else | 
 | 						{ | 
 | 							if (nodePrefix == "" || (node.OwnerDocument != null && node.OwnerDocument.DocumentElement != null && node.OwnerDocument.DocumentElement.NamespaceURI == nameSpaceURI && | 
 | 									node.OwnerDocument.DocumentElement.Prefix == "")) | 
 | 							{ | 
 | 								subNode = node.OwnerDocument.CreateElement(nodeName, nameSpaceURI); | 
 | 							} | 
 | 							else | 
 | 							{ | 
 | 								subNode = node.OwnerDocument.CreateElement(nodePrefix, nodeName, nameSpaceURI); | 
 | 							} | 
 | 						} | 
 | 						if (prependNode != null) | 
 | 						{ | 
 | 							node.InsertBefore(subNode, prependNode); | 
 | 							prependNode = null; | 
 | 						} | 
 | 						else if (insertFirst) | 
 | 						{ | 
 | 							node.PrependChild(subNode); | 
 | 						} | 
 | 						else | 
 | 						{ | 
 | 							node.AppendChild(subNode); | 
 | 						} | 
 | 					} | 
 | 				} | 
 | 				node = subNode; | 
 | 			} | 
 | 			return node; | 
 | 		} | 
 |  | 
 | 		/// <summary> | 
 | 		/// Options to insert a node in the XmlDocument | 
 | 		/// </summary> | 
 | 		internal enum eNodeInsertOrder | 
 | 		{ | 
 | 			/// <summary> | 
 | 			/// Insert as first node of "topNode" | 
 | 			/// </summary> | 
 | 			First, | 
 |  | 
 | 			/// <summary> | 
 | 			/// Insert as the last child of "topNode" | 
 | 			/// </summary> | 
 | 			Last, | 
 |  | 
 | 			/// <summary> | 
 | 			/// Insert after the "referenceNode" | 
 | 			/// </summary> | 
 | 			After, | 
 |  | 
 | 			/// <summary> | 
 | 			/// Insert before the "referenceNode" | 
 | 			/// </summary> | 
 | 			Before, | 
 |  | 
 | 			/// <summary> | 
 | 			/// Use the Schema List to insert in the right order. If the Schema list | 
 | 			/// is null or empty, consider "Last" as the selected option | 
 | 			/// </summary> | 
 | 			SchemaOrder | 
 | 		} | 
 |  | 
 | 		/// <summary> | 
 | 		/// Create a complex node. Insert the node according to SchemaOrder | 
 | 		/// using the TopNode as the parent | 
 | 		/// </summary> | 
 | 		/// <param name="path"></param> | 
 | 		/// <returns></returns> | 
 | 		internal XmlNode CreateComplexNode( | 
 | 			string path) | 
 | 		{ | 
 | 			return CreateComplexNode( | 
 | 				TopNode, | 
 | 				path, | 
 | 				eNodeInsertOrder.SchemaOrder, | 
 | 				null); | 
 | 		} | 
 |  | 
 | 		/// <summary> | 
 | 		/// Create a complex node. Insert the node according to the <paramref name="path"/> | 
 | 		/// using the <paramref name="topNode"/> as the parent | 
 | 		/// </summary> | 
 | 		/// <param name="topNode"></param> | 
 | 		/// <param name="path"></param> | 
 | 		/// <returns></returns> | 
 | 		internal XmlNode CreateComplexNode( | 
 | 			XmlNode topNode, | 
 | 			string path) | 
 | 		{ | 
 | 			return CreateComplexNode( | 
 | 				topNode, | 
 | 				path, | 
 | 				eNodeInsertOrder.SchemaOrder, | 
 | 				null); | 
 | 		} | 
 |  | 
 | 		/// <summary> | 
 | 		/// Creates complex XML nodes | 
 |     /// </summary> | 
 |     /// <remarks> | 
 | 		/// 1. "d:conditionalFormatting" | 
 | 		///		1.1. Creates/find the first "conditionalFormatting" node | 
 | 		///  | 
 | 		/// 2. "d:conditionalFormatting/@sqref" | 
 | 		///		2.1. Creates/find the first "conditionalFormatting" node | 
 | 		///		2.2. Creates (if not exists) the @sqref attribute | 
 | 		/// | 
 | 		/// 3. "d:conditionalFormatting/@id='7'/@sqref='A9:B99'" | 
 | 		///		3.1. Creates/find the first "conditionalFormatting" node | 
 | 		///		3.2. Creates/update its @id attribute to "7" | 
 | 		///		3.3. Creates/update its @sqref attribute to "A9:B99" | 
 | 		/// | 
 | 		/// 4. "d:conditionalFormatting[@id='7']/@sqref='X1:X5'" | 
 | 		///		4.1. Creates/find the first "conditionalFormatting" node with @id=7 | 
 | 		///		4.2. Creates/update its @sqref attribute to "X1:X5" | 
 | 		///	 | 
 | 		/// 5. "d:conditionalFormatting[@id='7']/@id='8'/@sqref='X1:X5'/d:cfRule/@id='AB'" | 
 | 		///		5.1. Creates/find the first "conditionalFormatting" node with @id=7 | 
 | 		///		5.2. Set its @id attribute to "8" | 
 | 		///		5.2. Creates/update its @sqref attribute and set it to "X1:X5" | 
 | 		///		5.3. Creates/find the first "cfRule" node (inside the node) | 
 | 		///		5.4. Creates/update its @id attribute to "AB" | 
 | 		///	 | 
 | 		/// 6. "d:cfRule/@id=''" | 
 | 		///		6.1. Creates/find the first "cfRule" node | 
 | 		///		6.1. Remove the @id attribute | 
 |     ///	</remarks> | 
 | 		/// <param name="topNode"></param> | 
 | 		/// <param name="path"></param> | 
 | 		/// <param name="nodeInsertOrder"></param> | 
 | 		/// <param name="referenceNode"></param> | 
 | 		/// <returns>The last node creates/found</returns> | 
 | 		internal XmlNode CreateComplexNode( | 
 | 			XmlNode topNode, | 
 | 			string path, | 
 | 			eNodeInsertOrder nodeInsertOrder, | 
 | 			XmlNode referenceNode) | 
 | 		{ | 
 | 			// Path is obrigatory | 
 | 			if ((path == null) || (path == string.Empty)) | 
 | 			{ | 
 | 				return topNode; | 
 | 			} | 
 |  | 
 | 			XmlNode node = topNode; | 
 | 			string nameSpaceURI = string.Empty; | 
 |  | 
 |       //TODO: BUG: when the "path" contains "/" in an attrribue value, it gives an error. | 
 |  | 
 | 			// Separate the XPath to Nodes and Attributes | 
 | 			foreach (string subPath in path.Split('/')) | 
 | 			{ | 
 | 				// The subPath can be any one of those: | 
 | 				// nodeName | 
 | 				// x:nodeName | 
 | 				// nodeName[find criteria] | 
 | 				// x:nodeName[find criteria] | 
 | 				// @attribute | 
 | 				// @attribute='attribute value' | 
 |  | 
 | 				// Check if the subPath has at least one character | 
 | 				if (subPath.Length > 0) | 
 | 				{ | 
 | 					// Check if the subPath is an attribute (with or without value) | 
 | 					if (subPath.StartsWith("@")) | 
 | 					{ | 
 | 						// @attribute										--> Create attribute | 
 | 						// @attribute=''								--> Remove attribute | 
 | 						// @attribute='attribute value' --> Create attribute + update value | 
 | 						string[] attributeSplit = subPath.Split('='); | 
 | 						string attributeName = attributeSplit[0].Substring(1, attributeSplit[0].Length - 1); | 
 | 						string attributeValue = null;	// Null means no attribute value | 
 |  | 
 | 						// Check if we have an attribute value to set | 
 | 						if (attributeSplit.Length > 1) | 
 | 						{ | 
 | 							// Remove the ' or " from the attribute value | 
 | 							attributeValue = attributeSplit[1].Replace("'", "").Replace("\"", ""); | 
 | 						} | 
 |  | 
 | 						// Get the attribute (if exists) | 
 | 						XmlAttribute attribute = (XmlAttribute)(node.Attributes.GetNamedItem(attributeName)); | 
 |  | 
 | 						// Remove the attribute if value is empty (not null) | 
 | 						if (attributeValue == string.Empty) | 
 | 						{ | 
 | 							// Only if the attribute exists | 
 | 							if (attribute != null) | 
 | 							{ | 
 | 								node.Attributes.Remove(attribute); | 
 | 							} | 
 | 						} | 
 | 						else | 
 | 						{ | 
 | 							// Create the attribue if does not exists | 
 | 							if (attribute == null) | 
 | 							{ | 
 | 								// Create the attribute | 
 | 								attribute = node.OwnerDocument.CreateAttribute( | 
 | 									attributeName); | 
 |  | 
 | 								// Add it to the current node | 
 | 								node.Attributes.Append(attribute); | 
 | 							} | 
 |  | 
 | 							// Update the attribute value | 
 | 							if (attributeValue != null) | 
 | 							{ | 
 | 								node.Attributes[attributeName].Value = attributeValue; | 
 | 							} | 
 | 						} | 
 | 					} | 
 | 					else | 
 | 					{ | 
 | 						// nodeName | 
 | 						// x:nodeName | 
 | 						// nodeName[find criteria] | 
 | 						// x:nodeName[find criteria] | 
 |  | 
 | 						// Look for the node (with or without filter criteria) | 
 | 						XmlNode subNode = node.SelectSingleNode(subPath, NameSpaceManager); | 
 |  | 
 | 						// Check if the node does not exists | 
 | 						if (subNode == null) | 
 | 						{ | 
 | 							string nodeName; | 
 | 							string nodePrefix; | 
 | 							string[] nameSplit = subPath.Split(':'); | 
 | 							nameSpaceURI = string.Empty; | 
 |  | 
 | 							// Check if the name has a prefix like "d:nodeName" | 
 | 							if (nameSplit.Length > 1) | 
 | 							{ | 
 | 								nodePrefix = nameSplit[0]; | 
 | 								nameSpaceURI = NameSpaceManager.LookupNamespace(nodePrefix); | 
 | 								nodeName = nameSplit[1]; | 
 | 							} | 
 | 							else | 
 | 							{ | 
 | 								nodePrefix = string.Empty; | 
 | 								nameSpaceURI = string.Empty; | 
 | 								nodeName = nameSplit[0]; | 
 | 							} | 
 |  | 
 | 							// Check if we have a criteria part in the node name | 
 | 							if (nodeName.IndexOf("[") > 0) | 
 | 							{ | 
 | 								// remove the criteria from the node name | 
 | 								nodeName = nodeName.Substring(0, nodeName.IndexOf("[")); | 
 | 							} | 
 |  | 
 | 							if (nodePrefix == string.Empty) | 
 | 							{ | 
 | 								subNode = node.OwnerDocument.CreateElement(nodeName, nameSpaceURI); | 
 | 							} | 
 | 							else | 
 | 							{ | 
 | 								if (node.OwnerDocument != null | 
 | 									&& node.OwnerDocument.DocumentElement != null | 
 | 									&& node.OwnerDocument.DocumentElement.NamespaceURI == nameSpaceURI | 
 | 									&& node.OwnerDocument.DocumentElement.Prefix == string.Empty) | 
 | 								{ | 
 | 									subNode = node.OwnerDocument.CreateElement( | 
 | 										nodeName, | 
 | 										nameSpaceURI); | 
 | 								} | 
 | 								else | 
 | 								{ | 
 | 									subNode = node.OwnerDocument.CreateElement( | 
 | 										nodePrefix, | 
 | 										nodeName, | 
 | 										nameSpaceURI); | 
 | 								} | 
 | 							} | 
 |  | 
 | 							// Check if we need to use the "SchemaOrder" | 
 | 							if (nodeInsertOrder == eNodeInsertOrder.SchemaOrder) | 
 | 							{ | 
 | 								// Check if the Schema Order List is empty | 
 | 								if ((SchemaNodeOrder == null) || (SchemaNodeOrder.Length == 0)) | 
 | 								{ | 
 | 									// Use the "Insert Last" option when Schema Order List is empty | 
 | 									nodeInsertOrder = eNodeInsertOrder.Last; | 
 | 								} | 
 | 								else | 
 | 								{ | 
 | 									// Find the prepend node in order to insert | 
 | 									referenceNode = GetPrependNode(nodeName, node); | 
 |  | 
 | 									if (referenceNode != null) | 
 | 									{ | 
 | 										nodeInsertOrder = eNodeInsertOrder.Before; | 
 | 									} | 
 | 									else | 
 | 									{ | 
 | 										nodeInsertOrder = eNodeInsertOrder.Last; | 
 | 									} | 
 | 								} | 
 | 							} | 
 |  | 
 | 							switch (nodeInsertOrder) | 
 | 							{ | 
 | 								case eNodeInsertOrder.After: | 
 | 									node.InsertAfter(subNode, referenceNode); | 
 |                                     referenceNode = null; | 
 |                                     break; | 
 |  | 
 | 								case eNodeInsertOrder.Before: | 
 | 									node.InsertBefore(subNode, referenceNode); | 
 |                                     referenceNode = null; | 
 | 									break; | 
 |  | 
 | 								case eNodeInsertOrder.First: | 
 | 									node.PrependChild(subNode); | 
 | 									break; | 
 |  | 
 | 								case eNodeInsertOrder.Last: | 
 | 									node.AppendChild(subNode); | 
 | 									break; | 
 | 							} | 
 | 						} | 
 |  | 
 | 						// Make the newly created node the top node when the rest of the path | 
 | 						// is being evaluated. So newly created nodes will be the children of the | 
 | 						// one we just created. | 
 | 						node = subNode; | 
 | 					} | 
 | 				} | 
 | 			} | 
 |  | 
 | 			// Return the last created/found node | 
 | 			return node; | 
 | 		} | 
 |  | 
 | 		/// <summary> | 
 | 		/// return Prepend node | 
 | 		/// </summary> | 
 | 		/// <param name="nodeName">name of the node to check</param> | 
 | 		/// <param name="node">Topnode to check children</param> | 
 | 		/// <returns></returns> | 
 | 		private XmlNode GetPrependNode(string nodeName, XmlNode node) | 
 | 		{ | 
 | 			int pos = GetNodePos(nodeName); | 
 | 			if (pos < 0) | 
 | 			{ | 
 | 				return null; | 
 | 			} | 
 | 			XmlNode prependNode = null; | 
 | 			foreach (XmlNode childNode in node.ChildNodes) | 
 | 			{ | 
 | 				int childPos = GetNodePos(childNode.Name); | 
 | 				if (childPos > -1)  //Found? | 
 | 				{ | 
 | 					if (childPos > pos) //Position is before | 
 | 					{ | 
 | 						prependNode = childNode; | 
 | 						break; | 
 | 					} | 
 | 				} | 
 | 			} | 
 | 			return prependNode; | 
 | 		} | 
 | 		private int GetNodePos(string nodeName) | 
 | 		{ | 
 | 			int ix = nodeName.IndexOf(":"); | 
 | 			if (ix > 0) | 
 | 			{ | 
 | 				nodeName = nodeName.Substring(ix + 1, nodeName.Length - (ix + 1)); | 
 | 			} | 
 | 			for (int i = 0; i < _schemaNodeOrder.Length; i++) | 
 | 			{ | 
 | 				if (nodeName == _schemaNodeOrder[i]) | 
 | 				{ | 
 | 					return i; | 
 | 				} | 
 | 			} | 
 | 			return -1; | 
 | 		} | 
 | 		internal void DeleteAllNode(string path) | 
 | 		{ | 
 | 			string[] split = path.Split('/'); | 
 | 			XmlNode node = TopNode; | 
 | 			foreach (string s in split) | 
 | 			{ | 
 | 				node = node.SelectSingleNode(s, NameSpaceManager); | 
 | 				if (node != null) | 
 | 				{ | 
 | 					if (node is XmlAttribute) | 
 | 					{ | 
 | 						(node as XmlAttribute).OwnerElement.Attributes.Remove(node as XmlAttribute); | 
 | 					} | 
 | 					else | 
 | 					{ | 
 | 						node.ParentNode.RemoveChild(node); | 
 | 					} | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					break; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		internal void DeleteNode(string path) | 
 | 		{ | 
 | 			var node = TopNode.SelectSingleNode(path, NameSpaceManager); | 
 | 			if (node != null) | 
 | 			{ | 
 | 				if (node is XmlAttribute) | 
 | 				{ | 
 | 					var att = (XmlAttribute)node; | 
 | 					att.OwnerElement.Attributes.Remove(att); | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					node.ParentNode.RemoveChild(node); | 
 | 				} | 
 | 			} | 
 | 		} | 
 |     internal void DeleteTopNode() | 
 |     { | 
 |       TopNode.ParentNode.RemoveChild(TopNode); | 
 |     } | 
 | 		internal void SetXmlNodeString(string path, string value) | 
 | 		{ | 
 | 			SetXmlNodeString(TopNode, path, value, false, false); | 
 | 		} | 
 | 		internal void SetXmlNodeString(string path, string value, bool removeIfBlank) | 
 | 		{ | 
 | 			SetXmlNodeString(TopNode, path, value, removeIfBlank, false); | 
 | 		} | 
 | 		internal void SetXmlNodeString(XmlNode node, string path, string value) | 
 | 		{ | 
 | 			SetXmlNodeString(node, path, value, false, false); | 
 | 		} | 
 | 		internal void SetXmlNodeString(XmlNode node, string path, string value, bool removeIfBlank) | 
 | 		{ | 
 | 			SetXmlNodeString(node, path, value, removeIfBlank, false); | 
 | 		} | 
 | 		internal void SetXmlNodeString(XmlNode node, string path, string value, bool removeIfBlank, bool insertFirst) | 
 | 		{ | 
 | 			if (node == null) | 
 | 			{ | 
 | 				return; | 
 | 			} | 
 | 			if (value == "" && removeIfBlank) | 
 | 			{ | 
 | 				DeleteAllNode(path); | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				XmlNode nameNode = node.SelectSingleNode(path, NameSpaceManager); | 
 | 				if (nameNode == null) | 
 | 				{ | 
 | 					CreateNode(path, insertFirst); | 
 | 					nameNode = node.SelectSingleNode(path, NameSpaceManager); | 
 | 				} | 
 | 				//if (nameNode.InnerText != value) HasChanged(); | 
 | 				nameNode.InnerText = value; | 
 | 			} | 
 | 		} | 
 | 		internal void SetXmlNodeBool(string path, bool value) | 
 | 		{ | 
 | 			SetXmlNodeString(TopNode, path, value ? "1" : "0", false, false); | 
 | 		} | 
 | 		internal void SetXmlNodeBool(string path, bool value, bool removeIf) | 
 | 		{ | 
 | 			if (value == removeIf) | 
 | 			{ | 
 | 				var node = TopNode.SelectSingleNode(path, NameSpaceManager); | 
 | 				if (node != null) | 
 | 				{ | 
 | 					if (node is XmlAttribute) | 
 | 					{ | 
 | 						var elem = (node as XmlAttribute).OwnerElement; | 
 | 						elem.ParentNode.RemoveChild(elem); | 
 | 					} | 
 | 					else | 
 | 					{ | 
 | 						TopNode.RemoveChild(node); | 
 | 					} | 
 | 				} | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				SetXmlNodeString(TopNode, path, value ? "1" : "0", false, false); | 
 | 			} | 
 | 		} | 
 | 		internal bool ExistNode(string path) | 
 | 		{ | 
 | 			if (TopNode == null || TopNode.SelectSingleNode(path, NameSpaceManager) == null) | 
 | 			{ | 
 | 				return false; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				return true; | 
 | 			} | 
 | 		} | 
 | 		internal bool? GetXmlNodeBoolNullable(string path) | 
 | 		{ | 
 | 			var value = GetXmlNodeString(path); | 
 | 			if (string.IsNullOrEmpty(value)) | 
 | 			{ | 
 | 				return null; | 
 | 			} | 
 | 			return GetXmlNodeBool(path); | 
 | 		} | 
 | 		internal bool GetXmlNodeBool(string path) | 
 | 		{ | 
 | 			return GetXmlNodeBool(path, false); | 
 | 		} | 
 | 		internal bool GetXmlNodeBool(string path, bool blankValue) | 
 | 		{ | 
 | 			string value = GetXmlNodeString(path); | 
 | 			if (value == "1" || value == "-1" || value.Equals("true",StringComparison.InvariantCultureIgnoreCase)) | 
 | 			{ | 
 | 				return true; | 
 | 			} | 
 | 			else if (value == "") | 
 | 			{ | 
 | 				return blankValue; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				return false; | 
 | 			} | 
 | 		} | 
 | 		internal int GetXmlNodeInt(string path) | 
 | 		{ | 
 | 			int i; | 
 | 			if (int.TryParse(GetXmlNodeString(path), out i)) | 
 | 			{ | 
 | 				return i; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				return int.MinValue; | 
 | 			} | 
 | 		} | 
 |         internal int? GetXmlNodeIntNull(string path) | 
 |         { | 
 |             int i; | 
 |             string s = GetXmlNodeString(path); | 
 |             if (s!="" && int.TryParse(s, out i)) | 
 |             { | 
 |                 return i; | 
 |             } | 
 |             else | 
 |             { | 
 |                 return null; | 
 |             } | 
 |         } | 
 |  | 
 | 		internal decimal GetXmlNodeDecimal(string path) | 
 | 		{ | 
 | 			decimal d; | 
 | 			if (decimal.TryParse(GetXmlNodeString(path), NumberStyles.Any, CultureInfo.InvariantCulture, out d)) | 
 | 			{ | 
 | 				return d; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				return 0; | 
 | 			} | 
 | 		} | 
 |         internal decimal? GetXmlNodeDecimalNull(string path) | 
 |         { | 
 |             decimal d; | 
 |             if (decimal.TryParse(GetXmlNodeString(path), NumberStyles.Any, CultureInfo.InvariantCulture, out d)) | 
 |             { | 
 |                 return d; | 
 |             } | 
 |             else | 
 |             { | 
 |                 return null; | 
 |             } | 
 |         } | 
 |         internal double? GetXmlNodeDoubleNull(string path) | 
 | 		{ | 
 | 			string s = GetXmlNodeString(path); | 
 | 			if (s == "") | 
 | 			{ | 
 | 				return null; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				double v; | 
 | 				if (double.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out v)) | 
 | 				{ | 
 | 					return v; | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					return null; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		internal double GetXmlNodeDouble(string path) | 
 | 		{ | 
 | 			string s = GetXmlNodeString(path); | 
 | 			if (s == "") | 
 | 			{ | 
 | 				return double.NaN; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				double v; | 
 | 				if (double.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out v)) | 
 | 				{ | 
 | 					return v; | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					return double.NaN; | 
 | 				} | 
 | 			} | 
 | 		} | 
 |     internal string GetXmlNodeString(XmlNode node, string path) | 
 |     { | 
 |       if (node == null) | 
 |       { | 
 |         return ""; | 
 |       } | 
 |  | 
 |       XmlNode nameNode = node.SelectSingleNode(path, NameSpaceManager); | 
 |  | 
 |       if (nameNode != null) | 
 |       { | 
 |         if (nameNode.NodeType == XmlNodeType.Attribute) | 
 |         { | 
 |           return nameNode.Value != null ? nameNode.Value : ""; | 
 |         } | 
 |         else | 
 |         { | 
 |           return nameNode.InnerText; | 
 |         } | 
 |       } | 
 |       else | 
 |       { | 
 |         return ""; | 
 |       } | 
 |     } | 
 | 		internal string GetXmlNodeString(string path) | 
 | 		{ | 
 |             return GetXmlNodeString(TopNode, path); | 
 | 		} | 
 | 		internal static Uri GetNewUri(Packaging.ZipPackage package, string sUri) | 
 | 		{ | 
 | 			return GetNewUri(package, sUri, 1); | 
 | 		} | 
 |         internal static Uri GetNewUri(Packaging.ZipPackage package, string sUri, int id) | 
 | 		{ | 
 | 			Uri uri; | 
 | 			do | 
 | 			{ | 
 | 				uri = new Uri(string.Format(sUri, id++), UriKind.Relative); | 
 | 			} | 
 | 			while (package.PartExists(uri)); | 
 | 			return uri; | 
 | 		} | 
 | 		/// <summary> | 
 | 		/// Insert the new node before any of the nodes in the comma separeted list | 
 | 		/// </summary> | 
 | 		/// <param name="parentNode">Parent node</param> | 
 | 		/// <param name="beforeNodes">comma separated list containing nodes to insert after. Left to right order</param> | 
 | 		/// <param name="newNode">The new node to be inserterd</param> | 
 | 		internal void InserAfter(XmlNode parentNode, string beforeNodes, XmlNode newNode) | 
 | 		{ | 
 | 			string[] nodePaths = beforeNodes.Split(','); | 
 |  | 
 | 			foreach (string nodePath in nodePaths) | 
 | 			{ | 
 | 				XmlNode node = parentNode.SelectSingleNode(nodePath, NameSpaceManager); | 
 | 				if (node != null) | 
 | 				{ | 
 | 					parentNode.InsertAfter(newNode, node); | 
 | 					return; | 
 | 				} | 
 | 			} | 
 | 			parentNode.InsertAfter(newNode, null); | 
 | 		} | 
 |         internal static void LoadXmlSafe(XmlDocument xmlDoc, Stream stream) | 
 |         { | 
 |             XmlReaderSettings settings = new XmlReaderSettings(); | 
 |             //Disable entity parsing (to aviod xmlbombs, External Entity Attacks etc). | 
 |             settings.ProhibitDtd = true; | 
 |             XmlReader reader = XmlReader.Create(stream, settings); | 
 |             xmlDoc.Load(reader); | 
 |         } | 
 |         internal static void LoadXmlSafe(XmlDocument xmlDoc, string xml, Encoding encoding) | 
 |         { | 
 |             var stream = new MemoryStream(encoding.GetBytes(xml)); | 
 |             LoadXmlSafe(xmlDoc, stream); | 
 |         } | 
 | 	} | 
 | } |