diff --git a/PathfinderAPI/Util/XML/ElementInfo.cs b/PathfinderAPI/Util/XML/ElementInfo.cs index 27505546..8bafbde2 100644 --- a/PathfinderAPI/Util/XML/ElementInfo.cs +++ b/PathfinderAPI/Util/XML/ElementInfo.cs @@ -1,4 +1,7 @@ -using System.Text; +using System.Linq; +using System; +using System.Collections.Generic; +using System.Text; using System.Xml; namespace Pathfinder.Util.XML; @@ -16,6 +19,20 @@ public string Content { public Dictionary Attributes { get; set; } = new Dictionary(); public List Children { get; set; } = new List(); public ulong NodeID { get; } = freeId++; + public XmlNodeType Type { get; set; } + public bool IsText => Type == XmlNodeType.Text; + + public ElementInfo() { Type = XmlNodeType.Element; } + + public ElementInfo(string name, string content = null, Dictionary attributes = null, List children = null, ElementInfo parent = null) + : this() + { + Name = name; + if(content != null) Content = content; + Attributes = attributes ?? new Dictionary(); + Children = children ?? new List(); + Parent = parent; + } public override string ToString() { @@ -28,60 +45,222 @@ public override string ToString() { WriteToXML(writer); } - return builder.Replace("\t", " ").ToString(); } + public bool TryContentAsBoolean(out bool result) + => Content.TryAsBoolean(out result); + + public bool TryContentAsInt(out int result) + => Content.TryAsInt(out result); + + public bool TryContentAsFloat(out float result) + => Content.TryAsFloat(out result); + + public bool GetContentAsBoolean(bool defaultVal = default) + => TryContentAsBoolean(out var result) ? result : defaultVal; + + public int GetContentAsInt(int defaultVal = default) + => TryContentAsInt(out var result) ? result : defaultVal; + + public float GetContentAsFloat(float defaultVal = default) + => TryContentAsFloat(out var result) ? result : defaultVal; + public bool ContentAsBoolean() - => bool.TryParse(Content, out var value) - ? value - : throw new FormatException($"Value of '{Name}' is not true or false"); + => Content.AsBoolean(nameof(Content)); public int ContentAsInt() - => int.TryParse(Content, out var value) - ? value - : throw new FormatException($"Value of '{Name}' is not an integer, e.g.: 0, 1, 2"); - + => Content.AsInt(nameof(Content)); + public float ContentAsFloat() - => float.TryParse(Content, out var value) - ? value - : throw new FormatException($"Value of '{Name}' is not a float, e.g.: 1.0"); + => Content.AsFloat(nameof(Content)); + + public bool TryAttributeAsBoolean(string attribName, out bool result) + => Attributes.TryAsBoolean(attribName, out result); + + public bool TryAttributeAsInt(string attribName, out int result) + => Attributes.TryAsInt(attribName, out result); + + public bool TryAttributeAsFloat(string attribName, out float result) + => Attributes.TryAsFloat(attribName, out result); + + public bool GetAttributeAsBoolean(string attribName, bool defaultVal = default) + => TryAttributeAsBoolean(attribName, out var result) ? result : defaultVal; + + public int GetAttributeAsInt(string attribName, int defaultVal = default) + => TryAttributeAsInt(attribName, out var result) ? result : defaultVal; + + public float GetAttributeAsFloat(string attribName, float defaultVal = default) + => TryAttributeAsFloat(attribName, out var result) ? result : defaultVal; + + public bool AttributeAsBoolean(string attribName) + => Attributes.AsBoolean(attribName, $"{nameof(Attributes)}"); + + public int AttributeAsInt(string attribName) + => Attributes.AsInt(attribName, $"{nameof(Attributes)}"); + + public float AttributeAsFloat(string attribName) + => Attributes.AsFloat(attribName, $"{nameof(Attributes)}"); + + public bool TryAddChild(ElementInfo info) + { + if(Children == null) return false; + if(info.Parent != null) + info.Parent.Children.Remove(info); + Children.Add(info); + info.Parent = this; + return Children.Last() == info; + } + + public bool TrySetParent(ElementInfo info) + { + return info.TryAddChild(this); + } + + public bool TrySetAttribute(string key, string value) + { + if(Attributes == null) return false; + Attributes[key] = value; + return Attributes[key] == value; + } + + public bool TryGetAttribute(string key, ref string value) + { + if(!Attributes?.TryGetValue(key, out value) ?? true) return false; + return true; + } + + public ElementInfo AddChild(ElementInfo info) + { + TryAddChild(info); + return this; + } + + public ElementInfo SetParent(ElementInfo info) + { + TrySetParent(info); + return this; + } + + public ElementInfo SetAttribute(string key, string value) + { + TrySetAttribute(key, value); + return this; + } + + public string GetAttribute(string key, string defaultValue = null) + { + TryGetAttribute(key, ref defaultValue); + return defaultValue; + } public void WriteToXML(XmlWriter writer) { + if(IsText) + { + writer.WriteString(Content); + return; + } writer.WriteStartElement(Name, ""); foreach (var attr in Attributes) writer.WriteAttributeString(attr.Key, attr.Value); - if (Content == null) - { - foreach (var child in Children) - child.WriteToXML(writer); - } - else - { - writer.WriteValue(Content); - } + foreach (var child in Children) + child.WriteToXML(writer); writer.WriteEndElement(); } + + public static ElementInfo FromText(string input) + => new ElementInfo + { + Content = input, + Type = XmlNodeType.Text, + Attributes = null, + Children = null + }; } -public static class ListExtensions +public static class ElementInfoStringExtensions { - public static ElementInfo GetElement(this List list, string elementName) - { - foreach (var possibleInfo in list) - { - if (possibleInfo.Name == elementName) - { - return possibleInfo; - } - } + public static bool TryAsBoolean(this string content, out bool result) + => bool.TryParse(content, out result); + public static bool TryAsInt(this string content, out int result) + => int.TryParse(content, out result); + public static bool TryAsFloat(this string content, out float result) + => float.TryParse(content, out result); - return null; + public static bool AsBooleanSafe(this string content, bool defaultVal = default) + => content.TryAsBoolean(out var value) + ? value + : defaultVal; + public static int AsIntSafe(this string content, int defaultVal = default) + => content.TryAsInt(out var value) + ? value + : defaultVal; + public static float AsFloatSafe(this string content, float defaultVal = default) + => content.TryAsFloat(out var value) + ? value + : defaultVal; + + public static bool AsBoolean(this string content, string valName = "content") + => content.TryAsBoolean(out var value) + ? value + : throw new FormatException($"Value of '{valName}' is '{content}' which is not true or false"); + public static int AsInt(this string content, string valName = "content") + => content.TryAsInt(out var value) + ? value + : throw new FormatException($"Value of '{valName}' is '{content}' which is not an integer, e.g.: 0, 1, 2"); + public static float AsFloat(this string content, string valName = "content") + => content.TryAsFloat(out var value) + ? value + : throw new FormatException($"Value of '{valName}' is '{content}' which is not a float, e.g.: 1.0"); +} + +public static class ElementInfoListExtensions +{ + public static ElementInfo GetElement(IEnumerable list, string elementName) + { + return list.FirstOrDefault(e => e.Name == elementName); } - public static bool TryGetElement(this List list, string elementName, out ElementInfo info) + public static bool TryGetElement(IEnumerable list, string elementName, out ElementInfo info) { info = GetElement(list, elementName); return info != null; } +} + +[Obsolete("Use ElementInfoListExtensions")] +public static class ListExtensions +{ + public static ElementInfo GetElement(this IEnumerable list, string elementName) + => ElementInfoListExtensions.GetElement(list, elementName); + public static bool TryGetElement(this IEnumerable list, string elementName, out ElementInfo info) + => ElementInfoListExtensions.TryGetElement(list, elementName, out info); +} + +public static class ElementInfoDictionaryExtensions +{ + public static bool TryAsBoolean(this IDictionary attribute, string key, out bool result) + { + result = default; + if(attribute == null) return false; + return attribute.TryGetValue(key, out var str) ? str.TryAsBoolean(out result) : false; + } + public static bool TryAsInt(this IDictionary attribute, string key, out int result) + { + result = default; + if(attribute == null) return false; + return attribute.TryGetValue(key, out var str) ? str.TryAsInt(out result) : false; + } + public static bool TryAsFloat(this IDictionary attribute, string key, out float result) + { + result = default; + if(attribute == null) return false; + return attribute.TryGetValue(key, out var str) ? str.TryAsFloat(out result) : false; + } + public static bool AsBoolean(this IDictionary attribute, string key, string dictName = "attribute") + => attribute[key].AsBoolean($"{dictName}[{key}]"); + public static int AsInt(this IDictionary attribute, string key, string dictName = "attribute") + => attribute[key].AsInt($"{dictName}[{key}]"); + public static float AsFloat(this IDictionary attribute, string key, string dictName = "attribute") + => attribute[key].AsFloat($"{dictName}[{key}]"); } \ No newline at end of file diff --git a/PathfinderAPI/Util/XML/EventExecutor.cs b/PathfinderAPI/Util/XML/EventExecutor.cs index 4f7a4808..cdda5980 100644 --- a/PathfinderAPI/Util/XML/EventExecutor.cs +++ b/PathfinderAPI/Util/XML/EventExecutor.cs @@ -193,14 +193,7 @@ protected override void ReadElement(Dictionary attributes) else { var topElement = currentElementStack.Peek(); - var element = new ElementInfo() - { - Name = Reader.Name, - Attributes = attributes, - Parent = topElement, - Content = null - }; - topElement.Children.Add(element); + var element = new ElementInfo(Reader.Name, attributes: attributes).SetParent(topElement); currentElementStack.Push(element); } } @@ -242,6 +235,7 @@ protected override void ReadText() topElement.Content = Reader.Value; else topElement.Content += "\n" + Reader.Value; + topElement.AddChild(ElementInfo.FromText(Reader.Value)); } }