1 |
using System; |
2 |
using System.Collections.Generic; |
3 |
using System.Text; |
4 |
using System.Xml; |
5 |
|
6 |
namespace Oni.Xml |
7 |
{ |
8 |
internal static class XmlReaderExtensions |
9 |
{ |
10 |
private static readonly char[] emptyChars = new char[0]; |
11 |
|
12 |
[ThreadStatic] |
13 |
private static char[] charBuffer; |
14 |
|
15 |
private static char[] CharBuffer |
16 |
{ |
17 |
get |
18 |
{ |
19 |
if (charBuffer == null) |
20 |
charBuffer = new char[16384]; |
21 |
|
22 |
return charBuffer; |
23 |
} |
24 |
} |
25 |
|
26 |
public static IEnumerable<string> ReadElementContentAsList(this XmlReader xml) |
27 |
{ |
28 |
if (xml.SkipEmpty()) |
29 |
yield break; |
30 |
|
31 |
xml.ReadStartElement(); |
32 |
|
33 |
if (xml.NodeType == XmlNodeType.EndElement) |
34 |
{ |
35 |
xml.ReadEndElement(); |
36 |
yield break; |
37 |
} |
38 |
|
39 |
#if DNET |
40 |
char[] buffer = CharBuffer; |
41 |
int offset = 0; |
42 |
int length; |
43 |
|
44 |
while ((length = xml.ReadValueChunk(buffer, offset, buffer.Length - offset)) > 0 || offset > 0) |
45 |
{ |
46 |
bool hasMore = length != 0; |
47 |
length += offset; |
48 |
offset = 0; |
49 |
|
50 |
for (int i = 0; i < length; i++) |
51 |
{ |
52 |
if (Char.IsWhiteSpace(buffer[i])) |
53 |
continue; |
54 |
|
55 |
int start = i; |
56 |
|
57 |
do |
58 |
i++; |
59 |
while (i < length && !Char.IsWhiteSpace(buffer[i])); |
60 |
|
61 |
if (i == length && hasMore) |
62 |
{ |
63 |
offset = i - start; |
64 |
Array.Copy(buffer, start, buffer, 0, offset); |
65 |
break; |
66 |
} |
67 |
|
68 |
yield return new string(buffer, start, i - start); |
69 |
} |
70 |
} |
71 |
|
72 |
while (xml.NodeType != XmlNodeType.EndElement) |
73 |
xml.Read(); |
74 |
#else |
75 |
string buffer = xml.ReadContentAsString(); |
76 |
|
77 |
for (int i = 0; i < buffer.Length; i++) |
78 |
{ |
79 |
if (char.IsWhiteSpace(buffer[i])) |
80 |
continue; |
81 |
|
82 |
int start = i; |
83 |
|
84 |
do |
85 |
i++; |
86 |
while (i < buffer.Length && !char.IsWhiteSpace(buffer[i])); |
87 |
|
88 |
yield return buffer.Substring(start, i - start); |
89 |
} |
90 |
#endif |
91 |
|
92 |
xml.ReadEndElement(); |
93 |
} |
94 |
|
95 |
private static void ReadArrayCore<T>(XmlReader xml, Func<string, T> parser, List<T> list) |
96 |
{ |
97 |
foreach (var text in xml.ReadElementContentAsList()) |
98 |
list.Add(parser(text)); |
99 |
} |
100 |
|
101 |
public static T[] ReadElementContentAsArray<T>(this XmlReader xml, Func<string, T> parser) |
102 |
{ |
103 |
var list = new List<T>(); |
104 |
ReadArrayCore<T>(xml, parser, list); |
105 |
return list.ToArray(); |
106 |
} |
107 |
|
108 |
public static T[] ReadElementContentAsArray<T>(this XmlReader xml, Func<string, T> converter, int count) |
109 |
{ |
110 |
var list = new List<T>(count); |
111 |
ReadArrayCore(xml, converter, list); |
112 |
var array = new T[count]; |
113 |
list.CopyTo(0, array, 0, count); |
114 |
return array; |
115 |
} |
116 |
|
117 |
public static void ReadElementContentAsArray<T>(this XmlReader xml, Func<string, T> parser, T[] array) |
118 |
{ |
119 |
var text = xml.ReadElementContentAsString(); |
120 |
var tokens = text.Split(emptyChars, StringSplitOptions.RemoveEmptyEntries); |
121 |
|
122 |
for (int i = 0; i < array.Length; i++) |
123 |
{ |
124 |
if (i < tokens.Length) |
125 |
array[i] = parser(tokens[i]); |
126 |
else |
127 |
array[i] = default(T); |
128 |
} |
129 |
} |
130 |
|
131 |
public static T[] ReadElementContentAsArray<T>(this XmlReader xml, Func<string, T> parser, string name) |
132 |
{ |
133 |
string text; |
134 |
|
135 |
if (name == null) |
136 |
text = xml.ReadElementContentAsString(); |
137 |
else |
138 |
text = xml.ReadElementContentAsString(name, string.Empty); |
139 |
|
140 |
var tokens = text.Split(emptyChars, StringSplitOptions.RemoveEmptyEntries); |
141 |
return tokens.ConvertAll(parser); |
142 |
} |
143 |
|
144 |
private static T[] ReadArray<T>(this XmlReader xml, Func<string, T> parser, int count) |
145 |
{ |
146 |
var text = xml.ReadElementContentAsString(); |
147 |
var tokens = text.Split(emptyChars, StringSplitOptions.RemoveEmptyEntries); |
148 |
var values = tokens.ConvertAll(parser); |
149 |
|
150 |
Array.Resize(ref values, count); |
151 |
|
152 |
return values; |
153 |
} |
154 |
|
155 |
public static T[] ReadElementContentAsArray<T>(this XmlReader xml, Func<string, T> converter, int count, string name) |
156 |
{ |
157 |
var array = xml.ReadElementContentAsArray<T>(converter, name); |
158 |
Array.Resize(ref array, count); |
159 |
return array; |
160 |
} |
161 |
|
162 |
|
163 |
public static Vector2 ReadElementContentAsVector2(this XmlReader xml, string name = null) |
164 |
{ |
165 |
var values = xml.ReadElementContentAsArray(XmlConvert.ToSingle, 2, name); |
166 |
|
167 |
return new Vector2(values[0], values[1]); |
168 |
} |
169 |
|
170 |
public static Vector3 ReadElementContentAsVector3(this XmlReader xml, string name = null) |
171 |
{ |
172 |
var values = xml.ReadElementContentAsArray(XmlConvert.ToSingle, 3); |
173 |
|
174 |
return new Vector3(values[0], values[1], values[2]); |
175 |
} |
176 |
|
177 |
public static Vector4 ReadElementContentAsVector4(this XmlReader xml) |
178 |
{ |
179 |
var values = xml.ReadElementContentAsArray(XmlConvert.ToSingle, 4); |
180 |
|
181 |
return new Vector4(values[0], values[1], values[2], values[3]); |
182 |
} |
183 |
|
184 |
public static Quaternion ReadElementContentAsEulerXYZ(this XmlReader xml) |
185 |
{ |
186 |
var values = xml.ReadElementContentAsArray(XmlConvert.ToSingle, 3); |
187 |
|
188 |
return Quaternion.CreateFromEulerXYZ(values[0], values[1], values[2]); |
189 |
} |
190 |
|
191 |
public static Quaternion ReadElementContentAsQuaternion(this XmlReader xml, string name = null) |
192 |
{ |
193 |
var quat = Quaternion.Identity; |
194 |
|
195 |
if (xml.IsEmptyElement) |
196 |
{ |
197 |
xml.Skip(); |
198 |
} |
199 |
else |
200 |
{ |
201 |
if (name == null) |
202 |
xml.ReadStartElement(); |
203 |
else |
204 |
xml.ReadStartElement(name); |
205 |
|
206 |
if (xml.NodeType == XmlNodeType.Text) |
207 |
{ |
208 |
var values = xml.ReadArray(XmlConvert.ToSingle, 4); |
209 |
|
210 |
quat = new Quaternion(values[0], values[1], values[2], -values[3]); |
211 |
} |
212 |
else |
213 |
{ |
214 |
var transforms = new List<Quaternion>(); |
215 |
|
216 |
while (xml.IsStartElement()) |
217 |
{ |
218 |
switch (xml.LocalName) |
219 |
{ |
220 |
case "rotate": |
221 |
var rotate = xml.ReadElementContentAsVector4(); |
222 |
transforms.Add(Quaternion.CreateFromAxisAngle(rotate.XYZ, MathHelper.ToRadians(rotate.W))); |
223 |
break; |
224 |
case "euler": |
225 |
transforms.Add(xml.ReadElementContentAsEulerXYZ()); |
226 |
break; |
227 |
default: |
228 |
throw new XmlException(string.Format("Unknown element {0}", xml.LocalName)); |
229 |
} |
230 |
} |
231 |
|
232 |
foreach (var transform in Utils.Reverse(transforms)) |
233 |
quat *= transform; |
234 |
} |
235 |
|
236 |
xml.ReadEndElement(); |
237 |
} |
238 |
|
239 |
return quat; |
240 |
} |
241 |
|
242 |
public static Matrix ReadElementContentAsMatrix43(this XmlReader xml, string name = null) |
243 |
{ |
244 |
var matrix = Matrix.Identity; |
245 |
|
246 |
if (xml.IsEmptyElement) |
247 |
{ |
248 |
xml.Skip(); |
249 |
} |
250 |
else |
251 |
{ |
252 |
if (name == null) |
253 |
xml.ReadStartElement(); |
254 |
else |
255 |
xml.ReadStartElement(name); |
256 |
|
257 |
if (xml.NodeType == XmlNodeType.Text) |
258 |
{ |
259 |
var values = xml.ReadArray(XmlConvert.ToSingle, 12); |
260 |
|
261 |
matrix = new Matrix( |
262 |
values[0], values[1], values[2], 0.0f, |
263 |
values[3], values[4], values[5], 0.0f, |
264 |
values[6], values[7], values[8], 0.0f, |
265 |
values[9], values[10], values[11], 1.0f); |
266 |
} |
267 |
else |
268 |
{ |
269 |
var transforms = new List<Matrix>(); |
270 |
|
271 |
while (xml.IsStartElement()) |
272 |
{ |
273 |
switch (xml.LocalName) |
274 |
{ |
275 |
case "translate": |
276 |
transforms.Add(Matrix.CreateTranslation(xml.ReadElementContentAsVector3())); |
277 |
break; |
278 |
case "rotate": |
279 |
var rotate = xml.ReadElementContentAsVector4(); |
280 |
transforms.Add(Matrix.CreateFromAxisAngle(rotate.XYZ, MathHelper.ToRadians(rotate.W))); |
281 |
break; |
282 |
case "euler": |
283 |
transforms.Add(xml.ReadElementContentAsEulerXYZ().ToMatrix()); |
284 |
break; |
285 |
case "scale": |
286 |
transforms.Add(Matrix.CreateScale(xml.ReadElementContentAsVector3())); |
287 |
break; |
288 |
default: |
289 |
throw new XmlException(string.Format("Unknown element {0}", xml.LocalName)); |
290 |
} |
291 |
} |
292 |
|
293 |
foreach (var transform in Utils.Reverse(transforms)) |
294 |
matrix *= transform; |
295 |
} |
296 |
|
297 |
xml.ReadEndElement(); |
298 |
} |
299 |
|
300 |
return matrix; |
301 |
} |
302 |
} |
303 |
} |