/* * Helpers.cs * Authors: Benjamin Nitschke (copied from various classes at abi.exdream.com) */ using System; using System.Collections.Generic; using System.Text; using System.Xml; using System.Globalization; using System.IO; namespace Pina3D { /// /// Xml helper. Virtually unchanged classes downloaded from abi.exdream.com, which much of his other code in this project utilizes. /// internal class XmlHelper { /// /// Private constructor to prevent instantiation. /// private XmlHelper() { } /// /// Get xml attribute with help of currently open xml reader and /// attributeName. If attribute does not exists an empty string is /// returned. /// /// Reader /// Attribute name /// String public static string GetXmlAttribute( XmlReader reader, string attributeName) { string ret = reader.GetAttribute(attributeName); return ret == null ? "" : ret; } /// /// Get xml int value, will return 0 if it does not exists or /// isn't an int. Will use the "value" attribute. /// /// Reader /// Float public static int GetXmlIntValue(XmlReader reader) { string str = reader.GetAttribute("value"); int ret = 0; //int.TryParse(str, out ret); if (StringHelper.IsNumericInt(str)) ret = Convert.ToInt32(str); return ret; } // GetXmlIntValue(reader) /// /// Get xml float value, will return 0 if it does not exists or /// isn't a float. Will use the "value" attribute. /// /// Reader /// Float public static float GetXmlFloatValue(XmlReader reader) { string str = reader.GetAttribute("value"); float ret = 0; //float.TryParse(str, out ret); if (StringHelper.IsNumericFloat(str)) ret = Convert.ToSingle(str, NumberFormatInfo.InvariantInfo); return ret; } /// /// Find node in the root level of the xml document (main nodes) /// /// Xml doc /// Xml node name /// Xml node public static XmlNode FindNodeInRoot(XmlDocument xmlDoc, string xmlNodeName) { if (xmlDoc == null) return null; foreach (XmlNode node in xmlDoc.ChildNodes) if (StringHelper.Compare(node.Name, xmlNodeName)) return node; // Not found return null; } /// /// Find node recursively in a xml document by its name. /// /// Xml document /// Xml node name /// Xml node or null if not found public static XmlNode FindNode(XmlNode xmlDoc, string xmlNodeName) { if (xmlDoc == null) return null; foreach (XmlNode node in xmlDoc.ChildNodes) { if (String.Compare(node.Name, xmlNodeName, true) == 0) return node; XmlNode foundChildNode = FindNode(node, xmlNodeName); if (foundChildNode != null) return foundChildNode; } // Not found at all. return null; } /// /// Get xml attribute, will return "" if not found. /// /// Node /// Attribute name /// String public static string GetXmlAttribute( XmlNode node, string attributeName) { if (node == null || node.Attributes == null) return ""; foreach (XmlNode attribute in node.Attributes) if (StringHelper.Compare(attribute.Name, attributeName)) return attribute.Value == null ? "" : attribute.Value; // Not found, just return empty string return ""; } /// /// Get xml int value, will return 0 if it does not exists or /// isn't an int. Will use the "value" attribute. /// /// Node /// Float public static int GetXmlIntValue(XmlNode node) { string str = GetXmlAttribute(node, "value"); int ret = 0; //int.TryParse(str, out ret); if (StringHelper.IsNumericInt(str)) ret = Convert.ToInt32(str); return ret; } /// /// Get xml float value, will return 0 if it does not exists or /// isn't a float. Will use the "value" attribute. /// /// Node /// Float public static float GetXmlFloatValue(XmlNode node) { string str = GetXmlAttribute(node, "value"); float ret = 0; //float.TryParse(str, out ret); if (StringHelper.IsNumericFloat(str)) ret = Convert.ToSingle(str, NumberFormatInfo.InvariantInfo); return ret; } /// /// Create xml document with declaration (version 1.0, utf-8). /// /// Created xml document public static XmlDocument CreateXmlDocumentWithDeclaration() { XmlDocument xmlDoc = new XmlDocument(); // Insert declaration as first element xmlDoc.InsertBefore( xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null), xmlDoc.DocumentElement); return xmlDoc; } /// /// Create parent node /// /// Xml doc /// Node name /// Xml node public static XmlNode CreateParentNode(XmlDocument xmlDoc, string nodeName) { XmlElement parentNode = xmlDoc.CreateElement(nodeName); xmlDoc.AppendChild(parentNode); return parentNode; } /// /// Create empty parent node named "ParentNode" on an empty xml document. /// /// Xml node public static XmlNode CreateEmptyParentNode() { // Create empty xml document XmlDocument xmlDoc = CreateXmlDocumentWithDeclaration(); // Create parent node, don't add it do xmlDoc, we dispose it anyway. return xmlDoc.CreateElement("ParentNode"); } /// /// Get xml document from any node, even the xml document itself. /// /// Some node /// Xml document public static XmlDocument GetXmlDocument(XmlNode someNode) { if (someNode == null) throw new ArgumentNullException("someNode", "Can't get xml document without valid node"); // Check if someNode is our XmlDocument XmlDocument xmlDoc = someNode as XmlDocument; // If not, use the OwnerDocument property if (xmlDoc == null) xmlDoc = someNode.OwnerDocument; return xmlDoc; } /// /// Create child node, will get xml document from parentNode. /// /// Parent node /// Child node name /// Xml node public static XmlNode CreateChildNode( XmlNode parentNode, string childNodeName) { // Create child node XmlNode childNode = GetXmlDocument(parentNode).CreateElement(childNodeName); // Add it to parent parentNode.AppendChild(childNode); // And return it return childNode; } /// /// Create node with attribute /// /// Xml document /// Node name /// Attribute name /// Attribute value /// Xml node public static XmlNode CreateNodeWithAttribute(XmlDocument xmlDoc, string nodeName, string attributeName, string attributeValue) { XmlElement node = xmlDoc.CreateElement(nodeName); XmlAttribute attribute = xmlDoc.CreateAttribute(attributeName); attribute.Value = attributeValue; node.Attributes.Append(attribute); return node; } /// /// Load xml from text and returns the first useable root (not the xml /// document). /// /// Xml text public static XmlNode LoadXmlFromText(string xmlText) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xmlText); return GetRootXmlNode(xmlDoc); } /// /// Load xml from file /// /// Xml filename /// Xml node public static XmlNode LoadXmlFromFile(string xmlFilename) { XmlDocument xmlDoc = new XmlDocument(); if (File.Exists(xmlFilename)) xmlDoc.Load(File.Open(xmlFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); return GetRootXmlNode(xmlDoc); } /// /// Get child node /// /// Root node /// Child name /// Xml node public static XmlNode GetChildNode(XmlNode rootNode, string childName) { if (rootNode == null) throw new ArgumentNullException("rootNode", "Need valid rootNode for GetChildNode."); foreach (XmlNode node in rootNode.ChildNodes) { if (node.Name == childName) return node; if (node.ChildNodes != null && node.ChildNodes.Count > 0) { XmlNode result = GetChildNode(node, childName); if (result != null) return result; } } // Not found, return null return null; } /// /// Get child node. This version does not search for a child node name, /// but instead we check if we can find any child with the /// attribute name and value as specified. /// /// Root node /// Child attribute name /// Child attribute value /// Xml node public static XmlNode GetChildNode(XmlNode rootNode, string childAttributeName, string childAttributeValue) { if (rootNode == null) throw new ArgumentNullException("rootNode", "Need valid rootNode for GetChildNode."); foreach (XmlNode node in rootNode.ChildNodes) { if (GetXmlAttribute(node, childAttributeName) == childAttributeValue) return node; if (node.ChildNodes != null && node.ChildNodes.Count > 0) { XmlNode result = GetChildNode(node, childAttributeName, childAttributeValue); if (result != null) return result; } } // Not found, return null return null; } /// /// Get root xml node (not the xml document, but the first useable root) /// /// Xml node public static XmlNode GetRootXmlNode(XmlNode xmlNode) { if (xmlNode == null) throw new ArgumentNullException("xmlNode", "Need valid xmlNode for GetRootXmlNode."); // Not a document type? Then jump back to document type. if (xmlNode.NodeType != XmlNodeType.Document) { return GetRootXmlNode(xmlNode.OwnerDocument); } foreach (XmlNode node in xmlNode.ChildNodes) { if (node.NodeType != XmlNodeType.XmlDeclaration && node.NodeType != XmlNodeType.ProcessingInstruction) { return node; } } // Nothing found? Then return original node, there isn't any real node // available yet. return xmlNode; } private const string DefaultDirectoryAttributeName = "Directory"; /// /// Get directory attribute /// /// Node /// String public static string GetDirectoryAttribute(XmlNode node) { string ret = GetXmlAttribute(node, DefaultDirectoryAttributeName); if (String.IsNullOrEmpty(ret)) { foreach (XmlNode childNode in node.ChildNodes) if (StringHelper.Compare(childNode.Name, DefaultDirectoryAttributeName)) return childNode.Value; } return ret; } /// /// Write xml to string. Will return the whole xml document content /// as a string. Declaration stuff is cut out, but everything else /// is the same as written to the file when calling xmlDoc.Save. /// /// Xml doc /// String public static string WriteXmlToString(XmlDocument xmlDoc) { if (xmlDoc == null) throw new ArgumentNullException("xmlDoc", "WriteXmlToString requires a valid xml document."); // Save xmlDoc to a memory stream MemoryStream memStream = new MemoryStream(); xmlDoc.Save(memStream); // Put it into a text reader memStream.Seek(0, SeekOrigin.Begin); TextReader textReader = new StreamReader(memStream); // And read everything int lineNumber = 0; string ret = ""; while (textReader.Peek() >= 0) { string line = textReader.ReadLine(); lineNumber++; // Skip first line (xml version and stuff) if (lineNumber == 1 && line.Length < 200 && (line.StartsWith(" line = line.Replace("<", "<").Replace(">", ">"); // Else just add to return string ret += (ret.Length == 0 ? "" : StringHelper.NewLine) + line; } // while (textReader.Peek) return ret; } /// /// Convert xml type to string /// /// Type value /// String public static string ConvertXmlTypeToString(object typeValue) { // Special save handling only for the date (at the moment) if (typeValue.GetType() == new DateTime().GetType()) { DateTime date = (DateTime)typeValue; return date.Year + "-" + date.Month.ToString("00") + "-" + date.Day.ToString("00"); } else { return typeValue.ToString(); } } } /// /// Downloaded from abi.exdream.com. Many unused functions were deleted from the original class. /// StringHelper: Provides additional or simplified string functions. /// This class does also offer a lot of powerful functions and /// allows complicated string operations. /// Easy functions at the beginning, harder ones later. /// public class StringHelper { public const string NewLine = "\r\n"; /// /// Don't allow instantiating this class. /// private StringHelper() { } /// /// Check if a string (s1, can be longer as s2) begins with another /// string (s2), only if s1 begins with the same string data as s2, /// true is returned, else false. The string compare is case insensitive. /// static public bool BeginsWith(string s1, string s2) { return String.Compare(s1, 0, s2, 0, s2.Length, true, CultureInfo.CurrentCulture) == 0; } /// /// Helps to compare strings, uses case insensitive comparison. /// String. /// static public bool Compare(string s1, string s2) { return String.Compare(s1, s2, true, CultureInfo.CurrentCulture) == 0; } /// /// Convert string data to int array, string must be in the game /// "1, 3, 8, 7", etc. WriteArrayData is the complementar function. /// /// int array, will be null if string is invalid! static public int[] ConvertStringToIntArray(string s) { // Invalid? if (s == null || s.Length == 0) return null; string[] splitted = s.Split(new char[] { ' ' }); int[] ret = new int[splitted.Length]; for (int i = 0; i < ret.Length; i++) if (String.IsNullOrEmpty(splitted[i]) == false) { try { ret[i] = Convert.ToInt32(splitted[i]); } catch { } } return ret; } /// /// Convert string data to float array, string must be in the game /// "1.5, 3.534, 8.76, 7.49", etc. WriteArrayData is the complementar /// function. /// /// float array, will be null if string is invalid! static public float[] ConvertStringToFloatArray(string s) { // Invalid? if (s == null || s.Length == 0) return null; string[] splitted = s.Split(new char[] { ' ' }); float[] ret = new float[splitted.Length]; for (int i = 0; i < ret.Length; i++) if (String.IsNullOrEmpty(splitted[i]) == false) { try { ret[i] = Convert.ToSingle(splitted[i], CultureInfo.InvariantCulture); } catch { } } return ret; } /// /// Extracts filename from full path+filename, cuts of extension /// if cutExtension is true. Can be also used to cut off directories /// from a path (only last one will remain). /// static public string ExtractFilename(string pathFile, bool cutExtension) { if (pathFile == null) return ""; // Also checking for normal slashes, needed for support reading 3ds max stuff. string[] fileName = pathFile.Split(new char[] { '\\', '/' }); if (fileName.Length == 0) { if (cutExtension) return CutExtension(pathFile); return pathFile; } if (cutExtension) return CutExtension(fileName[fileName.Length - 1]); return fileName[fileName.Length - 1]; } /// /// Cut of extension, e.g. "hi.txt" becomes "hi" /// static public string CutExtension(string file) { if (file == null) return ""; int l = file.LastIndexOf('.'); if (l > 0) return file.Remove(l, file.Length - l); return file; } /// /// Is numeric float /// /// Str /// Bool public static bool IsNumericFloat(string str) { return IsNumericFloat(str, CultureInfo.InvariantCulture.NumberFormat); } /// /// Allow only one decimal point, used for IsNumericFloat. /// /// Input string to check /// Used number format, e.g. /// CultureInfo.InvariantCulture.NumberFormat /// True if check succeeded, false otherwise private static bool AllowOnlyOneDecimalPoint(string str, NumberFormatInfo numberFormat) { char[] strInChars = str.ToCharArray(); bool hasGroupSeperator = false; int decimalSeperatorCount = 0; for (int i = 0; i < strInChars.Length; i++) { if (numberFormat.CurrencyDecimalSeparator.IndexOf(strInChars[i]) == 0) { decimalSeperatorCount++; } // has float group seperators ? if (numberFormat.CurrencyGroupSeparator.IndexOf(strInChars[i]) == 0) { hasGroupSeperator = true; } } if (hasGroupSeperator) { // If first digit is the group seperator or begins with 0, // there is something wrong, the group seperator is used as a comma. if (str.StartsWith(numberFormat.CurrencyGroupSeparator) || strInChars[0] == '0') return false; // look only at the digits in front of the decimal point string[] splittedByDecimalSeperator = str.Split( numberFormat.CurrencyDecimalSeparator.ToCharArray()); // ==> 1.000 -> 000.1 ==> only after 3 digits char[] firstSplittedInChars = splittedByDecimalSeperator[0].ToCharArray(); int arrayLength = firstSplittedInChars.Length; char[] firstSplittedInCharsInverted = new char[arrayLength]; for (int i = 0; i < arrayLength; i++) { firstSplittedInCharsInverted[i] = firstSplittedInChars[arrayLength - 1 - i]; } // group seperators are only allowed between 3 digits -> 1.000.000 for (int i = 0; i < arrayLength; i++) { if (i % 3 != 0 && numberFormat.CurrencyGroupSeparator.IndexOf( firstSplittedInCharsInverted[i]) == 0) { return false; } } } if (decimalSeperatorCount > 1) return false; return true; } /// /// Checks if string is numeric float value /// /// Input string /// Used number format, e.g. /// CultureInfo.InvariantCulture.NumberFormat /// True if str can be converted to a float, /// false otherwise public static bool IsNumericFloat(string str, NumberFormatInfo numberFormat) { // Can't be a float if string is not valid! if (String.IsNullOrEmpty(str)) return false; // Only 1 decimal point is allowed if (AllowOnlyOneDecimalPoint(str, numberFormat) == false) return false; // + allows in the first,last,don't allow in middle of the string // - allows in the first,last,don't allow in middle of the string // $ allows in the first,last,don't allow in middle of the string // , allows in the last,middle,don't allow in first char of the string // . allows in the first,last,middle, allows in all the indexs bool retVal = false; // If string is just 1 letter, don't allow it to be a sign if (str.Length == 1 && "+-$.,".IndexOf(str[0]) >= 0) return false; for (int i = 0; i < str.Length; i++) { // For first indexchar char pChar = //char.Parse(str.Substring(i, 1)); Convert.ToChar(str.Substring(i, 1)); if (retVal) retVal = false; if ((!retVal) && (str.IndexOf(pChar) == 0)) { retVal = ("+-$.0123456789".IndexOf(pChar) >= 0) ? true : false; } // For middle characters if ((!retVal) && (str.IndexOf(pChar) > 0) && (str.IndexOf(pChar) < (str.Length - 1))) { retVal = (",.0123456789".IndexOf(pChar) >= 0) ? true : false; } // For last characters if ((!retVal) && (str.IndexOf(pChar) == (str.Length - 1))) { retVal = ("+-$,.0123456789".IndexOf(pChar) >= 0) ? true : false; } if (!retVal) break; } return retVal; } /// /// Check if string is numeric integer. A decimal point is not accepted. /// /// String to check public static bool IsNumericInt(string str) { // Can't be an int if string is not valid! if (String.IsNullOrEmpty(str)) return false; // Go through every letter in str int strPos = 0; foreach (char ch in str) { // Only 0-9 are allowed if ("0123456789".IndexOf(ch) < 0 && // Allow +/- for first char (strPos > 0 || (ch != '-' && ch != '+'))) return false; strPos++; } // All fine, return true, this is a number! return true; } } }