Mercurial > projects > orange
annotate orange/serialization/Serializer.d @ 8:613a0bb20207
Now works with dmd 1.062
author | Jacob Carlborg <doob@me.com> |
---|---|
date | Wed, 21 Jul 2010 13:44:08 +0200 |
parents | 470ab5270d0c |
children | 99c52d46822a |
rev | line source |
---|---|
0 | 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.Serializer; | |
8 | |
9 version (Tango) | |
10 { | |
11 import tango.util.Convert : to, ConversionException; | |
12 } | |
13 | |
14 import orange.serialization._; | |
15 import orange.serialization.archives._; | |
16 import orange.util._; | |
17 | |
18 private | |
19 { | |
20 alias orange.util.CTFE.contains ctfeContains; | |
21 | |
22 private enum Mode | |
23 { | |
24 serializing, | |
25 deserializing | |
26 } | |
27 | |
28 alias Mode.serializing serializing; | |
29 alias Mode.deserializing deserializing; | |
30 } | |
31 | |
32 class Serializer (ArchiveType : IArchive) | |
33 { | |
34 static assert(isArchive!(ArchiveType), format!(`The type "`, ArchiveType, `" does not implement the necessary methods to be an archive.`)); | |
35 | |
36 private | |
37 { | |
38 ArchiveType archive; | |
39 alias ArchiveType.DataType DataType; | |
40 | |
41 RegisterBase[string] serializers; | |
42 RegisterBase[string] deserializers; | |
43 | |
44 size_t keyCounter; | |
45 | |
46 bool hasBegunSerializing; | |
47 bool hasBegunDeserializing; | |
48 } | |
49 | |
50 this () | |
51 { | |
52 archive = new ArchiveType; | |
53 } | |
54 | |
55 void registerSerializer (T) (string type, void delegate (T, Serializer, DataType) dg) | |
56 { | |
57 serializers[type] = toSerializeRegisterWrapper(dg); | |
58 } | |
59 | |
60 void registerSerializer (T) (string type, void function (T, Serializer, DataType) func) | |
61 { | |
62 serializers[type] = toSerializeRegisterWrapper(func); | |
63 } | |
64 | |
65 void registerDeserializer (T) (string type, void delegate (ref T, Serializer, DataType) dg) | |
66 { | |
67 deserializers[type] = toDeserializeRegisterWrapper(dg); | |
68 } | |
69 | |
70 void registerDeserializer (T) (string type, void function (ref T, Serializer, DataType) func) | |
71 { | |
72 deserializers[type] = toDeserializeRegisterWrapper(func); | |
73 } | |
74 | |
75 void reset () | |
76 { | |
77 hasBegunSerializing = false; | |
78 hasBegunDeserializing = false; | |
79 resetCounters(); | |
80 | |
81 archive.reset; | |
82 } | |
83 | |
84 DataType serialize (T) (T value, DataType key = null) | |
85 { | |
86 if (!hasBegunSerializing) | |
87 hasBegunSerializing = true; | |
88 | |
89 if (!key) | |
90 key = nextKey; | |
91 | |
92 archive.beginArchiving(); | |
93 | |
94 static if (isTypeDef!(T)) | |
95 serializeTypeDef(value, key); | |
96 | |
97 static if (isObject!(T)) | |
98 serializeObject(value, key); | |
99 | |
100 else static if (isStruct!(T)) | |
101 serializeStruct(value, key); | |
102 | |
103 else static if (isString!(T)) | |
104 serializeString(value, key); | |
105 | |
106 else static if (isArray!(T)) | |
107 serializeArray(value, key); | |
108 | |
109 else static if (isAssociativeArray!(T)) | |
110 serializeAssociativeArray(value, key); | |
111 | |
112 else static if (isPrimitive!(T)) | |
113 serializePrimitive(value, key); | |
114 | |
115 else static if (isPointer!(T)) | |
116 { | |
117 static if (isFunctionPointer!(T)) | |
2 | 118 goto error; |
0 | 119 |
120 else | |
121 serializePointer(value, key); | |
2 | 122 } |
0 | 123 |
124 else static if (isEnum!(T)) | |
125 serializeEnum(value, key); | |
126 | |
127 else | |
2 | 128 { |
129 error: | |
130 throw new SerializationException(format!(`The type "`, T, `" cannot be serialized.`), __FILE__, __LINE__); | |
131 } | |
0 | 132 |
133 return archive.data; | |
134 } | |
135 | |
136 private void serializeObject (T) (T value, DataType key) | |
2 | 137 { |
0 | 138 triggerEvents(serializing, value, { |
139 archive.archive(value, key, { | |
140 auto runtimeType = value.classinfo.name; | |
141 | |
142 if (runtimeType in serializers) | |
143 { | |
144 auto wrapper = getSerializerWrapper!(T)(runtimeType); | |
145 wrapper(value, this, key); | |
146 } | |
147 | |
148 else static if (isSerializable!(T, ArchiveType)) | |
149 value.toData(this, key); | |
150 | |
151 else | |
152 { | |
153 if (isBaseClass(value)) | |
154 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__); | |
155 | |
2 | 156 objectStructSerializeHelper(value); |
0 | 157 } |
158 }); | |
159 }); | |
160 } | |
161 | |
162 private void serializeStruct (T) (T value, DataType key) | |
163 { | |
164 triggerEvents(serializing, value, { | |
165 archive.archive(value, key, { | |
166 auto type = toDataType(T.stringof); | |
167 | |
168 if (type in serializers) | |
169 { | |
170 auto wrapper = getSerializerWrapper!(T)(type); | |
171 wrapper(value, this, key); | |
172 } | |
173 | |
174 else | |
175 { | |
176 static if (isSerializable!(T, ArchiveType)) | |
177 value.toData(this, key); | |
178 | |
179 else | |
2 | 180 objectStructSerializeHelper(value); |
0 | 181 } |
182 }); | |
183 }); | |
184 } | |
185 | |
186 private void serializeString (T) (T value, DataType key) | |
187 { | |
188 archive.archive(value, key); | |
189 } | |
190 | |
191 private void serializeArray (T) (T value, DataType key) | |
192 { | |
193 archive.archive(value, key, { | |
194 foreach (i, e ; value) | |
195 archive.archive(e, toDataType(i)); | |
196 }); | |
197 } | |
198 | |
199 private void serializeAssociativeArray (T) (T value, DataType key) | |
200 { | |
201 archive.archive(value, key, { | |
202 foreach(k, v ; value) | |
203 archive.archive(v, toDataType(k)); | |
204 }); | |
205 } | |
206 | |
207 private void serializePointer (T) (T value, DataType key) | |
208 { | |
209 archive.archive(value, key, { | |
210 if (key in serializers) | |
211 { | |
212 auto wrapper = getSerializerWrapper!(T)(key); | |
213 wrapper(value, this, key); | |
214 } | |
215 | |
216 else static if (isSerializable!(T, ArchiveType)) | |
217 value.toData(this, key); | |
218 | |
219 else | |
220 { | |
221 static if (isVoid!(BaseTypeOfPointer!(T))) | |
222 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__); | |
223 | |
224 else | |
225 serialize(*value, key); | |
226 } | |
227 }); | |
228 } | |
229 | |
230 private void serializeEnum (T) (T value, DataType key) | |
231 { | |
232 archive.archive(value, key); | |
233 } | |
234 | |
235 private void serializePrimitive (T) (T value, DataType key) | |
236 { | |
237 archive.archive(value, key); | |
238 } | |
239 | |
240 private void serializeTypeDef (T) (T value, DataType key) | |
241 { | |
242 archive.archive(value, key, { | |
243 serialize!(BaseTypeOfTypeDef!(T))(value, key); | |
244 }); | |
245 } | |
246 | |
247 T deserialize (T) (DataType data, DataType key = null) | |
248 { | |
249 if (hasBegunSerializing && !hasBegunDeserializing) | |
250 resetCounters(); | |
251 | |
252 if (!hasBegunDeserializing) | |
253 hasBegunDeserializing = true; | |
254 | |
255 if (!key) | |
256 key = nextKey; | |
257 | |
258 archive.beginUnarchiving(data); | |
259 return deserializeInternal!(T)(key); | |
260 } | |
261 | |
262 private T deserializeInternal (T) (DataType key) | |
263 { | |
264 static if (isTypeDef!(T)) | |
265 return deserializeTypeDef!(T)(key); | |
266 | |
267 else static if (isObject!(T)) | |
268 return deserializeObject!(T)(key); | |
269 | |
270 else static if (isStruct!(T)) | |
271 return deserializeStruct!(T)(key); | |
272 | |
273 else static if (isString!(T)) | |
274 return deserializeString!(T)(key); | |
275 | |
276 else static if (isArray!(T)) | |
277 return deserializeArray!(T)(key); | |
278 | |
279 else static if (isAssociativeArray!(T)) | |
280 return deserializeAssociativeArray!(T)(key); | |
281 | |
282 else static if (isPrimitive!(T)) | |
283 return deserializePrimitive!(T)(key); | |
284 | |
285 else static if (isPointer!(T)) | |
2 | 286 { |
287 static if (isFunctionPointer!(T)) | |
288 goto error; | |
289 | |
0 | 290 return deserializePointer!(T)(key); |
2 | 291 } |
0 | 292 |
293 else static if (isEnum!(T)) | |
294 return deserializeEnum!(T)(key); | |
295 | |
296 else | |
2 | 297 { |
298 error: | |
299 throw new SerializationException(format!(`The type "`, T, `" cannot be deserialized.`), __FILE__, __LINE__); | |
300 } | |
0 | 301 } |
302 | |
303 private T deserializeObject (T) (DataType key) | |
304 { | |
2 | 305 T value = archive.unarchive!(T)(key, (T value) { |
0 | 306 triggerEvents(deserializing, value, { |
307 auto runtimeType = value.classinfo.name; | |
308 | |
309 if (runtimeType in deserializers) | |
310 { | |
311 auto wrapper = getDeserializerWrapper!(T)(runtimeType); | |
312 wrapper(value, this, key); | |
313 } | |
314 | |
315 else static if (isSerializable!(T, ArchiveType)) | |
316 value.fromData(this, key); | |
317 | |
318 else | |
319 { | |
320 if (isBaseClass(value)) | |
321 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__); | |
322 | |
2 | 323 objectStructDeserializeHelper(value); |
0 | 324 } |
325 }); | |
326 | |
327 return value; | |
328 }); | |
329 | |
330 return value; | |
331 } | |
332 | |
333 private T deserializeStruct (T) (DataType key) | |
334 { | |
335 return archive.unarchive!(T)(key, (T value) { | |
336 triggerEvents(deserializing, value, { | |
337 auto type = toDataType(T.stringof); | |
338 | |
339 if (type in deserializers) | |
340 { | |
341 auto wrapper = getDeserializerWrapper!(T)(type); | |
342 wrapper(value, this, key); | |
343 } | |
344 | |
345 else | |
346 { | |
347 static if (isSerializable!(T, ArchiveType)) | |
348 value.fromData(this, key); | |
349 | |
350 else | |
2 | 351 objectStructDeserializeHelper(value); |
0 | 352 } |
353 }); | |
354 | |
355 return value; | |
356 }); | |
357 } | |
358 | |
359 private T deserializeString (T) (DataType key) | |
360 { | |
361 return archive.unarchive!(T)(key); | |
362 } | |
363 | |
364 private T deserializeArray (T) (DataType key) | |
365 { | |
366 return archive.unarchive!(T)(key, (T value) { | |
367 foreach (i, ref e ; value) | |
368 e = archive.unarchive!(typeof(e))(toDataType(i)); | |
369 | |
370 return value; | |
371 }); | |
372 } | |
373 | |
374 private T deserializeAssociativeArray (T) (DataType key) | |
375 { | |
376 return archive.unarchive!(T)(key, (T value) { | |
377 foreach (k, v ; archive.unarchiveAssociativeArrayVisitor!(T)) | |
378 value[k] = v; | |
379 | |
380 return value; | |
381 }); | |
382 } | |
383 | |
384 private T deserializePointer (T) (DataType key) | |
385 { | |
386 return archive.unarchive!(T)(key, (T value) { | |
387 if (key in deserializers) | |
388 { | |
389 auto wrapper = getDeserializerWrapper!(T)(key); | |
390 wrapper(value, this, key); | |
391 } | |
392 | |
393 else static if (isSerializable!(T, ArchiveType)) | |
394 value.fromData(this, key); | |
395 | |
396 else | |
397 { | |
398 static if (isVoid!(BaseTypeOfPointer!(T))) | |
399 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__); | |
400 | |
401 else | |
402 *value = deserializeInternal!(BaseTypeOfPointer!(T))(key); | |
403 } | |
404 | |
405 return value; | |
406 }); | |
407 } | |
408 | |
409 private T deserializeEnum (T) (DataType key) | |
410 { | |
411 return archive.unarchive!(T)(key); | |
412 } | |
413 | |
414 private T deserializePrimitive (T) (DataType key) | |
415 { | |
416 return archive.unarchive!(T)(key); | |
417 } | |
418 | |
419 private T deserializeTypeDef (T) (DataType key) | |
420 { | |
421 return archive.unarchive!(T)(key, (T value) { | |
422 return deserializeInternal!(BaseTypeOfTypeDef!(T))(key); | |
423 }); | |
424 } | |
425 | |
3 | 426 private void objectStructSerializeHelper (T) (ref T value) |
427 { | |
428 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.`)); | |
8 | 429 const nonSerializedFields = collectAnnotations!(nonSerializedField, T); |
3 | 430 |
431 foreach (i, dummy ; typeof(T.tupleof)) | |
432 { | |
433 const field = nameOfFieldAt!(T, i); | |
434 | |
435 static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) | |
436 { | |
437 alias typeof(T.tupleof[i]) Type; | |
438 Type v = value.tupleof[i]; | |
439 serialize(v, toDataType(field)); | |
440 } | |
441 } | |
2 | 442 |
4
470ab5270d0c
Simplified the implementation of RegisterWrapper. Fixed: the base class of an object was not serialized
Jacob Carlborg <doob@me.com>
parents:
3
diff
changeset
|
443 static if (isObject!(T) && !is(T == Object)) |
470ab5270d0c
Simplified the implementation of RegisterWrapper. Fixed: the base class of an object was not serialized
Jacob Carlborg <doob@me.com>
parents:
3
diff
changeset
|
444 serializeBaseTypes(value); |
3 | 445 } |
446 | |
447 private void objectStructDeserializeHelper (T) (ref T value) | |
448 { | |
449 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.`)); | |
8 | 450 const nonSerializedFields = collectAnnotations!(nonSerializedField, T); |
3 | 451 |
452 foreach (i, dummy ; typeof(T.tupleof)) | |
453 { | |
454 const field = nameOfFieldAt!(T, i); | |
455 | |
456 static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) | |
457 { | |
458 alias TypeOfField!(T, field) Type; | |
459 auto fieldValue = deserializeInternal!(Type)(toDataType(field)); | |
460 value.tupleof[i] = fieldValue; | |
461 } | |
462 } | |
4
470ab5270d0c
Simplified the implementation of RegisterWrapper. Fixed: the base class of an object was not serialized
Jacob Carlborg <doob@me.com>
parents:
3
diff
changeset
|
463 |
470ab5270d0c
Simplified the implementation of RegisterWrapper. Fixed: the base class of an object was not serialized
Jacob Carlborg <doob@me.com>
parents:
3
diff
changeset
|
464 static if (isObject!(T) && !is(T == Object)) |
470ab5270d0c
Simplified the implementation of RegisterWrapper. Fixed: the base class of an object was not serialized
Jacob Carlborg <doob@me.com>
parents:
3
diff
changeset
|
465 deserializeBaseTypes(value); |
0 | 466 } |
467 | |
2 | 468 private void serializeBaseTypes (T : Object) (T value) |
0 | 469 { |
470 alias BaseTypeTupleOf!(T)[0] Base; | |
471 | |
472 static if (!is(Base == Object)) | |
473 { | |
474 archive.archiveBaseClass!(Base)(nextKey); | |
4
470ab5270d0c
Simplified the implementation of RegisterWrapper. Fixed: the base class of an object was not serialized
Jacob Carlborg <doob@me.com>
parents:
3
diff
changeset
|
475 Base base = value; |
470ab5270d0c
Simplified the implementation of RegisterWrapper. Fixed: the base class of an object was not serialized
Jacob Carlborg <doob@me.com>
parents:
3
diff
changeset
|
476 objectStructSerializeHelper(base); |
0 | 477 } |
478 } | |
479 | |
2 | 480 private void deserializeBaseTypes (T : Object) (T value) |
0 | 481 { |
482 alias BaseTypeTupleOf!(T)[0] Base; | |
483 | |
484 static if (!is(Base == Object)) | |
485 { | |
486 archive.unarchiveBaseClass!(Base)(nextKey); | |
4
470ab5270d0c
Simplified the implementation of RegisterWrapper. Fixed: the base class of an object was not serialized
Jacob Carlborg <doob@me.com>
parents:
3
diff
changeset
|
487 Base base = value; |
470ab5270d0c
Simplified the implementation of RegisterWrapper. Fixed: the base class of an object was not serialized
Jacob Carlborg <doob@me.com>
parents:
3
diff
changeset
|
488 objectStructDeserializeHelper(base); |
0 | 489 } |
490 } | |
491 | |
492 private SerializeRegisterWrapper!(T, ArchiveType) getSerializerWrapper (T) (string type) | |
493 { | |
494 auto wrapper = cast(SerializeRegisterWrapper!(T, ArchiveType)) serializers[type]; | |
495 | |
496 if (wrapper) | |
497 return wrapper; | |
498 | |
499 assert(false, "throw exception here"); | |
500 } | |
501 | |
502 private DeserializeRegisterWrapper!(T, ArchiveType) getDeserializerWrapper (T) (string type) | |
503 { | |
504 auto wrapper = cast(DeserializeRegisterWrapper!(T, ArchiveType)) deserializers[type]; | |
505 | |
506 if (wrapper) | |
507 return wrapper; | |
508 | |
509 assert(false, "throw exception here"); | |
510 } | |
511 | |
512 private SerializeRegisterWrapper!(T, ArchiveType) toSerializeRegisterWrapper (T) (void delegate (T, Serializer, DataType) dg) | |
513 { | |
514 return new SerializeRegisterWrapper!(T, ArchiveType)(dg); | |
515 } | |
516 | |
517 private SerializeRegisterWrapper!(T, ArchiveType) toSerializeRegisterWrapper (T) (void function (T, Serializer, DataType) func) | |
518 { | |
519 return new SerializeRegisterWrapper!(T, ArchiveType)(func); | |
520 } | |
521 | |
522 private DeserializeRegisterWrapper!(T, ArchiveType) toDeserializeRegisterWrapper (T) (void delegate (ref T, Serializer, DataType) dg) | |
523 { | |
524 return new DeserializeRegisterWrapper!(T, ArchiveType)(dg); | |
525 } | |
526 | |
527 private DeserializeRegisterWrapper!(T, ArchiveType) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, DataType) func) | |
528 { | |
529 return new DeserializeRegisterWrapper!(T, ArchiveType)(func); | |
530 } | |
531 | |
532 private DataType toDataType (T) (T value) | |
533 { | |
534 try | |
535 return to!(DataType)(value); | |
536 | |
537 catch (ConversionException e) | |
538 throw new SerializationException(e); | |
539 } | |
540 | |
541 private bool isBaseClass (T) (T value) | |
542 { | |
543 auto name = value.classinfo.name; | |
544 auto index = name.lastIndexOf('.'); | |
545 | |
546 return T.stringof != name[index + 1 .. $]; | |
547 } | |
548 | |
549 private DataType nextKey () | |
550 { | |
551 return toDataType(keyCounter++); | |
552 } | |
553 | |
554 private void resetCounters () | |
555 { | |
556 keyCounter = 0; | |
557 } | |
558 | |
559 private void triggerEvent (string name, T) (T value) | |
560 { | |
3 | 561 static assert (isObject!(T) || isStruct!(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.`)); |
0 | 562 |
563 foreach (i, dummy ; typeof(T.tupleof)) | |
564 { | |
565 const field = nameOfFieldAt!(T, i); | |
566 | |
567 static if (field == name) | |
568 { | |
569 alias TypeOfField!(T, field) Type; | |
570 auto event = getValueOfField!(T, Type, field)(value); | |
571 event(value); | |
572 } | |
573 } | |
574 } | |
575 | |
576 private void triggerEvents (T) (Mode mode, T value, void delegate () dg) | |
577 { | |
578 if (mode == serializing) | |
579 triggerEvent!(onSerializingField)(value); | |
580 | |
581 else | |
582 triggerEvent!(onDeserializingField)(value); | |
583 | |
584 dg(); | |
585 | |
586 if (mode == serializing) | |
587 triggerEvent!(onSerializedField)(value); | |
588 | |
589 else | |
590 triggerEvent!(onDeserializedField)(value); | |
591 } | |
592 | |
8 | 593 private static string[] collectAnnotations (string name, T) () |
0 | 594 { |
3 | 595 static assert (isObject!(T) || isStruct!(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.`)); |
0 | 596 |
597 string[] annotations; | |
598 | |
8 | 599 foreach (i, type ; typeof(T.tupleof)) |
0 | 600 { |
601 const field = nameOfFieldAt!(T, i); | |
602 | |
603 static if (field == name) | |
8 | 604 annotations ~= type.field; |
0 | 605 } |
606 | |
607 return annotations; | |
608 } | |
609 } |