Mercurial > projects > orange
comparison orange/serialization/Serializer.d @ 25:b51e953f79eb experimental
Second step in refactoring the API.
author | Jacob Carlborg <doob@me.com> |
---|---|
date | Wed, 06 Oct 2010 16:18:02 +0200 |
parents | 9a575087b961 |
children | 78e5fef4bbf2 |
comparison
equal
deleted
inserted
replaced
24:55f0a9d5df8d | 25:b51e953f79eb |
---|---|
31 | 31 |
32 alias Mode.serializing serializing; | 32 alias Mode.serializing serializing; |
33 alias Mode.deserializing deserializing; | 33 alias Mode.deserializing deserializing; |
34 } | 34 } |
35 | 35 |
36 class Serializer (ArchiveType : IArchive) | 36 class Serializer |
37 { | 37 { |
38 static assert(isArchive!(ArchiveType), format!(`The type "`, ArchiveType, `" does not implement the necessary methods to be an archive.`)); | 38 alias void delegate (ArchiveException exception, IArchive.IDataType data) ErrorCallback; |
39 | 39 alias IArchive.IDataType DataType; |
40 alias ArchiveType.ErrorCallback ErrorCallback; | |
41 alias ArchiveType.DataType DataType; | |
42 | 40 |
43 private | 41 private |
44 { | 42 { |
45 ArchiveType archive; | 43 ErrorCallback errorCallback_; |
44 IArchive archive; | |
45 | |
46 size_t keyCounter; | |
47 size_t idCounter; | |
46 | 48 |
47 RegisterBase[string] serializers; | 49 RegisterBase[string] serializers; |
48 RegisterBase[string] deserializers; | 50 RegisterBase[string] deserializers; |
49 | |
50 size_t keyCounter; | |
51 | 51 |
52 bool hasBegunSerializing; | 52 bool hasBegunSerializing; |
53 bool hasBegunDeserializing; | 53 bool hasBegunDeserializing; |
54 | 54 |
55 void delegate (ArchiveException exception, DataType[] data) throwOnErrorCallback; | 55 void delegate (ArchiveException exception, DataType data) throwOnErrorCallback; |
56 void delegate (ArchiveException exception, DataType[] data) doNothingOnErrorCallback; | 56 void delegate (ArchiveException exception, DataType data) doNothingOnErrorCallback; |
57 } | 57 } |
58 | 58 |
59 this () | 59 this (IArchive archive) |
60 { | 60 { |
61 archive = new ArchiveType; | 61 this.archive = archive; |
62 | 62 |
63 throwOnErrorCallback = (ArchiveException exception, DataType[] data) { throw exception; }; | 63 throwOnErrorCallback = (ArchiveException exception, IArchive.IDataType data) { throw exception; }; |
64 doNothingOnErrorCallback = (ArchiveException exception, DataType[] data) { /* do nothing */ }; | 64 doNothingOnErrorCallback = (ArchiveException exception, IArchive.IDataType data) { /* do nothing */ }; |
65 | 65 |
66 setThrowOnErrorCallback(); | 66 setThrowOnErrorCallback(); |
67 } | 67 } |
68 | |
69 void registerSerializer (T) (string type, void delegate (T, Serializer, DataType) dg) | |
70 { | |
71 serializers[type] = toSerializeRegisterWrapper(dg); | |
72 } | |
73 | |
74 void registerSerializer (T) (string type, void function (T, Serializer, DataType) func) | |
75 { | |
76 serializers[type] = toSerializeRegisterWrapper(func); | |
77 } | |
78 | |
79 void registerDeserializer (T) (string type, void delegate (ref T, Serializer, DataType) dg) | |
80 { | |
81 deserializers[type] = toDeserializeRegisterWrapper(dg); | |
82 } | |
83 | |
84 void registerDeserializer (T) (string type, void function (ref T, Serializer, DataType) func) | |
85 { | |
86 deserializers[type] = toDeserializeRegisterWrapper(func); | |
87 } | |
88 | |
89 void reset () | |
90 { | |
91 hasBegunSerializing = false; | |
92 hasBegunDeserializing = false; | |
93 resetCounters(); | |
94 | |
95 archive.reset; | |
96 } | |
97 | 68 |
98 ErrorCallback errorCallback () | 69 ErrorCallback errorCallback () |
99 { | 70 { |
100 return archive.errorCallback; | 71 return errorCallback_; |
101 } | 72 } |
102 | 73 |
103 ErrorCallback errorCallback (ErrorCallback errorCallback) | 74 ErrorCallback errorCallback (ErrorCallback errorCallback) |
104 { | 75 { |
105 return archive.errorCallback = errorCallback; | 76 return errorCallback_ = errorCallback; |
106 } | 77 } |
107 | 78 |
108 void setThrowOnErrorCallback () | 79 void setThrowOnErrorCallback () |
109 { | 80 { |
110 errorCallback = throwOnErrorCallback; | 81 errorCallback = throwOnErrorCallback; |
113 void setDoNothingOnErrorCallback () | 84 void setDoNothingOnErrorCallback () |
114 { | 85 { |
115 errorCallback = doNothingOnErrorCallback; | 86 errorCallback = doNothingOnErrorCallback; |
116 } | 87 } |
117 | 88 |
118 DataType serialize (T) (T value, DataType key = null) | 89 DataType serialize (T) (T value, string key = null) |
119 { | 90 { |
120 if (!hasBegunSerializing) | 91 if (!hasBegunSerializing) |
121 hasBegunSerializing = true; | 92 hasBegunSerializing = true; |
122 | 93 |
123 serializeInternal(value, key); | 94 serializeInternal(value, key); |
124 archive.postProcess; | 95 archive.postProcess; |
125 | 96 |
126 return archive.data; | 97 return archive.data; |
127 } | 98 } |
128 | 99 |
129 private void serializeInternal (T) (T value, DataType key = null) | 100 private void serializeInternal (T) (T value, string key = null) |
130 { | 101 { |
131 if (!key) | 102 if (!key) |
132 key = nextKey; | 103 key = nextKey; |
133 | 104 |
134 archive.beginArchiving(); | 105 archive.beginArchiving(); |
170 { | 141 { |
171 error: | 142 error: |
172 throw new SerializationException(format!(`The type "`, T, `" cannot be serialized.`), __FILE__, __LINE__); | 143 throw new SerializationException(format!(`The type "`, T, `" cannot be serialized.`), __FILE__, __LINE__); |
173 } | 144 } |
174 } | 145 } |
175 | 146 |
176 private void serializeObject (T) (T value, DataType key) | 147 private void serializeObject (T) (T value, string key) |
177 { | 148 { |
149 auto runtimeType = value.classinfo.name; | |
150 | |
178 triggerEvents(serializing, value, { | 151 triggerEvents(serializing, value, { |
179 archive.archive(value, key, { | 152 archive.archiveObject(runtimeType, T.stringof, key, nextId, { |
180 auto runtimeType = value.classinfo.name; | |
181 | |
182 if (runtimeType in serializers) | 153 if (runtimeType in serializers) |
183 { | 154 { |
184 auto wrapper = getSerializerWrapper!(T)(runtimeType); | 155 auto wrapper = getSerializerWrapper!(T)(runtimeType); |
185 wrapper(value, this, key); | 156 wrapper(value, this, key); |
186 } | 157 } |
196 objectStructSerializeHelper(value); | 167 objectStructSerializeHelper(value); |
197 } | 168 } |
198 }); | 169 }); |
199 }); | 170 }); |
200 } | 171 } |
201 | 172 |
202 private void serializeStruct (T) (T value, DataType key) | 173 private void serializeStruct (T) (T value, DataType key) |
203 { | 174 { |
175 auto type = T.stringof; | |
176 | |
204 triggerEvents(serializing, value, { | 177 triggerEvents(serializing, value, { |
205 archive.archive(value, key, { | 178 archive.archive(type, key, nextId, { |
206 auto type = toDataType(T.stringof); | |
207 | |
208 if (type in serializers) | 179 if (type in serializers) |
209 { | 180 { |
210 auto wrapper = getSerializerWrapper!(T)(type); | 181 auto wrapper = getSerializerWrapper!(T)(type); |
211 wrapper(value, this, key); | 182 wrapper(value, this, key); |
212 } | 183 } |
221 } | 192 } |
222 }); | 193 }); |
223 }); | 194 }); |
224 } | 195 } |
225 | 196 |
226 private void serializeString (T) (T value, DataType key) | 197 private void serializeString (T) (T value, string key) |
227 { | 198 { |
228 archive.archive(value, key); | 199 archive.archive(value, key, nextId); |
229 } | 200 } |
230 | 201 |
231 private void serializeArray (T) (T value, DataType key) | 202 private void serializeArray (T) (T value, string key) |
232 { | 203 { |
233 archive.archive(value, key, { | 204 auto array = Array(value.ptr, value.length, BaseTypeOfArray!(T).sizeof); |
205 | |
206 archive.archiveArray(array, arrayToString!(T), key, nextId, { | |
234 foreach (i, e ; value) | 207 foreach (i, e ; value) |
235 serializeInternal(e, toDataType(i)); | 208 serializeInternal(e, toDataType(i)); |
236 }); | 209 }); |
237 } | 210 } |
238 | 211 |
239 private void serializeAssociativeArray (T) (T value, DataType key) | 212 private void serializePrimitive (T) (T value, string key) |
240 { | 213 { |
241 archive.archive(value, key, { | 214 archive.archive(value, key, nextId); |
242 foreach(k, v ; value) | |
243 serializeInternal(v, toDataType(k)); | |
244 }); | |
245 } | |
246 | |
247 private void serializePointer (T) (T value, DataType key) | |
248 { | |
249 archive.archive(value, key, { | |
250 if (key in serializers) | |
251 { | |
252 auto wrapper = getSerializerWrapper!(T)(key); | |
253 wrapper(value, this, key); | |
254 } | |
255 | |
256 else static if (isSerializable!(T, Serializer)) | |
257 value.toData(this, key); | |
258 | |
259 else | |
260 { | |
261 static if (isVoid!(BaseTypeOfPointer!(T))) | |
262 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__); | |
263 | |
264 else | |
265 serializeInternal(*value, key); | |
266 } | |
267 }); | |
268 } | |
269 | |
270 private void serializeEnum (T) (T value, DataType key) | |
271 { | |
272 archive.archive(value, key); | |
273 } | |
274 | |
275 private void serializePrimitive (T) (T value, DataType key) | |
276 { | |
277 archive.archive(value, key); | |
278 } | |
279 | |
280 private void serializeTypeDef (T) (T value, DataType key) | |
281 { | |
282 archive.archive(value, key, { | |
283 serializeInternal!(BaseTypeOfTypeDef!(T))(value, key); | |
284 }); | |
285 } | |
286 | |
287 T deserialize (T) (DataType data, DataType key = null) | |
288 { | |
289 if (hasBegunSerializing && !hasBegunDeserializing) | |
290 resetCounters(); | |
291 | |
292 if (!hasBegunDeserializing) | |
293 hasBegunDeserializing = true; | |
294 | |
295 if (!key) | |
296 key = nextKey; | |
297 | |
298 archive.beginUnarchiving(data); | |
299 return deserializeInternal!(T)(key); | |
300 } | |
301 | |
302 private T deserializeInternal (T) (DataType key) | |
303 { | |
304 static if (isTypeDef!(T)) | |
305 return deserializeTypeDef!(T)(key); | |
306 | |
307 else static if (isObject!(T)) | |
308 return deserializeObject!(T)(key); | |
309 | |
310 else static if (isStruct!(T)) | |
311 return deserializeStruct!(T)(key); | |
312 | |
313 else static if (isString!(T)) | |
314 return deserializeString!(T)(key); | |
315 | |
316 else static if (isArray!(T)) | |
317 return deserializeArray!(T)(key); | |
318 | |
319 else static if (isAssociativeArray!(T)) | |
320 return deserializeAssociativeArray!(T)(key); | |
321 | |
322 else static if (isPrimitive!(T)) | |
323 return deserializePrimitive!(T)(key); | |
324 | |
325 else static if (isPointer!(T)) | |
326 { | |
327 static if (isFunctionPointer!(T)) | |
328 goto error; | |
329 | |
330 return deserializePointer!(T)(key); | |
331 } | |
332 | |
333 else static if (isEnum!(T)) | |
334 return deserializeEnum!(T)(key); | |
335 | |
336 else | |
337 { | |
338 error: | |
339 throw new SerializationException(format!(`The type "`, T, `" cannot be deserialized.`), __FILE__, __LINE__); | |
340 } | |
341 } | |
342 | |
343 private T deserializeObject (T) (DataType key) | |
344 { | |
345 T value = archive.unarchive!(T)(key, (T value) { | |
346 triggerEvents(deserializing, value, { | |
347 auto runtimeType = value.classinfo.name; | |
348 | |
349 if (runtimeType in deserializers) | |
350 { | |
351 auto wrapper = getDeserializerWrapper!(T)(runtimeType); | |
352 wrapper(value, this, key); | |
353 } | |
354 | |
355 else static if (isSerializable!(T, Serializer)) | |
356 value.fromData(this, key); | |
357 | |
358 else | |
359 { | |
360 if (isBaseClass(value)) | |
361 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__); | |
362 | |
363 objectStructDeserializeHelper(value); | |
364 } | |
365 }); | |
366 | |
367 return value; | |
368 }); | |
369 | |
370 return value; | |
371 } | |
372 | |
373 private T deserializeStruct (T) (DataType key) | |
374 { | |
375 return archive.unarchive!(T)(key, (T value) { | |
376 triggerEvents(deserializing, value, { | |
377 auto type = toDataType(T.stringof); | |
378 | |
379 if (type in deserializers) | |
380 { | |
381 auto wrapper = getDeserializerWrapper!(T)(type); | |
382 wrapper(value, this, key); | |
383 } | |
384 | |
385 else | |
386 { | |
387 static if (isSerializable!(T, Serializer)) | |
388 value.fromData(this, key); | |
389 | |
390 else | |
391 objectStructDeserializeHelper(value); | |
392 } | |
393 }); | |
394 | |
395 return value; | |
396 }); | |
397 } | |
398 | |
399 private T deserializeString (T) (DataType key) | |
400 { | |
401 return archive.unarchive!(T)(key); | |
402 } | |
403 | |
404 private T deserializeArray (T) (DataType key) | |
405 { | |
406 return archive.unarchive!(T)(key, (T value) { | |
407 foreach (i, ref e ; value) | |
408 e = deserializeInternal!(typeof(e))(toDataType(i)); | |
409 | |
410 return value; | |
411 }); | |
412 } | |
413 | |
414 private T deserializeAssociativeArray (T) (DataType key) | |
415 { | |
416 return archive.unarchive!(T)(key, (T value) { | |
417 foreach (k, v ; archive.unarchiveAssociativeArrayVisitor!(T)) | |
418 value[k] = v; | |
419 | |
420 return value; | |
421 }); | |
422 } | |
423 | |
424 private T deserializePointer (T) (DataType key) | |
425 { | |
426 return archive.unarchive!(T)(key, (T value) { | |
427 if (key in deserializers) | |
428 { | |
429 auto wrapper = getDeserializerWrapper!(T)(key); | |
430 wrapper(value, this, key); | |
431 } | |
432 | |
433 else static if (isSerializable!(T, Serializer)) | |
434 value.fromData(this, key); | |
435 | |
436 else | |
437 { | |
438 static if (isVoid!(BaseTypeOfPointer!(T))) | |
439 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__); | |
440 | |
441 else | |
442 *value = deserializeInternal!(BaseTypeOfPointer!(T))(key); | |
443 } | |
444 | |
445 return value; | |
446 }); | |
447 } | |
448 | |
449 private T deserializeEnum (T) (DataType key) | |
450 { | |
451 return archive.unarchive!(T)(key); | |
452 } | |
453 | |
454 private T deserializePrimitive (T) (DataType key) | |
455 { | |
456 return archive.unarchive!(T)(key); | |
457 } | |
458 | |
459 private T deserializeTypeDef (T) (DataType key) | |
460 { | |
461 return archive.unarchive!(T)(key, (T value) { | |
462 return deserializeInternal!(BaseTypeOfTypeDef!(T))(key); | |
463 }); | |
464 } | 215 } |
465 | 216 |
466 private void objectStructSerializeHelper (T) (ref T value) | 217 private void objectStructSerializeHelper (T) (ref T value) |
467 { | 218 { |
468 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.`)); | 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.`)); |
509 { | 260 { |
510 alias BaseTypeTupleOf!(T)[0] Base; | 261 alias BaseTypeTupleOf!(T)[0] Base; |
511 | 262 |
512 static if (!is(Base == Object)) | 263 static if (!is(Base == Object)) |
513 { | 264 { |
514 archive.archiveBaseClass!(Base)(nextKey); | 265 archive.archiveBaseClass(Base.stringof, nextKey, nextId); |
515 Base base = value; | 266 Base base = value; |
516 objectStructSerializeHelper(base); | 267 objectStructSerializeHelper(base); |
517 } | 268 } |
518 } | 269 } |
519 | 270 |
547 return wrapper; | 298 return wrapper; |
548 | 299 |
549 assert(false, "throw exception here"); | 300 assert(false, "throw exception here"); |
550 } | 301 } |
551 | 302 |
552 private SerializeRegisterWrapper!(T, Serializer) toSerializeRegisterWrapper (T) (void delegate (T, Serializer, DataType) dg) | 303 private SerializeRegisterWrapper!(T, Serializer) toSerializeRegisterWrapper (T) (void delegate (T, Serializer, string) dg) |
553 { | 304 { |
554 return new SerializeRegisterWrapper!(T, Serializer)(dg); | 305 return new SerializeRegisterWrapper!(T, Serializer)(dg); |
555 } | 306 } |
556 | 307 |
557 private SerializeRegisterWrapper!(T, Serializer) toSerializeRegisterWrapper (T) (void function (T, Serializer, DataType) func) | 308 private SerializeRegisterWrapper!(T, Serializer) toSerializeRegisterWrapper (T) (void function (T, Serializer, string) func) |
558 { | 309 { |
559 return new SerializeRegisterWrapper!(T, Serializer)(func); | 310 return new SerializeRegisterWrapper!(T, Serializer)(func); |
560 } | 311 } |
561 | 312 |
562 private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void delegate (ref T, Serializer, DataType) dg) | 313 private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void delegate (ref T, Serializer, string) dg) |
563 { | 314 { |
564 return new DeserializeRegisterWrapper!(T, Serializer)(dg); | 315 return new DeserializeRegisterWrapper!(T, Serializer)(dg); |
565 } | 316 } |
566 | 317 |
567 private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, DataType) func) | 318 private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, string) func) |
568 { | 319 { |
569 return new DeserializeRegisterWrapper!(T, Serializer)(func); | 320 return new DeserializeRegisterWrapper!(T, Serializer)(func); |
570 } | 321 } |
571 | 322 |
572 private DataType toDataType (T) (T value) | 323 private template arrayToString (T) |
573 { | 324 { |
574 try | 325 const arrayToString = BaseTypeOfArray!(T).stringof; |
575 return to!(DataType)(value); | |
576 | |
577 catch (ConversionException e) | |
578 throw new SerializationException(e); | |
579 } | 326 } |
580 | 327 |
581 private bool isBaseClass (T) (T value) | 328 private bool isBaseClass (T) (T value) |
582 { | 329 { |
583 auto name = value.classinfo.name; | 330 auto name = value.classinfo.name; |
584 auto index = name.lastIndexOf('.'); | 331 auto index = name.lastIndexOf('.'); |
585 | 332 |
586 return T.stringof != name[index + 1 .. $]; | 333 return T.stringof != name[index + 1 .. $]; |
587 } | 334 } |
588 | 335 |
589 private DataType nextKey () | 336 private size_t nextId () |
337 { | |
338 return idCounter++; | |
339 } | |
340 | |
341 private string nextKey () | |
590 { | 342 { |
591 return toDataType(keyCounter++); | 343 return toDataType(keyCounter++); |
592 } | 344 } |
593 | 345 |
594 private void resetCounters () | 346 private void resetCounters () |
595 { | 347 { |
596 keyCounter = 0; | 348 keyCounter = 0; |
349 idCounter = 0; | |
350 } | |
351 | |
352 private string toDataType (T) (T value) | |
353 { | |
354 return to!(string)(value); | |
597 } | 355 } |
598 | 356 |
599 private void triggerEvent (string name, T) (T value) | 357 private void triggerEvent (string name, T) (T value) |
600 { | 358 { |
601 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.`)); | 359 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.`)); |