using System; using System.Collections.Generic; using System.Text; using System.Xml; namespace Oni.Xml { internal static class XmlReaderExtensions { private static readonly char[] emptyChars = new char[0]; [ThreadStatic] private static char[] charBuffer; private static char[] CharBuffer { get { if (charBuffer == null) charBuffer = new char[16384]; return charBuffer; } } public static IEnumerable ReadElementContentAsList(this XmlReader xml) { if (xml.SkipEmpty()) yield break; xml.ReadStartElement(); if (xml.NodeType == XmlNodeType.EndElement) { xml.ReadEndElement(); yield break; } #if DNET char[] buffer = CharBuffer; int offset = 0; int length; while ((length = xml.ReadValueChunk(buffer, offset, buffer.Length - offset)) > 0 || offset > 0) { bool hasMore = length != 0; length += offset; offset = 0; for (int i = 0; i < length; i++) { if (Char.IsWhiteSpace(buffer[i])) continue; int start = i; do i++; while (i < length && !Char.IsWhiteSpace(buffer[i])); if (i == length && hasMore) { offset = i - start; Array.Copy(buffer, start, buffer, 0, offset); break; } yield return new string(buffer, start, i - start); } } while (xml.NodeType != XmlNodeType.EndElement) xml.Read(); #else string buffer = xml.ReadContentAsString(); for (int i = 0; i < buffer.Length; i++) { if (char.IsWhiteSpace(buffer[i])) continue; int start = i; do i++; while (i < buffer.Length && !char.IsWhiteSpace(buffer[i])); yield return buffer.Substring(start, i - start); } #endif xml.ReadEndElement(); } private static void ReadArrayCore(XmlReader xml, Func parser, List list) { foreach (var text in xml.ReadElementContentAsList()) list.Add(parser(text)); } public static T[] ReadElementContentAsArray(this XmlReader xml, Func parser) { var list = new List(); ReadArrayCore(xml, parser, list); return list.ToArray(); } public static T[] ReadElementContentAsArray(this XmlReader xml, Func converter, int count) { var list = new List(count); ReadArrayCore(xml, converter, list); var array = new T[count]; list.CopyTo(0, array, 0, count); return array; } public static void ReadElementContentAsArray(this XmlReader xml, Func parser, T[] array) { var text = xml.ReadElementContentAsString(); var tokens = text.Split(emptyChars, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { if (i < tokens.Length) array[i] = parser(tokens[i]); else array[i] = default(T); } } public static T[] ReadElementContentAsArray(this XmlReader xml, Func parser, string name) { string text; if (name == null) text = xml.ReadElementContentAsString(); else text = xml.ReadElementContentAsString(name, string.Empty); var tokens = text.Split(emptyChars, StringSplitOptions.RemoveEmptyEntries); return tokens.ConvertAll(parser); } private static T[] ReadArray(this XmlReader xml, Func parser, int count) { var text = xml.ReadElementContentAsString(); var tokens = text.Split(emptyChars, StringSplitOptions.RemoveEmptyEntries); var values = tokens.ConvertAll(parser); Array.Resize(ref values, count); return values; } public static T[] ReadElementContentAsArray(this XmlReader xml, Func converter, int count, string name) { var array = xml.ReadElementContentAsArray(converter, name); Array.Resize(ref array, count); return array; } public static Vector2 ReadElementContentAsVector2(this XmlReader xml, string name = null) { var values = xml.ReadElementContentAsArray(XmlConvert.ToSingle, 2, name); return new Vector2(values[0], values[1]); } public static Vector3 ReadElementContentAsVector3(this XmlReader xml, string name = null) { var values = xml.ReadElementContentAsArray(XmlConvert.ToSingle, 3); return new Vector3(values[0], values[1], values[2]); } public static Vector4 ReadElementContentAsVector4(this XmlReader xml) { var values = xml.ReadElementContentAsArray(XmlConvert.ToSingle, 4); return new Vector4(values[0], values[1], values[2], values[3]); } public static Quaternion ReadElementContentAsEulerXYZ(this XmlReader xml) { var values = xml.ReadElementContentAsArray(XmlConvert.ToSingle, 3); return Quaternion.CreateFromEulerXYZ(values[0], values[1], values[2]); } public static Quaternion ReadElementContentAsQuaternion(this XmlReader xml, string name = null) { var quat = Quaternion.Identity; if (xml.IsEmptyElement) { xml.Skip(); } else { if (name == null) xml.ReadStartElement(); else xml.ReadStartElement(name); if (xml.NodeType == XmlNodeType.Text) { var values = xml.ReadArray(XmlConvert.ToSingle, 4); quat = new Quaternion(values[0], values[1], values[2], -values[3]); } else { var transforms = new List(); while (xml.IsStartElement()) { switch (xml.LocalName) { case "rotate": var rotate = xml.ReadElementContentAsVector4(); transforms.Add(Quaternion.CreateFromAxisAngle(rotate.XYZ, MathHelper.ToRadians(rotate.W))); break; case "euler": transforms.Add(xml.ReadElementContentAsEulerXYZ()); break; default: throw new XmlException(string.Format("Unknown element {0}", xml.LocalName)); } } foreach (var transform in Utils.Reverse(transforms)) quat *= transform; } xml.ReadEndElement(); } return quat; } public static Matrix ReadElementContentAsMatrix43(this XmlReader xml, string name = null) { var matrix = Matrix.Identity; if (xml.IsEmptyElement) { xml.Skip(); } else { if (name == null) xml.ReadStartElement(); else xml.ReadStartElement(name); if (xml.NodeType == XmlNodeType.Text) { var values = xml.ReadArray(XmlConvert.ToSingle, 12); matrix = new Matrix( values[0], values[1], values[2], 0.0f, values[3], values[4], values[5], 0.0f, values[6], values[7], values[8], 0.0f, values[9], values[10], values[11], 1.0f); } else { var transforms = new List(); while (xml.IsStartElement()) { switch (xml.LocalName) { case "translate": transforms.Add(Matrix.CreateTranslation(xml.ReadElementContentAsVector3())); break; case "rotate": var rotate = xml.ReadElementContentAsVector4(); transforms.Add(Matrix.CreateFromAxisAngle(rotate.XYZ, MathHelper.ToRadians(rotate.W))); break; case "euler": transforms.Add(xml.ReadElementContentAsEulerXYZ().ToMatrix()); break; case "scale": transforms.Add(Matrix.CreateScale(xml.ReadElementContentAsVector3())); break; default: throw new XmlException(string.Format("Unknown element {0}", xml.LocalName)); } } foreach (var transform in Utils.Reverse(transforms)) matrix *= transform; } xml.ReadEndElement(); } return matrix; } } }