Mercurial > projects > orange
comparison orange/serialization/Serializer.d @ 26:78e5fef4bbf2 experimental
Third step in refactoring the API. Stating to add unit tests.
author | Jacob Carlborg <doob@me.com> |
---|---|
date | Tue, 19 Oct 2010 10:22:10 +0200 |
parents | b51e953f79eb |
children | fc315d786f24 |
comparison
equal
deleted
inserted
replaced
25:b51e953f79eb | 26:78e5fef4bbf2 |
---|---|
13 { | 13 { |
14 import std.conv; | 14 import std.conv; |
15 alias ConvError ConversionException; | 15 alias ConvError ConversionException; |
16 } | 16 } |
17 | 17 |
18 import orange.core._; | |
18 import orange.serialization._; | 19 import orange.serialization._; |
19 import orange.serialization.archives._; | 20 import orange.serialization.archives._; |
20 import orange.util._; | 21 import orange.util._; |
21 | 22 |
22 private | 23 private |
29 deserializing | 30 deserializing |
30 } | 31 } |
31 | 32 |
32 alias Mode.serializing serializing; | 33 alias Mode.serializing serializing; |
33 alias Mode.deserializing deserializing; | 34 alias Mode.deserializing deserializing; |
35 | |
36 private char toUpper (char c) | |
37 { | |
38 if (c >= 'a' && c <= 'z') | |
39 return c - 32; | |
40 | |
41 return c; | |
42 } | |
34 } | 43 } |
35 | 44 |
36 class Serializer | 45 class Serializer |
37 { | 46 { |
38 alias void delegate (ArchiveException exception, IArchive.IDataType data) ErrorCallback; | 47 alias void delegate (ArchiveException exception, string[] data) ErrorCallback; |
39 alias IArchive.IDataType DataType; | 48 alias UntypedData Data; |
49 alias size_t Id; | |
40 | 50 |
41 private | 51 private |
42 { | 52 { |
43 ErrorCallback errorCallback_; | 53 ErrorCallback errorCallback_; |
44 IArchive archive; | 54 IArchive archive; |
45 | 55 |
46 size_t keyCounter; | 56 size_t keyCounter; |
47 size_t idCounter; | 57 Id idCounter; |
48 | 58 |
49 RegisterBase[string] serializers; | 59 RegisterBase[string] serializers; |
50 RegisterBase[string] deserializers; | 60 RegisterBase[string] deserializers; |
61 | |
62 Id[void*] serializedReferences; | |
63 void*[Id] deserializedReferences; | |
64 | |
65 Array[Id] serializedArrays; | |
66 void[][Id] deserializedSlices; | |
51 | 67 |
52 bool hasBegunSerializing; | 68 bool hasBegunSerializing; |
53 bool hasBegunDeserializing; | 69 bool hasBegunDeserializing; |
54 | 70 |
55 void delegate (ArchiveException exception, DataType data) throwOnErrorCallback; | 71 void delegate (ArchiveException exception, string[] data) throwOnErrorCallback; |
56 void delegate (ArchiveException exception, DataType data) doNothingOnErrorCallback; | 72 void delegate (ArchiveException exception, string[] data) doNothingOnErrorCallback; |
57 } | 73 } |
58 | 74 |
59 this (IArchive archive) | 75 this (IArchive archive) |
60 { | 76 { |
61 this.archive = archive; | 77 this.archive = archive; |
62 | 78 |
63 throwOnErrorCallback = (ArchiveException exception, IArchive.IDataType data) { throw exception; }; | 79 throwOnErrorCallback = (ArchiveException exception, string[] data) { throw exception; }; |
64 doNothingOnErrorCallback = (ArchiveException exception, IArchive.IDataType data) { /* do nothing */ }; | 80 doNothingOnErrorCallback = (ArchiveException exception, string[] data) { /* do nothing */ }; |
65 | 81 |
66 setThrowOnErrorCallback(); | 82 setThrowOnErrorCallback(); |
67 } | 83 } |
68 | 84 |
69 ErrorCallback errorCallback () | 85 ErrorCallback errorCallback () |
84 void setDoNothingOnErrorCallback () | 100 void setDoNothingOnErrorCallback () |
85 { | 101 { |
86 errorCallback = doNothingOnErrorCallback; | 102 errorCallback = doNothingOnErrorCallback; |
87 } | 103 } |
88 | 104 |
89 DataType serialize (T) (T value, string key = null) | 105 void reset () |
106 { | |
107 resetCounters(); | |
108 | |
109 serializers = null; | |
110 deserializers = null; | |
111 | |
112 serializedReferences = null; | |
113 deserializedReferences = null; | |
114 | |
115 serializedArrays = null; | |
116 deserializedSlices = null; | |
117 | |
118 hasBegunSerializing = false; | |
119 hasBegunDeserializing = false; | |
120 | |
121 archive.reset; | |
122 } | |
123 | |
124 Data serialize (T) (T value, string key = null) | |
90 { | 125 { |
91 if (!hasBegunSerializing) | 126 if (!hasBegunSerializing) |
92 hasBegunSerializing = true; | 127 hasBegunSerializing = true; |
93 | 128 |
94 serializeInternal(value, key); | 129 serializeInternal(value, key); |
95 archive.postProcess; | 130 postProcess; |
96 | 131 |
97 return archive.data; | 132 return archive.untypedData; |
98 } | 133 } |
99 | 134 |
100 private void serializeInternal (T) (T value, string key = null) | 135 private void serializeInternal (T) (T value, string key = null) |
101 { | 136 { |
102 if (!key) | 137 if (!key) |
103 key = nextKey; | 138 key = nextKey; |
104 | 139 |
105 archive.beginArchiving(); | 140 archive.beginArchiving(); |
106 | 141 |
107 static if (isTypeDef!(T)) | 142 static if ( is(T == typedef) ) |
108 serializeTypeDef(value, key); | 143 serializeTypedef(value, key); |
109 | 144 |
110 static if (isObject!(T)) | 145 else static if (isObject!(T)) |
111 serializeObject(value, key); | 146 serializeObject(value, key); |
112 | 147 |
113 else static if (isStruct!(T)) | 148 else static if (isStruct!(T)) |
114 serializeStruct(value, key); | 149 serializeStruct(value, key); |
115 | 150 |
144 } | 179 } |
145 } | 180 } |
146 | 181 |
147 private void serializeObject (T) (T value, string key) | 182 private void serializeObject (T) (T value, string key) |
148 { | 183 { |
149 auto runtimeType = value.classinfo.name; | 184 if (!value) |
150 | 185 return archive.archiveNull(T.stringof, key); |
151 triggerEvents(serializing, value, { | 186 |
152 archive.archiveObject(runtimeType, T.stringof, key, nextId, { | 187 auto reference = getSerializedReference(value); |
188 | |
189 if (reference != Id.max) | |
190 return archive.archiveReference(key, reference); | |
191 | |
192 auto runtimeType = value.classinfo.name; | |
193 | |
194 Id id = nextId; | |
195 addSerializedReference(value, id); | |
196 | |
197 triggerEvents(serializing, value, { | |
198 archive.archiveObject(runtimeType, T.stringof, key, id, { | |
153 if (runtimeType in serializers) | 199 if (runtimeType in serializers) |
154 { | 200 { |
155 auto wrapper = getSerializerWrapper!(T)(runtimeType); | 201 auto wrapper = getSerializerWrapper!(T)(runtimeType); |
156 wrapper(value, this, key); | 202 wrapper(value, this, key); |
157 } | 203 } |
161 | 207 |
162 else | 208 else |
163 { | 209 { |
164 if (isBaseClass(value)) | 210 if (isBaseClass(value)) |
165 throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a serializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__); | 211 throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a serializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__); |
166 | 212 |
167 objectStructSerializeHelper(value); | 213 objectStructSerializeHelper(value); |
168 } | 214 } |
169 }); | 215 }); |
170 }); | 216 }); |
171 } | 217 } |
172 | 218 |
173 private void serializeStruct (T) (T value, DataType key) | 219 private void serializeStruct (T) (T value, string key) |
174 { | 220 { |
175 auto type = T.stringof; | 221 string type = T.stringof; |
176 | 222 |
177 triggerEvents(serializing, value, { | 223 triggerEvents(serializing, value, { |
178 archive.archive(type, key, nextId, { | 224 archive.archiveStruct(type, key, nextId, { |
179 if (type in serializers) | 225 if (type in serializers) |
180 { | 226 { |
181 auto wrapper = getSerializerWrapper!(T)(type); | 227 auto wrapper = getSerializerWrapper!(T)(type); |
182 wrapper(value, this, key); | 228 wrapper(value, this, key); |
183 } | 229 } |
194 }); | 240 }); |
195 } | 241 } |
196 | 242 |
197 private void serializeString (T) (T value, string key) | 243 private void serializeString (T) (T value, string key) |
198 { | 244 { |
245 auto id = nextId; | |
246 auto array = Array(value.ptr, value.length, ElementTypeOfArray!(T).sizeof); | |
247 | |
248 archive.archive(value, key, id); | |
249 addSerializedArray(array, id); | |
250 } | |
251 | |
252 private void serializeArray (T) (T value, string key) | |
253 { | |
254 auto array = Array(value.ptr, value.length, ElementTypeOfArray!(T).sizeof); | |
255 auto id = nextId; | |
256 | |
257 archive.archiveArray(array, arrayToString!(T), key, id, { | |
258 foreach (i, e ; value) | |
259 serializeInternal(e, toData(i)); | |
260 }); | |
261 | |
262 addSerializedArray(array, id); | |
263 } | |
264 | |
265 private void serializeAssociativeArray (T) (T value, string key) | |
266 { | |
267 string keyType = KeyTypeOfAssociativeArray!(T).stringof; | |
268 string valueType = ValueTypeOfAssociativeArray!(T).stringof; | |
269 | |
270 archive.archiveAssociativeArray(keyType, valueType, value.length, key, nextId, { | |
271 size_t i; | |
272 | |
273 foreach(k, v ; value) | |
274 { | |
275 archive.archiveAssociativeArrayKey(toData(i), { | |
276 serializeInternal(k, toData(i)); | |
277 }); | |
278 | |
279 archive.archiveAssociativeArrayValue(toData(i), { | |
280 serializeInternal(v, toData(i)); | |
281 }); | |
282 | |
283 i++; | |
284 } | |
285 }); | |
286 } | |
287 | |
288 private void serializePointer (T) (T value, string key) | |
289 { | |
290 if (!value) | |
291 return archive.archiveNull(T.stringof, key); | |
292 | |
293 auto reference = getSerializedReference(value); | |
294 | |
295 if (reference != Id.max) | |
296 return archive.archiveReference(key, reference); | |
297 | |
298 Id id = nextId; | |
299 | |
300 addSerializedReference(value, id); | |
301 | |
302 archive.archivePointer(key, id, { | |
303 if (key in serializers) | |
304 { | |
305 auto wrapper = getSerializerWrapper!(T)(key); | |
306 wrapper(value, this, key); | |
307 } | |
308 | |
309 else static if (isSerializable!(T, Serializer)) | |
310 value.toData(this, key); | |
311 | |
312 else | |
313 { | |
314 static if (isVoid!(BaseTypeOfPointer!(T))) | |
315 throw new SerializationException(`The value with the key "` ~ to!(string)(key) ~ `"` ~ format!(` of the type "`, T, `" cannot be serialized on its own, either implement orange.serialization.Serializable.isSerializable or register a serializer.`), __FILE__, __LINE__); | |
316 | |
317 else | |
318 serializeInternal(*value, nextKey); | |
319 } | |
320 }); | |
321 } | |
322 | |
323 private void serializeEnum (T) (T value, string key) | |
324 { | |
325 alias BaseTypeOfEnum!(T) EnumBaseType; | |
326 auto val = cast(EnumBaseType) value; | |
327 string type = T.stringof; | |
328 | |
329 archive.archiveEnum(val, type, key, nextId); | |
330 } | |
331 | |
332 private void serializePrimitive (T) (T value, string key) | |
333 { | |
199 archive.archive(value, key, nextId); | 334 archive.archive(value, key, nextId); |
200 } | 335 } |
201 | 336 |
202 private void serializeArray (T) (T value, string key) | 337 private void serializeTypedef (T) (T value, string key) |
203 { | 338 { |
204 auto array = Array(value.ptr, value.length, BaseTypeOfArray!(T).sizeof); | 339 archive.archiveTypedef(T.stringof, key, nextId, { |
205 | 340 serializeInternal!(BaseTypeOfTypedef!(T))(value, nextKey); |
206 archive.archiveArray(array, arrayToString!(T), key, nextId, { | |
207 foreach (i, e ; value) | |
208 serializeInternal(e, toDataType(i)); | |
209 }); | 341 }); |
210 } | 342 } |
211 | 343 |
212 private void serializePrimitive (T) (T value, string key) | 344 T deserialize (T) (Data data, string key = null) |
213 { | 345 { |
214 archive.archive(value, key, nextId); | 346 if (hasBegunSerializing && !hasBegunDeserializing) |
347 resetCounters(); | |
348 | |
349 if (!hasBegunDeserializing) | |
350 hasBegunDeserializing = true; | |
351 | |
352 if (!key) | |
353 key = nextKey; | |
354 | |
355 archive.beginUnarchiving(data); | |
356 return deserializeInternal!(T)(key); | |
357 } | |
358 | |
359 private T deserializeInternal (T) (string key) | |
360 { | |
361 static if (isTypedef!(T)) | |
362 return deserializeTypedef!(T)(key); | |
363 | |
364 else static if (isObject!(T)) | |
365 return deserializeObject!(T)(key); | |
366 | |
367 else static if (isStruct!(T)) | |
368 return deserializeStruct!(T)(key); | |
369 | |
370 else static if (isString!(T)) | |
371 return deserializeString!(T)(key); | |
372 | |
373 else static if (isArray!(T)) | |
374 return deserializeArray!(T)(key); | |
375 | |
376 else static if (isAssociativeArray!(T)) | |
377 return deserializeAssociativeArray!(T)(key); | |
378 | |
379 else static if (isPrimitive!(T)) | |
380 return deserializePrimitive!(T)(key); | |
381 | |
382 else static if (isPointer!(T)) | |
383 { | |
384 static if (isFunctionPointer!(T)) | |
385 goto error; | |
386 | |
387 return deserializePointer!(T)(key); | |
388 } | |
389 | |
390 else static if (isEnum!(T)) | |
391 return deserializeEnum!(T)(key); | |
392 | |
393 else | |
394 { | |
395 error: | |
396 throw new SerializationException(format!(`The type "`, T, `" cannot be deserialized.`), __FILE__, __LINE__); | |
397 } | |
398 } | |
399 | |
400 private T deserializeObject (T) (string key) | |
401 { | |
402 auto id = deserializeReference(key); | |
403 | |
404 if (auto reference = getDeserializedReference!(T)(id)) | |
405 return *reference; | |
406 | |
407 T value; | |
408 Object val = value; | |
409 | |
410 archive.unarchiveObject(key, id, val, { | |
411 triggerEvents(deserializing, value, { | |
412 value = cast(T) val; | |
413 auto runtimeType = value.classinfo.name; | |
414 | |
415 if (runtimeType in deserializers) | |
416 { | |
417 auto wrapper = getDeserializerWrapper!(T)(runtimeType); | |
418 wrapper(value, this, key); | |
419 } | |
420 | |
421 else static if (isSerializable!(T, Serializer)) | |
422 value.fromData(this, key); | |
423 | |
424 else | |
425 { | |
426 if (isBaseClass(value)) | |
427 throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a deserializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__); | |
428 | |
429 objectStructDeserializeHelper(value); | |
430 } | |
431 }); | |
432 }); | |
433 | |
434 addDeserializedReference(value, id); | |
435 | |
436 return value; | |
437 } | |
438 | |
439 private T deserializeStruct (T) (string key) | |
440 { | |
441 T value; | |
442 | |
443 archive.unarchiveStruct(key, { | |
444 triggerEvents(deserializing, value, { | |
445 auto type = toData(T.stringof); | |
446 | |
447 if (type in deserializers) | |
448 { | |
449 auto wrapper = getDeserializerWrapper!(T)(type); | |
450 wrapper(value, this, key); | |
451 } | |
452 | |
453 else | |
454 { | |
455 static if (isSerializable!(T, Serializer)) | |
456 value.fromData(this, key); | |
457 | |
458 else | |
459 objectStructDeserializeHelper(value); | |
460 } | |
461 }); | |
462 }); | |
463 | |
464 return value; | |
465 } | |
466 | |
467 private T deserializeString (T) (string key) | |
468 { | |
469 auto slice = deserializeSlice(key); | |
470 | |
471 if (auto tmp = getDeserializedSlice!(T)(slice)) | |
472 return *tmp; | |
473 | |
474 T value; | |
475 | |
476 if (slice.id != size_t.max) | |
477 { | |
478 static if (is(T == string)) | |
479 value = archive.unarchiveString(slice.id).toSlice(slice); | |
480 | |
481 else static if (is(T == wstring)) | |
482 value = archive.unarchiveWstring(slice.id).toSlice(slice); | |
483 | |
484 else static if (is(T == dstring)) | |
485 value = archive.unarchiveDstring(slice.id).toSlice(slice); | |
486 } | |
487 | |
488 else | |
489 { | |
490 static if (is(T == string)) | |
491 value = archive.unarchiveString(key, slice.id); | |
492 | |
493 else static if (is(T == wstring)) | |
494 value = archive.unarchiveWstring(key, slice.id); | |
495 | |
496 else static if (is(T == dstring)) | |
497 value = archive.unarchiveDstring(key, slice.id); | |
498 } | |
499 | |
500 addDeserializedSlice(value, slice.id); | |
501 | |
502 return value; | |
503 } | |
504 | |
505 private T deserializeArray (T) (string key) | |
506 { | |
507 auto slice = deserializeSlice(key); | |
508 | |
509 if (auto tmp = getDeserializedSlice!(T)(slice)) | |
510 return *tmp; | |
511 | |
512 T value; | |
513 | |
514 auto dg = (size_t length) { | |
515 value.length = length; | |
516 | |
517 foreach (i, ref e ; value) | |
518 e = deserializeInternal!(typeof(e))(toData(i)); | |
519 }; | |
520 | |
521 if (slice.id != size_t.max) | |
522 { | |
523 archive.unarchiveArray(slice.id, dg); | |
524 addDeserializedSlice(value, slice.id); | |
525 | |
526 return value.toSlice(slice); | |
527 } | |
528 | |
529 else | |
530 { | |
531 slice.id = archive.unarchiveArray(key, dg); | |
532 | |
533 if (auto a = slice.id in deserializedSlices) | |
534 return cast(T) *a; | |
535 | |
536 addDeserializedSlice(value, slice.id); | |
537 | |
538 return value; | |
539 } | |
540 } | |
541 | |
542 private T deserializeAssociativeArray (T) (string key) | |
543 { | |
544 T value; | |
545 | |
546 alias KeyTypeOfAssociativeArray!(T) Key; | |
547 alias ValueTypeOfAssociativeArray!(T) Value; | |
548 | |
549 archive.unarchiveAssociativeArray(key, (size_t length) { | |
550 for (size_t i = 0; i < length; i++) | |
551 { | |
552 Key aaKey; | |
553 Value aaValue; | |
554 auto k = toData(i); | |
555 | |
556 archive.unarchiveAssociativeArrayKey(k, { | |
557 aaKey = deserializeInternal!(Key)(k); | |
558 }); | |
559 | |
560 archive.unarchiveAssociativeArrayValue(k, { | |
561 aaValue = deserializeInternal!(Value)(k); | |
562 }); | |
563 | |
564 value[aaKey] = aaValue; | |
565 } | |
566 }); | |
567 | |
568 return value; | |
569 } | |
570 | |
571 /* auto id = deserializeReference(key); | |
572 | |
573 if (auto reference = getDeserializedReference!(T)(id)) | |
574 return *reference; | |
575 | |
576 T value; | |
577 Object val = value; | |
578 | |
579 archive.unarchiveObject(key, id, val, { | |
580 triggerEvents(deserializing, value, { | |
581 value = cast(T) val; | |
582 auto runtimeType = value.classinfo.name; | |
583 | |
584 if (runtimeType in deserializers) | |
585 { | |
586 auto wrapper = getDeserializerWrapper!(T)(runtimeType); | |
587 wrapper(value, this, key); | |
588 } | |
589 | |
590 else static if (isSerializable!(T, Serializer)) | |
591 value.fromData(this, key); | |
592 | |
593 else | |
594 { | |
595 if (isBaseClass(value)) | |
596 throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a deserializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__); | |
597 | |
598 objectStructDeserializeHelper(value); | |
599 } | |
600 }); | |
601 }); | |
602 | |
603 addDeserializedReference(value, id); | |
604 | |
605 return value; | |
606 */ | |
607 | |
608 private T deserializePointer (T) (string key) | |
609 { | |
610 auto id = deserializeReference(key); | |
611 | |
612 if (auto reference = getDeserializedReference!(T)(id)) | |
613 return *reference; | |
614 | |
615 T value = new BaseTypeOfPointer!(T); | |
616 | |
617 id = archive.unarchivePointer(key, { | |
618 if (key in deserializers) | |
619 { | |
620 auto wrapper = getDeserializerWrapper!(T)(key); | |
621 wrapper(value, this, key); | |
622 } | |
623 | |
624 else static if (isSerializable!(T, Serializer)) | |
625 value.fromData(this, key); | |
626 | |
627 else | |
628 { | |
629 static if (isVoid!(BaseTypeOfPointer!(T))) | |
630 throw new SerializationException(`The value with the key "` ~ to!(string)(key) ~ `"` ~ format!(` of the type "`, T, `" cannot be deserialized on its own, either implement orange.serialization.Serializable.isSerializable or register a deserializer.`), __FILE__, __LINE__); | |
631 | |
632 else | |
633 *value = deserializeInternal!(BaseTypeOfPointer!(T))(nextKey); | |
634 } | |
635 }); | |
636 | |
637 addDeserializedReference(value, id); | |
638 | |
639 return value; | |
640 } | |
641 | |
642 private T deserializeEnum (T) (string key) | |
643 { | |
644 alias BaseTypeOfEnum!(T) Enum; | |
645 | |
646 const functionName = toUpper(Enum.stringof[0]) ~ Enum.stringof[1 .. $]; | |
647 mixin("return cast(T) archive.unarchiveEnum" ~ functionName ~ "(key);"); | |
648 } | |
649 | |
650 private T deserializePrimitive (T) (string key) | |
651 { | |
652 const functionName = toUpper(T.stringof[0]) ~ T.stringof[1 .. $]; | |
653 mixin("return archive.unarchive" ~ functionName ~ "(key);"); | |
654 } | |
655 | |
656 private T deserializeTypedef (T) (string key) | |
657 { | |
658 T value; | |
659 | |
660 archive.unarchiveTypedef!(T)(key, { | |
661 value = cast(T) deserializeInternal!(BaseTypeOfTypedef!(T))(nextKey); | |
662 }); | |
663 | |
664 return value; | |
665 } | |
666 | |
667 private Id deserializeReference (string key) | |
668 { | |
669 return archive.unarchiveReference(key); | |
670 } | |
671 | |
672 private Slice deserializeSlice (string key) | |
673 { | |
674 return archive.unarchiveSlice(key); | |
215 } | 675 } |
216 | 676 |
217 private void objectStructSerializeHelper (T) (ref T value) | 677 private void objectStructSerializeHelper (T) (ref T value) |
218 { | 678 { |
219 static assert(isStruct!(T) || isObject!(T), format!(`The given value of the type "`, T, `" is not a valid type, the only valid types for this method are objects and structs.`)); | 679 static assert(isStruct!(T) || isObject!(T), format!(`The given value of the type "`, T, `" is not a valid type, the only valid types for this method are objects and structs.`)); |
225 | 685 |
226 static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) | 686 static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) |
227 { | 687 { |
228 alias typeof(T.tupleof[i]) Type; | 688 alias typeof(T.tupleof[i]) Type; |
229 Type v = value.tupleof[i]; | 689 Type v = value.tupleof[i]; |
230 serializeInternal(v, toDataType(field)); | 690 serializeInternal(v, toData(field)); |
231 } | 691 } |
232 } | 692 } |
233 | 693 |
234 static if (isObject!(T) && !is(T == Object)) | 694 static if (isObject!(T) && !is(T == Object)) |
235 serializeBaseTypes(value); | 695 serializeBaseTypes(value); |
245 const field = nameOfFieldAt!(T, i); | 705 const field = nameOfFieldAt!(T, i); |
246 | 706 |
247 static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) | 707 static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) |
248 { | 708 { |
249 alias TypeOfField!(T, field) Type; | 709 alias TypeOfField!(T, field) Type; |
250 auto fieldValue = deserializeInternal!(Type)(toDataType(field)); | 710 auto fieldValue = deserializeInternal!(Type)(toData(field)); |
251 value.tupleof[i] = fieldValue; | 711 value.tupleof[i] = fieldValue; |
252 } | 712 } |
253 } | 713 } |
254 | 714 |
255 static if (isObject!(T) && !is(T == Object)) | 715 static if (isObject!(T) && !is(T == Object)) |
257 } | 717 } |
258 | 718 |
259 private void serializeBaseTypes (T : Object) (T value) | 719 private void serializeBaseTypes (T : Object) (T value) |
260 { | 720 { |
261 alias BaseTypeTupleOf!(T)[0] Base; | 721 alias BaseTypeTupleOf!(T)[0] Base; |
262 | 722 |
263 static if (!is(Base == Object)) | 723 static if (!is(Base == Object)) |
264 { | 724 { |
265 archive.archiveBaseClass(Base.stringof, nextKey, nextId); | 725 archive.archiveBaseClass(Base.stringof, nextKey, nextId); |
266 Base base = value; | 726 Base base = value; |
267 objectStructSerializeHelper(base); | 727 objectStructSerializeHelper(base); |
276 { | 736 { |
277 archive.unarchiveBaseClass!(Base)(nextKey); | 737 archive.unarchiveBaseClass!(Base)(nextKey); |
278 Base base = value; | 738 Base base = value; |
279 objectStructDeserializeHelper(base); | 739 objectStructDeserializeHelper(base); |
280 } | 740 } |
741 } | |
742 | |
743 private void addSerializedReference (T) (T value, Id id) | |
744 { | |
745 static assert(isReference!(T), format!(`The given type "`, T, `" is not a reference type, i.e. object or pointer.`)); | |
746 | |
747 serializedReferences[cast(void*) value] = id; | |
748 } | |
749 | |
750 private void addDeserializedReference (T) (T value, Id id) | |
751 { | |
752 static assert(isReference!(T), format!(`The given type "`, T, `" is not a reference type, i.e. object or pointer.`)); | |
753 | |
754 deserializedReferences[id] = cast(void*) value; | |
755 } | |
756 | |
757 private void addDeserializedSlice (T) (T value, Id id) | |
758 { | |
759 static assert(isArray!(T) || isString!(T), format!(`The given type "`, T, `" is not a slice type, i.e. array or string.`)); | |
760 | |
761 deserializedSlices[id] = value; | |
762 } | |
763 | |
764 private Id getSerializedReference (T) (T value) | |
765 { | |
766 if (auto tmp = cast(void*) value in serializedReferences) | |
767 return *tmp; | |
768 | |
769 return Id.max; | |
770 } | |
771 | |
772 private T* getDeserializedReference (T) (Id id) | |
773 { | |
774 if (auto reference = id in deserializedReferences) | |
775 return cast(T*) reference; | |
776 | |
777 return null; | |
778 } | |
779 | |
780 private T* getDeserializedSlice (T) (Slice slice) | |
781 { | |
782 if (auto array = slice.id in deserializedSlices) | |
783 return &(cast(T) *array)[slice.offset .. slice.offset + slice.length]; // dereference the array, cast it to the right type, | |
784 // slice it and then return a pointer to the result } | |
785 return null; | |
786 } | |
787 | |
788 private T* getDeserializedArray (T) (Id id) | |
789 { | |
790 if (auto array = id in deserializedSlices) | |
791 return cast(T*) array; | |
792 } | |
793 | |
794 private T[] toSlice (T) (T[] array, Slice slice) | |
795 { | |
796 return array[slice.offset .. slice.offset + slice.length]; | |
281 } | 797 } |
282 | 798 |
283 private SerializeRegisterWrapper!(T, Serializer) getSerializerWrapper (T) (string type) | 799 private SerializeRegisterWrapper!(T, Serializer) getSerializerWrapper (T) (string type) |
284 { | 800 { |
285 auto wrapper = cast(SerializeRegisterWrapper!(T, Serializer)) serializers[type]; | 801 auto wrapper = cast(SerializeRegisterWrapper!(T, Serializer)) serializers[type]; |
318 private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, string) func) | 834 private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, string) func) |
319 { | 835 { |
320 return new DeserializeRegisterWrapper!(T, Serializer)(func); | 836 return new DeserializeRegisterWrapper!(T, Serializer)(func); |
321 } | 837 } |
322 | 838 |
839 private void addSerializedArray (Array array, Id id) | |
840 { | |
841 serializedArrays[id] = array; | |
842 } | |
843 | |
844 private void postProcessArrays () | |
845 { | |
846 bool foundSlice = true; | |
847 | |
848 foreach (sliceKey, slice ; serializedArrays) | |
849 { | |
850 foreach (arrayKey, array ; serializedArrays) | |
851 { | |
852 if (slice.isSliceOf(array) && slice != array) | |
853 { | |
854 auto s = Slice(slice.length, (slice.ptr - array.ptr) / slice.elementSize); | |
855 archive.archiveSlice(s, sliceKey, arrayKey); | |
856 foundSlice = true; | |
857 break; | |
858 } | |
859 | |
860 else | |
861 foundSlice = false; | |
862 } | |
863 | |
864 if (!foundSlice) | |
865 archive.postProcessArray(sliceKey); | |
866 } | |
867 } | |
868 | |
869 private void postProcess () | |
870 { | |
871 postProcessArrays(); | |
872 } | |
873 | |
323 private template arrayToString (T) | 874 private template arrayToString (T) |
324 { | 875 { |
325 const arrayToString = BaseTypeOfArray!(T).stringof; | 876 const arrayToString = ElementTypeOfArray!(T).stringof; |
326 } | 877 } |
327 | 878 |
328 private bool isBaseClass (T) (T value) | 879 private bool isBaseClass (T) (T value) |
329 { | 880 { |
330 auto name = value.classinfo.name; | 881 auto name = value.classinfo.name; |
331 auto index = name.lastIndexOf('.'); | 882 auto index = name.lastIndexOf('.'); |
332 | 883 |
333 return T.stringof != name[index + 1 .. $]; | 884 return T.stringof != name[index + 1 .. $]; |
334 } | 885 } |
335 | 886 |
336 private size_t nextId () | 887 private Id nextId () |
337 { | 888 { |
338 return idCounter++; | 889 return idCounter++; |
339 } | 890 } |
340 | 891 |
341 private string nextKey () | 892 private string nextKey () |
342 { | 893 { |
343 return toDataType(keyCounter++); | 894 return toData(keyCounter++); |
344 } | 895 } |
345 | 896 |
346 private void resetCounters () | 897 private void resetCounters () |
347 { | 898 { |
348 keyCounter = 0; | 899 keyCounter = 0; |
349 idCounter = 0; | 900 idCounter = 0; |
350 } | 901 } |
351 | 902 |
352 private string toDataType (T) (T value) | 903 private string toData (T) (T value) |
353 { | 904 { |
354 return to!(string)(value); | 905 return to!(string)(value); |
355 } | 906 } |
356 | 907 |
357 private void triggerEvent (string name, T) (T value) | 908 private void triggerEvent (string name, T) (T value) |
376 if (mode == serializing) | 927 if (mode == serializing) |
377 triggerEvent!(onSerializingField)(value); | 928 triggerEvent!(onSerializingField)(value); |
378 | 929 |
379 else | 930 else |
380 triggerEvent!(onDeserializingField)(value); | 931 triggerEvent!(onDeserializingField)(value); |
381 | 932 |
382 dg(); | 933 dg(); |
383 | 934 |
384 if (mode == serializing) | 935 if (mode == serializing) |
385 triggerEvent!(onSerializedField)(value); | 936 triggerEvent!(onSerializedField)(value); |
386 | 937 |
387 else | 938 else |
388 triggerEvent!(onDeserializedField)(value); | 939 triggerEvent!(onDeserializedField)(value); |
403 } | 954 } |
404 | 955 |
405 return annotations; | 956 return annotations; |
406 } | 957 } |
407 } | 958 } |
959 | |
960 | |
961 | |
962 debug (OrangeUnitTest) | |
963 { | |
964 import orange.serialization.archives.XMLArchive; | |
965 | |
966 enum Foo { a, b, c } | |
967 typedef int Int; | |
968 | |
969 class A {} | |
970 struct B {} | |
971 class C { string str; } | |
972 class D { int[] arr; } | |
973 class E { int[int] aa; } | |
974 class F { int value; int* ptr; } | |
975 class G { Foo foo; } | |
976 | |
977 class H | |
978 { | |
979 bool bool_; | |
980 byte byte_; | |
981 //cdouble cdouble_; // currently not suppported by to!() | |
982 //cent cent_; // currently not implemented but a reserved keyword | |
983 //cfloat cfloat_; // currently not suppported by to!() | |
984 char char_; | |
985 //creal creal_; // currently not suppported by to!() | |
986 dchar dchar_; | |
987 double double_; | |
988 float float_; | |
989 //idouble idouble_; // currently not suppported by to!() | |
990 //ifloat ifloat_; // currently not suppported by to!() | |
991 int int_; | |
992 //ireal ireal_; // currently not suppported by to!() | |
993 long long_; | |
994 real real_; | |
995 short short_; | |
996 ubyte ubyte_; | |
997 //ucent ucent_; // currently not implemented but a reserved keyword | |
998 uint uint_; | |
999 ulong ulong_; | |
1000 ushort ushort_; | |
1001 wchar wchar_; | |
1002 | |
1003 equals_t opEquals (Object other) | |
1004 { | |
1005 if (auto o = cast(H) other) | |
1006 { | |
1007 return bool_ == o.bool_ && | |
1008 byte_ == o.byte_ && | |
1009 //cdouble_ == o.cdouble_ && // currently not suppported by to!() | |
1010 //cent_ == o.cent_ && // currently not implemented but a reserved keyword | |
1011 //cfloat_ == o.cfloat_ && // currently not suppported by to!() | |
1012 char_ == o.char_ && | |
1013 //creal_ == o.creal_ && // currently not suppported by to!() | |
1014 dchar_ == o.dchar_ && | |
1015 double_ == o.double_ && | |
1016 float_ == o.float_ && | |
1017 //idouble_ == o.idouble_ && // currently not suppported by to!() | |
1018 //ifloat_ == o.ifloat_ && // currently not suppported by to!() | |
1019 int_ == o.int_ && | |
1020 //ireal_ == o.ireal_ && // currently not suppported by to!() | |
1021 long_ == o.long_ && | |
1022 real_ == o.real_ && | |
1023 short_ == o.short_ && | |
1024 ubyte_ == o.ubyte_ && | |
1025 //ucent_ == o.ucent_ && // currently not implemented but a reserved keyword | |
1026 uint_ == o.uint_ && | |
1027 ulong_ == o.ulong_ && | |
1028 ushort_ == o.ushort_ && | |
1029 wchar_ == o.wchar_; | |
1030 } | |
1031 | |
1032 return false; | |
1033 } | |
1034 } | |
1035 | |
1036 class I | |
1037 { | |
1038 Int a; | |
1039 } | |
1040 | |
1041 void main () | |
1042 { | |
1043 auto archive = new XMLArchive!(char); | |
1044 auto serializer = new Serializer(archive); | |
1045 string data; | |
1046 | |
1047 void serializeObject () | |
1048 { | |
1049 serializer.reset; | |
1050 data = `<?xml version="1.0" encoding="UTF-8"?> | |
1051 <archive type="org.dsource.orange.xml" version="1.0.0"> | |
1052 <data> | |
1053 <struct type="B" key="0"/> | |
1054 </data> | |
1055 </archive>`; | |
1056 | |
1057 serializer.serialize(B()); | |
1058 assert(archive.data == data); | |
1059 } | |
1060 | |
1061 void serializeStruct () | |
1062 { | |
1063 serializer.reset; | |
1064 data = `<?xml version="1.0" encoding="UTF-8"?> | |
1065 <archive type="org.dsource.orange.xml" version="1.0.0"> | |
1066 <data> | |
1067 <struct type="B" key="0"/> | |
1068 </data> | |
1069 </archive>`; | |
1070 | |
1071 serializer.serialize(B()); | |
1072 assert(archive.data == data); | |
1073 } | |
1074 | |
1075 // Struct | |
1076 | |
1077 | |
1078 | |
1079 // String | |
1080 | |
1081 serializer.reset; | |
1082 data = `<?xml version="1.0" encoding="UTF-8"?> | |
1083 <archive type="org.dsource.orange.xml" version="1.0.0"> | |
1084 <data> | |
1085 <object runtimeType="orange.serialization.Serializer.C" type="C" key="0" id="0"> | |
1086 <string type="char" length="3" key="str" id="1">foo</string> | |
1087 </object> | |
1088 </data> | |
1089 </archive>`; | |
1090 | |
1091 auto c = new C; | |
1092 c.str = "foo"; | |
1093 serializer.serialize(c); | |
1094 assert(archive.data == data); | |
1095 | |
1096 // Deserializing | |
1097 | |
1098 auto cDeserialized = serializer.deserialize!(C)(data); | |
1099 assert(c.str == cDeserialized.str); | |
1100 | |
1101 // Array | |
1102 | |
1103 serializer.reset; | |
1104 data = `<?xml version="1.0" encoding="UTF-8"?> | |
1105 <archive type="org.dsource.orange.xml" version="1.0.0"> | |
1106 <data> | |
1107 <object runtimeType="orange.serialization.Serializer.D" type="D" key="0" id="0"> | |
1108 <array type="int" length="6" key="arr" id="1"> | |
1109 <int key="0">27</int> | |
1110 <int key="1">382</int> | |
1111 <int key="2">283</int> | |
1112 <int key="3">3820</int> | |
1113 <int key="4">32</int> | |
1114 <int key="5">832</int> | |
1115 </array> | |
1116 </object> | |
1117 </data> | |
1118 </archive>`; | |
1119 | |
1120 auto d = new D; | |
1121 d.arr = [27, 382, 283, 3820, 32, 832]; | |
1122 serializer.serialize(d); | |
1123 assert(archive.data == data); | |
1124 | |
1125 // Deserializing | |
1126 | |
1127 auto dDeserialized = serializer.deserialize!(D)(data); | |
1128 assert(d.arr == dDeserialized.arr); | |
1129 | |
1130 // Associative Array | |
1131 | |
1132 serializer.reset(); | |
1133 data = `<?xml version="1.0" encoding="UTF-8"?> | |
1134 <archive type="org.dsource.orange.xml" version="1.0.0"> | |
1135 <data> | |
1136 <object runtimeType="orange.serialization.Serializer.E" type="E" key="0" id="0"> | |
1137 <associativeArray keyType="int" valueType="int" length="4" key="aa"> | |
1138 <key key="0"> | |
1139 <int key="0">1</int> | |
1140 </key> | |
1141 <value key="0"> | |
1142 <int key="0">2</int> | |
1143 </value> | |
1144 <key key="1"> | |
1145 <int key="1">3</int> | |
1146 </key> | |
1147 <value key="1"> | |
1148 <int key="1">4</int> | |
1149 </value> | |
1150 <key key="2"> | |
1151 <int key="2">6</int> | |
1152 </key> | |
1153 <value key="2"> | |
1154 <int key="2">7</int> | |
1155 </value> | |
1156 <key key="3"> | |
1157 <int key="3">39</int> | |
1158 </key> | |
1159 <value key="3"> | |
1160 <int key="3">472</int> | |
1161 </value> | |
1162 </associativeArray> | |
1163 </object> | |
1164 </data> | |
1165 </archive>`; | |
1166 | |
1167 auto e = new E; | |
1168 e.aa = [3 : 4, 1 : 2, 39 : 472, 6 : 7]; | |
1169 serializer.serialize(e); | |
1170 assert(archive.data == data); | |
1171 | |
1172 // Deserializing | |
1173 | |
1174 auto eDeserialized = serializer.deserialize!(E)(data); | |
1175 //assert(e.aa == eDeserialized.aa); // cannot compare associative array | |
1176 | |
1177 // Pointer | |
1178 | |
1179 serializer.reset(); | |
1180 data = `<?xml version="1.0" encoding="UTF-8"?> | |
1181 <archive type="org.dsource.orange.xml" version="1.0.0"> | |
1182 <data> | |
1183 <object runtimeType="orange.serialization.Serializer.F" type="F" key="0" id="0"> | |
1184 <pointer key="ptr" id="2"> | |
1185 <int key="1">9</int> | |
1186 </pointer> | |
1187 <int key="value">9</int> | |
1188 </object> | |
1189 </data> | |
1190 </archive>`; | |
1191 | |
1192 auto f = new F; | |
1193 f.value = 9; | |
1194 f.ptr = &f.value; | |
1195 serializer.serialize(f); | |
1196 //assert(archive.data == data); // this is not a reliable comparison, the order of int and pointer is not reliable | |
1197 | |
1198 // Deserializing | |
1199 | |
1200 auto fDeserialized = serializer.deserialize!(F)(data); | |
1201 assert(*f.ptr == *fDeserialized.ptr); | |
1202 | |
1203 // Enum | |
1204 | |
1205 serializer.reset(); | |
1206 data = `<?xml version="1.0" encoding="UTF-8"?> | |
1207 <archive type="org.dsource.orange.xml" version="1.0.0"> | |
1208 <data> | |
1209 <object runtimeType="orange.serialization.Serializer.G" type="G" key="0" id="0"> | |
1210 <enum type="Foo" baseType="int" key="foo">1</enum> | |
1211 </object> | |
1212 </data> | |
1213 </archive>`; | |
1214 | |
1215 auto g = new G; | |
1216 g.foo = Foo.b; | |
1217 serializer.serialize(g); | |
1218 assert(archive.data == data); | |
1219 | |
1220 // Deserializing | |
1221 | |
1222 auto gDeserialized = serializer.deserialize!(G)(data); | |
1223 assert(g.foo == gDeserialized.foo); | |
1224 | |
1225 // Primitives | |
1226 | |
1227 serializer.reset; | |
1228 data = `<?xml version="1.0" encoding="UTF-8"?> | |
1229 <archive type="org.dsource.orange.xml" version="1.0.0"> | |
1230 <data> | |
1231 <object runtimeType="orange.serialization.Serializer.H" type="H" key="0" id="0"> | |
1232 <byte key="byte_">1</byte> | |
1233 <char key="char_">a</char> | |
1234 <dchar key="dchar_">b</dchar> | |
1235 <double key="double_">0</double> | |
1236 <float key="float_">0</float> | |
1237 <int key="int_">1</int> | |
1238 <long key="long_">1</long> | |
1239 <real key="real_">0</real> | |
1240 <short key="short_">1</short> | |
1241 <ubyte key="ubyte_">1</ubyte> | |
1242 <uint key="uint_">1</uint> | |
1243 <ulong key="ulong_">1</ulong> | |
1244 <ushort key="ushort_">1</ushort> | |
1245 <wchar key="wchar_">c</wchar> | |
1246 <bool key="bool_">true</bool> | |
1247 </object> | |
1248 </data> | |
1249 </archive>`; | |
1250 | |
1251 auto h = new H; | |
1252 | |
1253 h.bool_ = true; | |
1254 h.byte_ = 1; | |
1255 h.char_ = 'a'; | |
1256 //h.cdouble_ = 0.0 + 0.0 * 1.0i; // currently not suppported by to!() | |
1257 //h.cfloat_ = 0.0f + 0.0f * 1.0i; // currently not suppported by to!() | |
1258 //h.creal_ = 0.0 + 0.0 * 1.0i; // currently not suppported by to!() | |
1259 h.dchar_ = 'b'; | |
1260 h.double_ = 0.0; | |
1261 h.float_ = 0.0f; | |
1262 //h.idouble_ = 0.0 * 1.0i; // currently not suppported by to!() | |
1263 //h.ifloat_ = 0.0f * 1.0i; // currently not suppported by to!() | |
1264 h.int_ = 1; | |
1265 //h.ireal_ = 0.0 * 1.0i; // currently not suppported by to!() | |
1266 h.long_ = 1L; | |
1267 h.real_ = 0.0; | |
1268 h.short_ = 1; | |
1269 h.ubyte_ = 1U; | |
1270 h.uint_ = 1U; | |
1271 h.ulong_ = 1LU; | |
1272 h.ushort_ = 1U; | |
1273 h.wchar_ = 'c'; | |
1274 | |
1275 serializer.serialize(h); | |
1276 //assert(archive.data == data); // this is not a reliable comparison | |
1277 | |
1278 // Deserializing | |
1279 | |
1280 auto hDeserialized = serializer.deserialize!(H)(data); | |
1281 assert(h == hDeserialized); | |
1282 | |
1283 // Typedef | |
1284 | |
1285 serializer.reset(); | |
1286 data = `<?xml version="1.0" encoding="UTF-8"?> | |
1287 <archive type="org.dsource.orange.xml" version="1.0.0"> | |
1288 <data> | |
1289 <object runtimeType="orange.serialization.Serializer.I" type="I" key="0" id="0"> | |
1290 <typedef type="Int" key="a"> | |
1291 <int key="1">1</int> | |
1292 </typedef> | |
1293 </object> | |
1294 </data> | |
1295 </archive>`; | |
1296 | |
1297 auto i = new I; | |
1298 i.a = 1; | |
1299 serializer.serialize(i); | |
1300 assert(archive.data == data); | |
1301 | |
1302 // Deserializing | |
1303 | |
1304 auto iDeserialized = serializer.deserialize!(I)(data); | |
1305 assert(i.a == iDeserialized.a); | |
1306 | |
1307 println("unit tests successful"); | |
1308 } | |
1309 } |