Mercurial > projects > orange
comparison orange/serialization/Serializer.d @ 0:f7b078e85f7f
First commit
author | Jacob Carlborg <doob@me.com> |
---|---|
date | Wed, 26 May 2010 17:19:13 +0200 |
parents | |
children | ea37a9470e3e |
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.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)) | |
118 static assert(false, format!(`The type "`, T, `" cannot be serialized.`)); | |
119 | |
120 else | |
121 serializePointer(value, key); | |
122 } | |
123 | |
124 | |
125 else static if (isEnum!(T)) | |
126 serializeEnum(value, key); | |
127 | |
128 else | |
129 static assert(false, format!(`The type "`, T, `" cannot be serialized.`)); | |
130 | |
131 return archive.data; | |
132 } | |
133 | |
134 private void serializeObject (T) (T value, DataType key) | |
135 { | |
136 auto nonSerializedFields = collectAnnotations!(nonSerializedField)(value); | |
137 | |
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 | |
156 objectStructSerializeHelper(value, nonSerializedFields); | |
157 } | |
158 }); | |
159 }); | |
160 } | |
161 | |
162 private void serializeStruct (T) (T value, DataType key) | |
163 { | |
164 auto nonSerializedFields = collectAnnotations!(nonSerializedField)(value); | |
165 | |
166 triggerEvents(serializing, value, { | |
167 archive.archive(value, key, { | |
168 auto type = toDataType(T.stringof); | |
169 | |
170 if (type in serializers) | |
171 { | |
172 auto wrapper = getSerializerWrapper!(T)(type); | |
173 wrapper(value, this, key); | |
174 } | |
175 | |
176 else | |
177 { | |
178 static if (isSerializable!(T, ArchiveType)) | |
179 value.toData(this, key); | |
180 | |
181 else | |
182 objectStructSerializeHelper(value, nonSerializedFields); | |
183 } | |
184 }); | |
185 }); | |
186 } | |
187 | |
188 private void serializeString (T) (T value, DataType key) | |
189 { | |
190 archive.archive(value, key); | |
191 } | |
192 | |
193 private void serializeArray (T) (T value, DataType key) | |
194 { | |
195 archive.archive(value, key, { | |
196 foreach (i, e ; value) | |
197 archive.archive(e, toDataType(i)); | |
198 }); | |
199 } | |
200 | |
201 private void serializeAssociativeArray (T) (T value, DataType key) | |
202 { | |
203 archive.archive(value, key, { | |
204 foreach(k, v ; value) | |
205 archive.archive(v, toDataType(k)); | |
206 }); | |
207 } | |
208 | |
209 private void serializePointer (T) (T value, DataType key) | |
210 { | |
211 archive.archive(value, key, { | |
212 if (key in serializers) | |
213 { | |
214 auto wrapper = getSerializerWrapper!(T)(key); | |
215 wrapper(value, this, key); | |
216 } | |
217 | |
218 else static if (isSerializable!(T, ArchiveType)) | |
219 value.toData(this, key); | |
220 | |
221 else | |
222 { | |
223 static if (isVoid!(BaseTypeOfPointer!(T))) | |
224 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__); | |
225 | |
226 else | |
227 serialize(*value, key); | |
228 } | |
229 }); | |
230 } | |
231 | |
232 private void serializeEnum (T) (T value, DataType key) | |
233 { | |
234 archive.archive(value, key); | |
235 } | |
236 | |
237 private void serializePrimitive (T) (T value, DataType key) | |
238 { | |
239 archive.archive(value, key); | |
240 } | |
241 | |
242 private void serializeTypeDef (T) (T value, DataType key) | |
243 { | |
244 archive.archive(value, key, { | |
245 serialize!(BaseTypeOfTypeDef!(T))(value, key); | |
246 }); | |
247 } | |
248 | |
249 T deserialize (T) (DataType data, DataType key = null) | |
250 { | |
251 if (hasBegunSerializing && !hasBegunDeserializing) | |
252 resetCounters(); | |
253 | |
254 if (!hasBegunDeserializing) | |
255 hasBegunDeserializing = true; | |
256 | |
257 if (!key) | |
258 key = nextKey; | |
259 | |
260 archive.beginUnarchiving(data); | |
261 return deserializeInternal!(T)(key); | |
262 } | |
263 | |
264 private T deserializeInternal (T) (DataType key) | |
265 { | |
266 static if (isTypeDef!(T)) | |
267 return deserializeTypeDef!(T)(key); | |
268 | |
269 else static if (isObject!(T)) | |
270 return deserializeObject!(T)(key); | |
271 | |
272 else static if (isStruct!(T)) | |
273 return deserializeStruct!(T)(key); | |
274 | |
275 else static if (isString!(T)) | |
276 return deserializeString!(T)(key); | |
277 | |
278 else static if (isArray!(T)) | |
279 return deserializeArray!(T)(key); | |
280 | |
281 else static if (isAssociativeArray!(T)) | |
282 return deserializeAssociativeArray!(T)(key); | |
283 | |
284 else static if (isPrimitive!(T)) | |
285 return deserializePrimitive!(T)(key); | |
286 | |
287 else static if (isPointer!(T)) | |
288 return deserializePointer!(T)(key); | |
289 | |
290 else static if (isEnum!(T)) | |
291 return deserializeEnum!(T)(key); | |
292 | |
293 else | |
294 static assert(false, format!(`The type "`, T, `" cannot be deserialized.`)); | |
295 } | |
296 | |
297 private T deserializeObject (T) (DataType key) | |
298 { | |
299 T value = archive.unarchive!(T)(key, (T value) { | |
300 auto nonSerializedFields = collectAnnotations!(nonSerializedField)(value); | |
301 | |
302 triggerEvents(deserializing, value, { | |
303 auto runtimeType = value.classinfo.name; | |
304 | |
305 if (runtimeType in deserializers) | |
306 { | |
307 auto wrapper = getDeserializerWrapper!(T)(runtimeType); | |
308 wrapper(value, this, key); | |
309 } | |
310 | |
311 else static if (isSerializable!(T, ArchiveType)) | |
312 value.fromData(this, key); | |
313 | |
314 else | |
315 { | |
316 if (isBaseClass(value)) | |
317 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__); | |
318 | |
319 objectStructDeserializeHelper(value, nonSerializedFields); | |
320 } | |
321 }); | |
322 | |
323 return value; | |
324 }); | |
325 | |
326 return value; | |
327 } | |
328 | |
329 private T deserializeStruct (T) (DataType key) | |
330 { | |
331 return archive.unarchive!(T)(key, (T value) { | |
332 auto nonSerializedFields = collectAnnotations!(nonSerializedField)(value); | |
333 | |
334 triggerEvents(deserializing, value, { | |
335 auto type = toDataType(T.stringof); | |
336 | |
337 if (type in deserializers) | |
338 { | |
339 auto wrapper = getDeserializerWrapper!(T)(type); | |
340 wrapper(value, this, key); | |
341 } | |
342 | |
343 else | |
344 { | |
345 static if (isSerializable!(T, ArchiveType)) | |
346 value.fromData(this, key); | |
347 | |
348 else | |
349 objectStructDeserializeHelper(value, nonSerializedFields); | |
350 } | |
351 }); | |
352 | |
353 return value; | |
354 }); | |
355 } | |
356 | |
357 private T deserializeString (T) (DataType key) | |
358 { | |
359 return archive.unarchive!(T)(key); | |
360 } | |
361 | |
362 private T deserializeArray (T) (DataType key) | |
363 { | |
364 return archive.unarchive!(T)(key, (T value) { | |
365 foreach (i, ref e ; value) | |
366 e = archive.unarchive!(typeof(e))(toDataType(i)); | |
367 | |
368 return value; | |
369 }); | |
370 } | |
371 | |
372 private T deserializeAssociativeArray (T) (DataType key) | |
373 { | |
374 return archive.unarchive!(T)(key, (T value) { | |
375 foreach (k, v ; archive.unarchiveAssociativeArrayVisitor!(T)) | |
376 value[k] = v; | |
377 | |
378 return value; | |
379 }); | |
380 } | |
381 | |
382 private T deserializePointer (T) (DataType key) | |
383 { | |
384 return archive.unarchive!(T)(key, (T value) { | |
385 if (key in deserializers) | |
386 { | |
387 auto wrapper = getDeserializerWrapper!(T)(key); | |
388 wrapper(value, this, key); | |
389 } | |
390 | |
391 else static if (isSerializable!(T, ArchiveType)) | |
392 value.fromData(this, key); | |
393 | |
394 else | |
395 { | |
396 static if (isVoid!(BaseTypeOfPointer!(T))) | |
397 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__); | |
398 | |
399 else | |
400 *value = deserializeInternal!(BaseTypeOfPointer!(T))(key); | |
401 } | |
402 | |
403 return value; | |
404 }); | |
405 } | |
406 | |
407 private T deserializeEnum (T) (DataType key) | |
408 { | |
409 return archive.unarchive!(T)(key); | |
410 } | |
411 | |
412 private T deserializePrimitive (T) (DataType key) | |
413 { | |
414 return archive.unarchive!(T)(key); | |
415 } | |
416 | |
417 private T deserializeTypeDef (T) (DataType key) | |
418 { | |
419 return archive.unarchive!(T)(key, (T value) { | |
420 return deserializeInternal!(BaseTypeOfTypeDef!(T))(key); | |
421 }); | |
422 } | |
423 | |
424 private void objectStructSerializeHelper (T) (T value, string[] nonSerializedFields) | |
425 { | |
426 foreach (i, dummy ; typeof(T.tupleof)) | |
427 { | |
428 const field = nameOfFieldAt!(T, i); | |
429 | |
430 if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) | |
431 { | |
432 alias typeof(T.tupleof[i]) Type; | |
433 | |
434 Type v = value.tupleof[i]; | |
435 | |
436 //Type v = getValueOfField!(T, Type, field)(value); | |
437 serialize(v, toDataType(field)); | |
438 } | |
439 } | |
440 | |
441 static if (is(T : Object) && !is(T == Object)) | |
442 serializeBaseTypes(value, nonSerializedFields); | |
443 } | |
444 | |
445 private void objectStructDeserializeHelper (T) (T value, string[] nonSerializedFields) | |
446 { | |
447 foreach (i, dummy ; typeof(T.tupleof)) | |
448 { | |
449 const field = nameOfFieldAt!(T, i); | |
450 | |
451 if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) | |
452 { | |
453 alias TypeOfField!(T, field) Type; | |
454 auto fieldValue = deserializeInternal!(Type)(toDataType(field)); | |
455 setValueOfField!(T, Type, field)(value, fieldValue); | |
456 } | |
457 } | |
458 | |
459 static if (is(T : Object) && !is(T == Object)) | |
460 deserializeBaseTypes(value, nonSerializedFields); | |
461 } | |
462 | |
463 private void serializeBaseTypes (T : Object) (T value, string[] nonSerializedFields) | |
464 { | |
465 alias BaseTypeTupleOf!(T)[0] Base; | |
466 | |
467 static if (!is(Base == Object)) | |
468 { | |
469 archive.archiveBaseClass!(Base)(nextKey); | |
470 objectStructSerializeHelper!(Base)(value, nonSerializedFields); | |
471 } | |
472 } | |
473 | |
474 private void deserializeBaseTypes (T : Object) (T value, string[] nonSerializedFields) | |
475 { | |
476 alias BaseTypeTupleOf!(T)[0] Base; | |
477 | |
478 static if (!is(Base == Object)) | |
479 { | |
480 archive.unarchiveBaseClass!(Base)(nextKey); | |
481 objectStructDeserializeHelper!(Base)(value, nonSerializedFields); | |
482 } | |
483 } | |
484 | |
485 private SerializeRegisterWrapper!(T, ArchiveType) getSerializerWrapper (T) (string type) | |
486 { | |
487 auto wrapper = cast(SerializeRegisterWrapper!(T, ArchiveType)) serializers[type]; | |
488 | |
489 if (wrapper) | |
490 return wrapper; | |
491 | |
492 assert(false, "throw exception here"); | |
493 } | |
494 | |
495 private DeserializeRegisterWrapper!(T, ArchiveType) getDeserializerWrapper (T) (string type) | |
496 { | |
497 auto wrapper = cast(DeserializeRegisterWrapper!(T, ArchiveType)) deserializers[type]; | |
498 | |
499 if (wrapper) | |
500 return wrapper; | |
501 | |
502 assert(false, "throw exception here"); | |
503 } | |
504 | |
505 private SerializeRegisterWrapper!(T, ArchiveType) toSerializeRegisterWrapper (T) (void delegate (T, Serializer, DataType) dg) | |
506 { | |
507 return new SerializeRegisterWrapper!(T, ArchiveType)(dg); | |
508 } | |
509 | |
510 private SerializeRegisterWrapper!(T, ArchiveType) toSerializeRegisterWrapper (T) (void function (T, Serializer, DataType) func) | |
511 { | |
512 return new SerializeRegisterWrapper!(T, ArchiveType)(func); | |
513 } | |
514 | |
515 private DeserializeRegisterWrapper!(T, ArchiveType) toDeserializeRegisterWrapper (T) (void delegate (ref T, Serializer, DataType) dg) | |
516 { | |
517 return new DeserializeRegisterWrapper!(T, ArchiveType)(dg); | |
518 } | |
519 | |
520 private DeserializeRegisterWrapper!(T, ArchiveType) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, DataType) func) | |
521 { | |
522 return new DeserializeRegisterWrapper!(T, ArchiveType)(func); | |
523 } | |
524 | |
525 private DataType toDataType (T) (T value) | |
526 { | |
527 try | |
528 return to!(DataType)(value); | |
529 | |
530 catch (ConversionException e) | |
531 throw new SerializationException(e); | |
532 } | |
533 | |
534 private bool isBaseClass (T) (T value) | |
535 { | |
536 auto name = value.classinfo.name; | |
537 auto index = name.lastIndexOf('.'); | |
538 | |
539 return T.stringof != name[index + 1 .. $]; | |
540 } | |
541 | |
542 private DataType nextKey () | |
543 { | |
544 return toDataType(keyCounter++); | |
545 } | |
546 | |
547 private void resetCounters () | |
548 { | |
549 keyCounter = 0; | |
550 } | |
551 | |
552 private void triggerEvent (string name, T) (T value) | |
553 { | |
554 static assert (is(T == class) || is(T == struct), format!(`The given value of the type "`, T, `" is not a valid type, the only valid types for this method are classes and structs.`)); | |
555 | |
556 foreach (i, dummy ; typeof(T.tupleof)) | |
557 { | |
558 const field = nameOfFieldAt!(T, i); | |
559 | |
560 static if (field == name) | |
561 { | |
562 alias TypeOfField!(T, field) Type; | |
563 auto event = getValueOfField!(T, Type, field)(value); | |
564 event(value); | |
565 } | |
566 } | |
567 } | |
568 | |
569 private void triggerEvents (T) (Mode mode, T value, void delegate () dg) | |
570 { | |
571 if (mode == serializing) | |
572 triggerEvent!(onSerializingField)(value); | |
573 | |
574 else | |
575 triggerEvent!(onDeserializingField)(value); | |
576 | |
577 dg(); | |
578 | |
579 if (mode == serializing) | |
580 triggerEvent!(onSerializedField)(value); | |
581 | |
582 else | |
583 triggerEvent!(onDeserializedField)(value); | |
584 } | |
585 | |
586 private string[] collectAnnotations (string name, T) (T value) | |
587 { | |
588 static assert (is(T == class) || is(T == struct), format!(`The given value of the type "`, T, `" is not a valid type, the only valid types for this method are classes and structs.`)); | |
589 | |
590 string[] annotations; | |
591 | |
592 foreach (i, dummy ; typeof(T.tupleof)) | |
593 { | |
594 const field = nameOfFieldAt!(T, i); | |
595 | |
596 static if (field == name) | |
597 { | |
598 alias TypeOfField!(T, field) Type; | |
599 auto f = getValueOfField!(T, Type, field)(value); | |
600 annotations ~= f.field; | |
601 } | |
602 } | |
603 | |
604 return annotations; | |
605 } | |
606 } |