comparison orange/serialization/archives/XMLArchive.d @ 0:f7b078e85f7f

First commit
author Jacob Carlborg <doob@me.com>
date Wed, 26 May 2010 17:19:13 +0200
parents
children ae24aae69a3b
comparison
equal deleted inserted replaced
-1:000000000000 0:f7b078e85f7f
1 /**
2 * Copyright: Copyright (c) 2010 Jacob Carlborg.
3 * Authors: Jacob Carlborg
4 * Version: Initial created: Jan 26, 2010
5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6 */
7 module orange.serialization.archives.XMLArchive;
8
9 version (Tango)
10 {
11 import tango.text.xml.DocPrinter;
12 import tango.text.xml.Document;
13 import tango.util.Convert : to;
14 }
15
16 import orange.serialization.archives._;
17 import orange.util._;
18
19 private enum ArchiveMode
20 {
21 archiving,
22 unarchiving
23 }
24
25 class XMLArchive (U = char) : Archive!(U)
26 {
27 static assert (isChar!(U), format!(`The given type "`, U, `" is not a valid type. Valid types are: "char", "wchar" and "dchar".`));
28
29 private struct Tags
30 {
31 static const DataType structTag = "struct";
32 static const DataType dataTag = "data";
33 static const DataType archiveTag = "archive";
34 static const DataType arrayTag = "array";
35 static const DataType objectTag = "object";
36 static const DataType baseTag = "base";
37 static const DataType stringTag = "string";
38 static const DataType referenceTag = "reference";
39 static const DataType pointerTag = "pointer";
40 static const DataType associativeArrayTag = "associativeArray";
41 static const DataType typedefTag = "typedef";
42 static const DataType nullTag = "null";
43 static const DataType enumTag = "enum";
44 }
45
46 private struct Attributes
47 {
48 static const DataType typeAttribute = "type";
49 static const DataType versionAttribute = "version";
50 static const DataType lengthAttribute = "length";
51 static const DataType keyAttribute = "key";
52 static const DataType runtimeTypeAttribute = "runtimeType";
53 static const DataType idAttribute = "id";
54 static const DataType keyTypeAttribute = "keyType";
55 static const DataType valueTypeAttribute = "valueType";
56 }
57
58 private
59 {
60 DataType archiveType = "org.dsource.orange.xml";
61 DataType archiveVersion = "0.1";
62
63 Document!(U) doc;
64 doc.Node lastElement;
65 DocPrinter!(U) printer;
66 doc.Node lastElementSaved;
67
68 bool hasBegunArchiving;
69 bool hasBegunUnarchiving;
70
71 DataType[void*] archivedReferences;
72 void*[DataType] unarchivedReferences;
73
74 size_t idCounter;
75 }
76
77 this ()
78 {
79 doc = new Document!(U);
80 }
81
82 public void beginArchiving ()
83 {
84 if (!hasBegunArchiving)
85 {
86 doc.header;
87 lastElement = doc.tree.element(null, Tags.archiveTag)
88 .attribute(null, Attributes.typeAttribute, archiveType)
89 .attribute(null, Attributes.versionAttribute, archiveVersion);
90 lastElement = lastElement.element(null, Tags.dataTag);
91
92 hasBegunArchiving = true;
93 }
94 }
95
96 public void beginUnarchiving (DataType data)
97 {
98 if (!hasBegunUnarchiving)
99 {
100 doc.parse(data);
101 hasBegunUnarchiving = true;
102
103 auto set = doc.query[Tags.archiveTag][Tags.dataTag];
104
105 if (set.nodes.length == 1)
106 lastElement = set.nodes[0];
107
108 else if (set.nodes.length == 0)
109 throw new ArchiveException(errorMessage!(ArchiveMode.unarchiving) ~ `The "` ~ to!(string)(Tags.dataTag) ~ `" tag could not be found.`, __FILE__, __LINE__);
110
111 else
112 throw new ArchiveException(errorMessage!(ArchiveMode.unarchiving) ~ `There were more than one "` ~ to!(string)(Tags.dataTag) ~ `" tag.`, __FILE__, __LINE__);
113 }
114 }
115
116 public DataType data ()
117 {
118 if (!printer)
119 printer = new DocPrinter!(U);
120
121 return printer.print(doc);
122 }
123
124 public void reset ()
125 {
126 hasBegunArchiving = false;
127 hasBegunUnarchiving = false;
128 idCounter = 0;
129 doc.reset;
130 }
131
132 private void begin ()
133 {
134 lastElementSaved = lastElement;
135 }
136
137 private void end ()
138 {
139 lastElement = lastElementSaved;
140 }
141
142 public void archive (T) (T value, DataType key, void delegate () dg = null)
143 {
144 if (!hasBegunArchiving)
145 beginArchiving();
146
147 restore(lastElement) in {
148 bool callDelegate = true;
149
150 static if (isTypeDef!(T))
151 archiveTypeDef(value, key);
152
153 else static if (isObject!(T))
154 archiveObject(value, key, callDelegate);
155
156 else static if (isStruct!(T))
157 archiveStruct(value, key);
158
159 else static if (isString!(T))
160 archiveString(value, key);
161
162 else static if (isArray!(T))
163 archiveArray(value, key);
164
165 else static if (isAssociativeArray!(T))
166 archiveAssociativeArray(value, key);
167
168 else static if (isPrimitive!(T))
169 archivePrimitive(value, key);
170
171 else static if (isPointer!(T))
172 archivePointer(value, key, callDelegate);
173
174 else static if (isEnum!(T))
175 archiveEnum(value, key);
176
177 else
178 static assert(false, format!(`The type "`, T, `" cannot be archived.`));
179
180 if (callDelegate && dg)
181 dg();
182 };
183 }
184
185 private void archiveObject (T) (T value, DataType key, ref bool callDelegate)
186 {
187 if (!value)
188 {
189 lastElement.element(null, Tags.nullTag)
190 .attribute(null, Attributes.typeAttribute, toDataType(T.stringof))
191 .attribute(null, Attributes.keyAttribute, key);
192 callDelegate = false;
193 }
194
195 else if (auto reference = getArchivedReference(value))
196 {
197 archiveReference(key, reference);
198 callDelegate = false;
199 }
200
201 else
202 {
203 DataType id = nextId;
204
205 lastElement = lastElement.element(null, Tags.objectTag)
206 .attribute(null, Attributes.runtimeTypeAttribute, toDataType(value.classinfo.name))
207 .attribute(null, Attributes.typeAttribute, toDataType(T.stringof))
208 .attribute(null, Attributes.keyAttribute, key)
209 .attribute(null, Attributes.idAttribute, id);
210
211 addArchivedReference(value, id);
212 }
213 }
214
215 private void archiveStruct (T) (T value, DataType key)
216 {
217 lastElement = lastElement.element(null, Tags.structTag)
218 .attribute(null, Attributes.typeAttribute, toDataType(T.stringof))
219 .attribute(null, Attributes.keyAttribute, key);
220 }
221
222 private void archiveString (T) (T value, DataType key)
223 {
224 lastElement.element(null, Tags.stringTag, toDataType(value))
225 .attribute(null, Attributes.typeAttribute, toDataType(BaseTypeOfArray!(T).stringof))
226 .attribute(null, Attributes.keyAttribute, key);
227 }
228
229 private void archiveArray (T) (T value, DataType key)
230 {
231 lastElement = lastElement.element(null, Tags.arrayTag)
232 .attribute(null, Attributes.typeAttribute, toDataType(BaseTypeOfArray!(T).stringof))
233 .attribute(null, Attributes.lengthAttribute, toDataType(value.length))
234 .attribute(null, Attributes.keyAttribute, key);
235 }
236
237 private void archiveAssociativeArray (T) (T value, DataType key)
238 {
239 lastElement = lastElement.element(null, Tags.associativeArrayTag)
240 .attribute(null, Attributes.keyTypeAttribute, toDataType(KeyTypeOfAssociativeArray!(T).stringof))
241 .attribute(null, Attributes.valueTypeAttribute, toDataType(ValueTypeOfAssociativeArray!(T).stringof))
242 .attribute(null, Attributes.lengthAttribute, toDataType(value.length))
243 .attribute(null, Attributes.keyAttribute, key);
244 }
245
246 private void archivePointer (T) (T value, DataType key, ref bool callDelegate)
247 {
248 if (auto reference = getArchivedReference(value))
249 {
250 archiveReference(key, reference);
251 callDelegate = false;
252 }
253
254 else
255 {
256 DataType id = nextId;
257
258 lastElement = lastElement.element(null, Tags.pointerTag)
259 .attribute(null, Attributes.keyAttribute, key)
260 .attribute(null, Attributes.idAttribute, id);
261
262 addArchivedReference(value, id);
263 }
264 }
265
266 private void archiveEnum (T) (T value, DataType key)
267 {
268 lastElement.element(null, Tags.enumTag, toDataType(value))
269 .attribute(null, Attributes.typeAttribute, toDataType(T.stringof))
270 .attribute(null, Attributes.keyAttribute, key);
271 }
272
273 private void archivePrimitive (T) (T value, DataType key)
274 {
275 lastElement.element(null, toDataType(T.stringof), toDataType(value))
276 .attribute(null, Attributes.keyAttribute, key);
277 }
278
279 private void archiveTypeDef (T) (T value, DataType key)
280 {
281 lastElement = lastElement.element(null, Tags.typedefTag)
282 .attribute(null, Attributes.typeAttribute, toDataType(BaseTypeOfTypeDef!(T).stringof));
283 .attribute(null, Attributes.key, key);
284 }
285
286 public T unarchive (T) (DataType key, T delegate (T) dg = null)
287 {
288 if (!hasBegunUnarchiving)
289 beginUnarchiving(data);
290
291 return restore!(T)(lastElement) in {
292 T value;
293
294 bool callDelegate = true;
295
296 static if (isTypeDef!(T))
297 value = unarchiveTypeDef!(T)(key);
298
299 else static if (isObject!(T))
300 value = unarchiveObject!(T)(key, callDelegate);
301
302 else static if (isStruct!(T))
303 value = unarchiveStruct!(T)(key);
304
305 else static if (isString!(T))
306 value = unarchiveString!(T)(key);
307
308 else static if (isArray!(T))
309 value = unarchiveArray!(T)(key);
310
311 else static if (isAssociativeArray!(T))
312 value = unarchiveAssociativeArray!(T)(key);
313
314 else static if (isPrimitive!(T))
315 value = unarchivePrimitive!(T)(key);
316
317 else static if (isPointer!(T))
318 value = unarchivePointer!(T)(key, callDelegate);
319
320 else static if (isEnum!(T))
321 value = unarchiveEnum!(T)(key);
322
323 else
324 static assert(false, format!(`The type "`, T, `" cannot be unarchived.`));
325
326 if (callDelegate && dg)
327 return dg(value);
328
329 return value;
330 };
331 }
332
333 private T unarchiveObject (T) (DataType key, ref bool callDelegate)
334 {
335 DataType id = unarchiveReference(key);
336
337 if (auto reference = getUnarchivedReference!(T)(id))
338 {
339 callDelegate = false;
340 return *reference;
341 }
342
343 auto tmp = getElement(Tags.objectTag, key, Attributes.keyAttribute, false);
344
345 if (!tmp)
346 {
347 lastElement = getElement(Tags.nullTag, key);
348 callDelegate = false;
349 return null;
350 }
351
352 lastElement = tmp;
353
354 auto runtimeType = getValueOfAttribute(Attributes.runtimeTypeAttribute);
355 auto name = fromDataType!(string)(runtimeType);
356 id = getValueOfAttribute(Attributes.idAttribute);
357
358 T result;
359
360 static if (is(typeof(T._ctor)))
361 {
362 ParameterTupleOf!(typeof(T._ctor)) params;
363 result = factory!(T, typeof(params))(name, params);
364 }
365
366 else
367 result = factory!(T)(name);
368
369 addUnarchivedReference(result, id);
370
371 return result;
372 }
373
374 private T unarchiveStruct (T) (DataType key)
375 {
376 lastElement = getElement(Tags.structTag, key);
377
378 return T.init;
379 }
380
381 private T unarchiveString (T) (DataType key)
382 {
383 return fromDataType!(T)(getElement(Tags.stringTag, key).value);
384 }
385
386 private T unarchiveArray (T) (DataType key)
387 {
388 T value;
389
390 lastElement = getElement(Tags.arrayTag, key);
391 auto length = getValueOfAttribute(Attributes.lengthAttribute);
392 value.length = fromDataType!(size_t)(length);
393
394 return value;
395 }
396
397 private T unarchiveAssociativeArray (T) (DataType key)
398 {
399 lastElement = getElement(Tags.associativeArrayTag, key);
400
401 return T.init;
402 }
403
404 private T unarchivePointer (T) (DataType key, ref bool callDelegate)
405 {
406 DataType id = unarchiveReference(key);
407
408 if (auto reference = getUnarchivedReference!(T)(id))
409 {
410 callDelegate = false;
411 return *reference;
412 }
413
414 lastElement = getElement(Tags.pointerTag, key);
415 id = getValueOfAttribute(Attributes.idAttribute);
416
417 T result = new BaseTypeOfPointer!(T);
418
419 addUnarchivedReference(result, id);
420
421 return result;
422 }
423
424 private T unarchiveEnum (T) (DataType key)
425 {
426 return fromDataType!(T)(getElement(Tags.enumTag, key).value);
427 }
428
429 private T unarchivePrimitive (T) (DataType key)
430 {
431 return fromDataType!(T)(getElement(toDataType(T.stringof), key).value);
432 }
433
434 private T unarchiveTypeDef (T) (DataType key)
435 {
436 lastElement = getElement(Tags.typedefTag, key);
437
438 return T.init;
439 }
440
441 public AssociativeArrayVisitor!(KeyTypeOfAssociativeArray!(T), ValueTypeOfAssociativeArray!(T)) unarchiveAssociativeArrayVisitor (T) ()
442 {
443 return AssociativeArrayVisitor!(KeyTypeOfAssociativeArray!(T), ValueTypeOfAssociativeArray!(T))(this);
444 }
445
446 public void archiveBaseClass (T : Object) (DataType key)
447 {
448 lastElement = lastElement.element(null, Tags.baseTag)
449 .attribute(null, Attributes.typeAttribute, toDataType(T.stringof))
450 .attribute(null, Attributes.keyAttribute, key);
451 }
452
453 public void unarchiveBaseClass (T : Object) (DataType key)
454 {
455 lastElement = getElement(Tags.baseTag, key);
456 }
457
458 template errorMessage (ArchiveMode mode = ArchiveMode.archiving)
459 {
460 static if (mode == ArchiveMode.archiving)
461 const errorMessage = "Could not continue archiving due to unrecognized data format: ";
462
463 else static if (mode == ArchiveMode.unarchiving)
464 const errorMessage = "Could not continue unarchiving due to unrecognized data format: ";
465 }
466
467 private doc.Node getElement (DataType tag, DataType key, DataType attribute = Attributes.keyAttribute, bool throwOnError = true)
468 {
469 auto set = lastElement.query[tag].attribute((doc.Node node) {
470 if (node.name == attribute && node.value == key)
471 return true;
472
473 return false;
474 });
475
476 if (set.nodes.length == 1)
477 return set.nodes[0].parent;
478
479 else
480 {
481 if (throwOnError)
482 {
483 if (set.nodes.length == 0)
484 throw new ArchiveException(`Could not find an element "` ~ to!(string)(tag) ~ `" with the attribute "` ~ to!(string)(Attributes.keyAttribute) ~ `" with the value "` ~ to!(string)(key) ~ `".`, __FILE__, __LINE__);
485
486 else
487 throw new ArchiveException(`Could not unarchive the value with the key "` ~ to!(string)(key) ~ `" due to malformed data.`, __FILE__, __LINE__);
488 }
489
490 return null;
491 }
492 }
493
494 private DataType getValueOfAttribute (DataType attribute)
495 {
496 auto set = lastElement.query.attribute(attribute);
497
498 if (set.nodes.length == 1)
499 return set.nodes[0].value;
500
501 else if (set.nodes.length == 0)
502 throw new ArchiveException(`Could not find the attribute "` ~ to!(string)(attribute) ~ `".`, __FILE__, __LINE__);
503
504 else
505 throw new ArchiveException(`Could not unarchive the value of the attribute "` ~ to!(string)(attribute) ~ `" due to malformed data.`, __FILE__, __LINE__);
506 }
507
508 private void addArchivedReference (T) (T value, DataType id)
509 {
510 static assert(isReference!(T), format!(`The given type "`, T, `" is not a reference type, i.e. object or pointer.`));
511
512 archivedReferences[cast(void*) value] = id;
513 }
514
515 private void addUnarchivedReference (T) (T value, DataType id)
516 {
517 static assert(isReference!(T), format!(`The given type "`, T, `" is not a reference type, i.e. object or pointer.`));
518
519 unarchivedReferences[id] = cast(void*) value;
520 }
521
522 private DataType getArchivedReference (T) (T value)
523 {
524 if (auto tmp = cast(void*) value in archivedReferences)
525 return *tmp;
526
527 return null;
528 }
529
530 private T* getUnarchivedReference (T) (DataType id)
531 {
532 if (auto reference = id in unarchivedReferences)
533 return cast(T*) reference;
534
535 return null;
536 }
537
538 private DataType nextId ()
539 {
540 return toDataType(idCounter++);
541 }
542
543 private void archiveReference (DataType key, DataType id)
544 {
545 lastElement.element(null, Tags.referenceTag, id)
546 .attribute(null, Attributes.keyAttribute, key);
547 }
548
549 private DataType unarchiveReference (DataType key)
550 {
551 auto element = getElement(Tags.referenceTag, key, Attributes.keyAttribute, false);
552
553 if (element)
554 return element.value;
555
556 return cast(DataType) null;
557 }
558
559 private struct AssociativeArrayVisitor(Key, Value)
560 {
561 private XMLArchive archive;
562
563 static AssociativeArrayVisitor opCall (XMLArchive archive)
564 {
565 AssociativeArrayVisitor aai;
566 aai.archive = archive;
567
568 return aai;
569 }
570
571 int opApply(int delegate(ref Key, ref Value) dg)
572 {
573 int result;
574
575 foreach (node ; archive.lastElement.children)
576 {
577 restore(archive.lastElement) in {
578 archive.lastElement = node;
579
580 if (node.attributes.exist)
581 {
582 Key key = to!(Key)(archive.getValueOfAttribute(Attributes.keyAttribute));
583 Value value = to!(Value)(node.value);
584
585 result = dg(key, value);
586 }
587 };
588
589 if (result)
590 break;
591 }
592
593 return result;
594 }
595 }
596 }