// Copyright (c) Craftwork Games. All rights reserved. // Licensed under the MIT license. // See LICENSE file in the project root for full license information. using System; using System.Xml; using Microsoft.Xna.Framework; namespace MonoGame.Extended.Serialization.Xml; /// /// Provides extension methods for to simplify reading and parsing XML Attributes into /// strong-typed values. /// public static class XmlReaderExtensions { /// /// Reads an XML attribute as an value. /// /// The XML reader instance. /// The name of the attribute to read. /// The value parsed from the value of the specified attribute. /// /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as an . /// public static int GetAttributeInt(this XmlReader reader, string attributeName) { string value = reader.GetAttribute(attributeName); if (value == null) { throw new XmlException($"Required attribute '{attributeName}' is missing."); } try { return int.Parse(value); } catch (Exception ex) when (ex is FormatException || ex is OverflowException) { throw new XmlException( $"Invalid integer format for attribute '{attributeName}'. Expected integer, but got '{value}'", ex ); } } /// /// Reads an XML attribute as an value, returning a default value if the attribute is missing or invalid. /// /// The XML reader instance. /// The name of the attribute to read. /// The default value to return if the attribute is missing or cannot be parsed. /// The value parsed from the value of the specified attribute, or the default value if parsing fails. public static int GetAttributeInt(this XmlReader reader, string attributeName, int defaultValue) { string value = reader.GetAttribute(attributeName); if (value == null) { return defaultValue; } try { return int.Parse(value); } catch { return defaultValue; } } /// /// Reads an XML attribute as a value. /// /// The XML reader instance. /// The name of the attribute to read. /// The value parsed from the specified attribute. /// /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as a . /// public static float GetAttributeFloat(this XmlReader reader, string attributeName) { string value = reader.GetAttribute(attributeName); if (value == null) { throw new XmlException($"Required attribute '{attributeName}' is missing."); } try { return float.Parse(value); } catch (Exception ex) when (ex is FormatException || ex is OverflowException) { throw new XmlException( $"Invalid float format for attribute '{attributeName}'. Expected float, but got '{value}'", ex ); } } /// /// Reads an XML attribute as a value, returning a default value if the attribute is missing or invalid. /// /// The XML reader instance. /// The name of the attribute to read. /// The default value to return if the attribute is missing or cannot be parsed. /// The value parsed from the specified attribute, or the default value if parsing fails. public static float GetAttributeFloat(this XmlReader reader, string attributeName, float defaultValue) { string value = reader.GetAttribute(attributeName); if (value == null) { return defaultValue; } try { return float.Parse(value); } catch { return defaultValue; } } /// /// Reads an XML attribute as a value. /// /// The XML reader instance. /// The name of the attribute to read. /// The value parsed from the value of the specified attribute. /// /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as a . /// public static bool GetAttributeBool(this XmlReader reader, string attributeName) { string value = reader.GetAttribute(attributeName); if (value == null) { throw new XmlException($"Required attribute '{attributeName}' is missing."); } try { return bool.Parse(value); } catch (Exception ex) when (ex is FormatException) { throw new XmlException( $"Invalid bool format for attribute '{attributeName}'. Expected 'true' or 'false' but got '{value}'", ex ); } } /// /// Reads an XML attribute as a value, returning a default value if the attribute is missing or invalid. /// /// The XML reader instance. /// The name of the attribute to read. /// The default value to return if the attribute is missing or cannot be parsed. /// The value parsed from the value of the specified attribute, or the default value if parsing fails. public static bool GetAttributeBool(this XmlReader reader, string attributeName, bool defaultValue) { string value = reader.GetAttribute(attributeName); if (value == null) { return defaultValue; } try { return bool.Parse(value); } catch { return defaultValue; } } /// /// Reads an XML attribute as an enumeration value. /// /// The enumeration type to parse. /// The XML reader instance. /// The name of the attribute to read. /// The enumeration value parsed from the value of the specified attribute. /// /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as the specified enumeration type. /// public static T GetAttributeEnum(this XmlReader reader, string attributeName) where T : struct, Enum { string value = reader.GetAttribute(attributeName); if (value == null) { throw new XmlException($"Required attribute '{attributeName}' is missing."); } try { return Enum.Parse(value); } catch (Exception ex) when (ex is ArgumentException) { throw new XmlException( $"Invalid {typeof(T).Name} format for attribute '{attributeName}'. Expected a {typeof(T).Name} value but got '{value}'", ex ); } } /// /// Reads an XML attribute as an enumeration value, returning a default value if the attribute is missing or invalid. /// /// The enumeration type to parse. /// The XML reader instance. /// The name of the attribute to read. /// The default value to return if the attribute is missing or cannot be parsed. /// The enumeration value parsed from the value of the specified attribute, or the default value if parsing fails. public static T GetAttributeEnum(this XmlReader reader, string attributeName, T defaultValue) where T : struct, Enum { string value = reader.GetAttribute(attributeName); if (value == null) { return defaultValue; } try { return Enum.Parse(value); } catch { return defaultValue; } } /// /// Reads an XML attribute as a value. /// /// The XML reader instance. /// The name of the attribute to read. /// The value parsed from the value of the specified attribute. /// /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as a . /// public static Rectangle GetAttributeRectangle(this XmlReader reader, string attributeName) { string value = reader.GetAttribute(attributeName); if (value == null) { throw new XmlException($"Required attribute '{attributeName}' is missing."); } string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries); try { if (split.Length != 4) { throw new InvalidOperationException($"{nameof(Rectangle)} attribute must contain four integer values separated by a comma"); } int x = int.Parse(split[0]); int y = int.Parse(split[1]); int width = int.Parse(split[2]); int height = int.Parse(split[3]); return new Rectangle(x, y, width, height); } catch (Exception ex) when (ex is FormatException || ex is OverflowException || ex is InvalidOperationException) { throw new XmlException( $"Invalid {nameof(Rectangle)} format for attribute '{attributeName}'. Expected 'x,y,width,height' but got '{value}'", ex ); } } /// /// Reads an XML attribute as a value, returning a default value if the attribute is missing or invalid. /// /// The XML reader instance. /// The name of the attribute to read. /// The default value to return if the attribute is missing or cannot be parsed. /// The value parsed from the value of the specified attribute, or the default value if parsing fails. public static Rectangle GetAttributeRectangle(this XmlReader reader, string attributeName, Rectangle defaultValue) { string value = reader.GetAttribute(attributeName); if (value == null) { return defaultValue; } string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries); try { if (split.Length != 4) { return defaultValue; } int x = int.Parse(split[0]); int y = int.Parse(split[1]); int width = int.Parse(split[2]); int height = int.Parse(split[3]); return new Rectangle(x, y, width, height); } catch { return defaultValue; } } /// /// Reads an XML attribute as a value. /// /// The XML reader instance. /// The name of the attribute to read. /// The value parsed from the value of the specified attribute. /// /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as a . /// public static Vector2 GetAttributeVector2(this XmlReader reader, string attributeName) { string value = reader.GetAttribute(attributeName); if (value == null) { throw new XmlException($"Required attribute '{attributeName}' is missing."); } string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries); try { if (split.Length != 2) { throw new InvalidOperationException($"{nameof(Vector2)} attribute must contain two float values separated by a comma"); } float x = float.Parse(split[0]); float y = float.Parse(split[1]); return new Vector2(x, y); } catch (Exception ex) when (ex is FormatException || ex is OverflowException || ex is InvalidOperationException) { throw new XmlException( $"Invalid {nameof(Vector2)} format for attribute '{attributeName}'. Expected 'x,y', but got '{value}'", ex ); } } /// /// Reads an XML attribute as a value, returning a default value if the attribute is missing or invalid. /// /// The XML reader instance. /// The name of the attribute to read. /// The default value to return if the attribute is missing or cannot be parsed. /// The value parsed from the value of the specified attribute, or the default value if parsing fails. public static Vector2 GetAttributeVector2(this XmlReader reader, string attributeName, Vector2 defaultValue) { string value = reader.GetAttribute(attributeName); if (value == null) { return defaultValue; } string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries); try { if (split.Length != 2) { return defaultValue; } float x = float.Parse(split[0]); float y = float.Parse(split[1]); return new Vector2(x, y); } catch { return defaultValue; } } /// /// Reads an XML attribute as a value. /// /// The XML reader instance. /// The name of the attribute to read. /// The value parsed from the value of the specified attribute. /// /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as a . /// public static Vector3 GetAttributeVector3(this XmlReader reader, string attributeName) { string value = reader.GetAttribute(attributeName); if (value == null) { throw new XmlException($"Required attribute '{attributeName}' is missing."); } string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries); try { if (split.Length != 3) { throw new InvalidOperationException($"{nameof(Vector3)} attribute must contain three float values separated by a comma"); } float x = float.Parse(split[0]); float y = float.Parse(split[1]); float z = float.Parse(split[2]); return new Vector3(x, y, z); } catch (Exception ex) when (ex is FormatException || ex is OverflowException || ex is InvalidOperationException) { throw new XmlException( $"Invalid {nameof(Vector3)} format for attribute '{attributeName}'. Expected 'x,y,z', but got '{value}'", ex ); } } /// /// Reads an XML attribute as a value, returning a default value if the attribute is missing or invalid. /// /// The XML reader instance. /// The name of the attribute to read. /// The default value to return if the attribute is missing or cannot be parsed. /// The value parsed from the value of the specified attribute, or the default value if parsing fails. public static Vector3 GetAttributeVector3(this XmlReader reader, string attributeName, Vector3 defaultValue) { string value = reader.GetAttribute(attributeName); if (value == null) { return defaultValue; } string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries); try { if (split.Length != 3) { return defaultValue; } float x = float.Parse(split[0]); float y = float.Parse(split[1]); float z = float.Parse(split[2]); return new Vector3(x, y, z); } catch { return defaultValue; } } }