Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

root/trunk/doost/util/serializer/Serializer.d

Revision 87, 19.1 kB (checked in by aarti_pl, 14 years ago)

- Serialization: preparation for from base class serialization support

  • Property svn:executable set to *
Line 
1 /*******************************************************************************
2
3     License:    Boost Software License, v. 1.0
4                 Academic Free License, v. 3.0
5                 BSD License
6
7     Authors:    Marcin Kuszczak, www.zapytajmnie.com (author's christian site)
8
9     Version:    0.9.1
10     Date:       30-Apr-2008
11
12     History:    0.9.1 (30-Apr-2008) -   initial public version
13
14  ******************************************************************************/
15
16 module doost.util.serializer.Serializer;
17
18 import doost.util.serializer.Exception;
19
20 import std.traits;
21 import doost.api.Common;
22 import doost.core.Traits;
23 import doost.storage.Storage;
24 import doost.util.serializer.Registry;
25
26
27 //------------------------------------------------------------------------------
28
29 /*******************************************************************************
30  ******************************************************************************/
31 template Serializable() {
32     void describeUdt(T)(T arch) {
33         foreach(i, v; this.tupleof)
34             //BUG: v nie jest przekazywane przez referencję? nie moÅŒna wstawić zamiast this.tupleof[i] - v
35             //BUG: below will and all operations on v or this.tupleof[i] will fail
36             //writefln(v.stringof);
37             arch.describe(this.tupleof[i], this.tupleof[i].stringof);
38     }
39 }
40
41 /*******************************************************************************
42         Below templates are hacks for use with static arrays
43  ******************************************************************************/
44 private void checkType(T : U[N], U, size_t N)(U[] arr) {
45     if (arr.length>N)
46         throw new SerializerException("Length of parsed array exeedes static array bounds.");
47 }
48
49 private void checkType(T)(T val) {
50 }
51
52 /*******************************************************************************
53  ******************************************************************************/
54 enum Tracking {ON, OFF};
55 //FIXME: Archive powinno mieć dwie referencje, tak aby moÅŒna było mieć
56 //INPUT storage oraz output STORAGE
57 //------------------------------------------------------------------------------
58
59 //ENH: not possible - why?: class Serializer(alias T...)
60 //ENH: lack of static access to some member of templates without instantiation needed
61 //BUG: inner class Archive is invisible in default parameter of template
62 /*******************************************************************************
63  ******************************************************************************/
64 class Serializer(alias CONCRETEARCHIVE, STORAGE=string) {
65 //ENH: when parameter of function is preceeding by alias, stringof on this parameter
66 //should give original name of symbol
67
68 private:
69     //TODO: checks for type requirements
70     const info = "Type '" ~ STORAGE.stringof ~ "' does not meet Storage requirements: ";
71     //static assert(is(typeof(STORAGE.init) == STORAGE), info ~ "lack of init() method.");
72     //static assert(is(typeof(STORAGE.dup) == STORAGE), info ~ "lack of dup() method.");
73
74     enum Mode {LOAD, DUMP}
75
76     alias StorageElementType!(STORAGE)    ELEMENTTYPE;
77     alias StorageType!(STORAGE)           STORAGETYPE;
78
79     static synchronized Archive m_archive;
80
81 //DMD-ENH: it should work:
82 //static synchronized Archive m_archive = new Archive;
83
84 public:
85
86     /***************************************************************************
87         Constructor
88      **************************************************************************/
89     this() {
90         m_archive = new Archive;
91         static if (is(typeof(init(Archive)) == bool)) {
92             init(m_archive);
93         }
94     }
95
96     /***************************************************************************
97         Returns: copy of current global Archive instance
98      **************************************************************************/
99     Archive archive() {
100         return m_archive.dup_info;
101     }
102
103     /***************************************************************************
104         Params: arch = Archive instance which should be set as global
105      **************************************************************************/
106     void archive(Archive arch) {
107         m_archive = arch.dup_info;
108     }
109
110     /***************************************************************************
111         Returns: reference to global instance of Archive
112      **************************************************************************/
113     //TODO: zmienna globalna powinna być dla typu przy uÅŒyciu sztuczki ze static T val;
114     Archive global() {
115         return m_archive;
116     }
117
118     //ENH: not possible to use it with is expression
119     //void wrapper(T : U[N], U, size_t N)(T val, void delegate(ref U[] value) dg);
120     //BUG: specialization not allowed for deduced parameter
121     //STORAGE dump(T : U[N], U, size_t N)(T v)
122
123     STORAGE dump(VALUE)(VALUE v, STORAGE storage, Archive archive = null) {
124         return dumpInternal(v, storage, archive);
125     }
126
127     STORAGE dump(VALUE)(VALUE v, Archive archive = null) {
128         STORAGE storage;
129         static if (is(STORAGE == class)) {
130             assert(archive !is null && archive.storage !is null, "You must pass storage to dump method");
131             storage = archive.storage;
132         }
133         return dumpInternal(v, storage, archive);
134     }
135
136     /***************************************************************************
137         Dumps type into storage
138      **************************************************************************/
139     //FIXME: it would be better to make instatiations using STORAGE below, and not
140     //when creating serializer
141     private STORAGE dumpInternal(VALUE)(VALUE v, STORAGE storage, Archive archive = null) {
142         Unqual!(normalizedType!(VALUE)) value = cast(Unqual!(normalizedType!(VALUE)))v;
143
144         if (archive is null) archive = m_archive.dup_info;
145             else archive = archive.dup_info;
146
147         archive.mode = Mode.DUMP;
148         archive.storage = storage;
149
150         archive.start();
151         bool res = archive.traverse(value);
152         if (res == false)
153             throw new SerializerException("Can not dump type " ~ VALUE.stringof ~ ".");
154         archive.finish(archive);
155
156         return archive.storage;
157     }
158
159     //BUG: in case of below signature, there is no way to add another template parameter
160     //U[] load(T : U[N], U, size_t N)(STORAGE storage, Archive archive=null) {
161     /***************************************************************************
162      **************************************************************************/
163     normalizedType!(VALUE) load(VALUE)(STORAGE storage, Archive archive = null) {
164         //TODO: static asserts in serializer --> check if all template functions exists in Archive
165         if (archive is null) archive = m_archive.dup_info;
166             else archive = archive.dup_info;
167
168         archive.mode = Mode.LOAD;
169         archive.storage = storage;
170
171         Unqual!(normalizedType!(VALUE)) value;
172
173         archive.start();
174
175         if (!archive.traverse(value))
176             throw new SerializerException("Could not load type '" ~ VALUE.stringof ~ "'.");
177
178         checkType(value);
179         archive.finish(archive);
180
181         return cast(normalizedType!(VALUE))value;
182     }
183
184 //------------------------------------------------------------------------------
185
186     /***************************************************************************
187      **************************************************************************/
188     //FIXME: Archiwum powinno być inicjowane dla konkretnego typu STORAGE
189     //W przypadku klas moÅŒe to być tylko interfejs
190     static class Archive {
191         private:
192         //BUG: private attribute is not applied to template functions
193         //BUG: how to check if ArchiveExt is defined?
194
195         mixin CONCRETEARCHIVE!(Archive);
196
197         static assert (IndexOf!(STORAGETYPE, AvailableStorageTypes) != -1, "Only following storage types are available: " ~ AvailableStorageTypes.stringof);
198
199         Mode mode = Mode.DUMP;              //serialization/deserialization
200         Registry registry;
201
202         this() {
203             registry = new Registry;
204             static if(is(typeof(init()))) {
205                 bool ret = init();
206                 if (!ret) {
207                     throw new SerializerException("Could not init archiver");
208                 }
209             }
210         }
211
212 public:
213         Archive dup_info() {
214             Archive result;
215
216             static if(is(typeof(dup_infoext()) == Archive)) {
217                 result = dup_infoext;
218             } else result = new Archive;
219
220             result.tracking = tracking;
221             result.registry = registry.dup_info;
222
223             result.level = level;
224             result.mode = mode;
225
226             return result;
227         }
228
229         //BUG: allow following style of declaration ??????:
230         //void describe(TYPES...)(ref TYPES params, string name="")
231         //or: void describe(TYPES...)(string name="", ref TYPES params)
232
233         /***************************************************************************
234          **************************************************************************/
235         bool traverse(VALUE)(ref VALUE value) {
236             pragma(msg, "traverse: " ~ VALUE.stringof);
237
238             static if (!is(VALUE TYPE == typedef)) {
239                 //level is not increased for typedefs
240                 level++;
241                 scope(success) level--;
242             }
243
244             //Eventual additional operations ... at begining of traverse...
245             //BUG: SFINAE should fail on syntax errors... Currently simple syntax errors
246             //are very difficult to catch
247             static if (is(typeof(begin!(VALUE)(value)) == bool)) {
248                 begin!(VALUE)(value);
249             }
250
251             //... and at the end
252             static if (is(typeof(finish!(VALUE)(value)) == bool)) {
253                 scope(success) finish!(VALUE)(value);
254             }
255
256             //TODO: shouldn't it be an option?
257             //runtime extension parsers for type;
258             if (auto peek = registry.peek!(TypeDescription, VALUE)) {
259                 if (mode == Mode.LOAD && peek.loader !is null) return peek.loader(value, this);
260                 if (mode == Mode.DUMP && peek.dumper !is null) return peek.dumper(value, this);
261             }
262
263             //BUG: Trick with delegate doesn't work here. Why?
264             //compile time extension function for parsing
265             //Below line is for debuging; change 0 to 1
266             static if (0 && CONCRETEARCHIVE.stringof == "JsonArchiveExt(ARCHIVE)" && VALUE.stringof =="Date") return extend!(VALUE)(value);
267             static if (is(typeof(extend!(VALUE)(value)) == bool)) {
268                 return extend!(VALUE)(value);
269             }
270
271             //Typedefs
272             static if (is(VALUE TYPE == typedef)) {
273                 return traverse(cast(Unqual!TYPE)value);
274             } else
275             //Pointers
276             static if (is(VALUE TYPE == TYPE*)) {
277                 if (mode == Mode.LOAD) {
278                     if(loadRef(value)) return true;
279
280                     if (tracking == Tracking.ON) {
281                         value = registry.get!(TypeDescription, VALUE).address(0); //construct and register
282                     } else {
283                         value = registry.get!(TypeDescription, VALUE).construct; //just construct - not register
284                     }
285
286                 } else {
287                     if(dumpRef(value)) return true;
288                 }
289
290                 return traverse(cast(Unqual!TYPE)*value);
291             } else
292             //Strings
293             static if (isSomeString!(VALUE)) {
294                 if (mode == Mode.LOAD) return loadString(value);
295                     else return dumpString(value);
296             } else
297             //Arrays
298             static if (is(VALUE U == U[])) {
299                 if (mode == Mode.LOAD) return loadArray(value);
300                     else return dumpArray(value);
301             } else
302             //Associative Arrays
303             static if (isAssociativeArray!(VALUE)) {
304                 if (mode == Mode.LOAD) return loadAssociativeArray(value);
305                     else return dumpAssociativeArray(value);
306             } else
307             //Structs
308             static if (is(VALUE == struct)) {
309                 if (mode == Mode.LOAD) return loadUdt(value);
310                     else return dumpUdt(value);
311             } else
312             //Classes
313             static if (is(VALUE == class)) {
314                 if (mode == Mode.LOAD) {
315                     if (loadRef(value)) return true;
316
317                     if (tracking == Tracking.ON) {
318                         value = registry.get!(TypeDescription, VALUE).address(0); //construct and register
319                     } else {
320                         value = registry.get!(TypeDescription, VALUE).construct; //just construct - not register
321                     }
322
323                     return loadUdt(value);
324                 } else {
325                     if (dumpRef(value)) return true;
326                     return dumpUdt(value);
327                 }
328             } else
329             static if (is(VALUE == bool)) {
330                 if (mode == Mode.LOAD) return loadBool(value);
331                     else return dumpBool(value);
332             } else
333             static if (isNumeric!(VALUE)) {
334                 if (mode == Mode.LOAD) return loadNumber(value);
335                     else return dumpNumber(value);
336             } else
337             static if (isSomeChar!(VALUE)) {
338                 if (mode == Mode.LOAD) return loadChar(value);
339                     else return dumpChar(value);
340             } else {
341                 static assert(false, "Unsupported type '" ~ VALUE.stringof ~
342                 "'. Types like void*, union, enum are not serializable without additional information");
343             }
344             //TODO: Unions - they are possibly serializable, but probably only through
345             //loadUdt/dumpUdt; Enums should be also serializable
346         }
347
348         /***************************************************************************
349             Returns: true if ok
350          **************************************************************************/
351         bool processMemberStruct(VALUE)(ref VALUE value) {
352             uint defined_ver = 0;
353             static if (is(typeof((cast(VALUE)value).versionUdt) == uint)) {
354                 defined_ver = (cast(VALUE)value).versionUdt;
355             }
356
357             auto reg = registry.peek!(TypeDescription, VALUE);
358             if (reg !is null && reg.ver > 0) defined_ver = reg.ver;
359
360             if (mode == Mode.LOAD) {
361                 uint stored_ver;
362                 bool loaded=false;
363                 auto verloaded = loadVersion(stored_ver);
364
365                 if (stored_ver != defined_ver) {
366                     static if ( is(typeof(&value.transformUdt!(Archive)) == void delegate(Archive, ref VALUE, uint))) {
367                         value.transformUdt(this, value, stored_ver);
368                         loaded = true;
369                     } else {
370                         throw new SerializerException("Udt version in storage is different than in program");
371                     }
372                 }
373
374                 if (!loaded) loadFields(value);
375
376             } else {
377                 if (!dumpVersion(defined_ver))
378                     throw new SerializerException("Can not dump version.");
379
380                 dumpFields(value);
381             }
382             return true;
383         }
384
385         /***************************************************************************
386          **************************************************************************/
387         bool processMemberStructFields(VALUE)(ref VALUE value) {
388
389             //Load function exists
390             //TODO: maybe functions loadUdt/dumpUdt/describeUdt should return bool as other functions?
391             //or returning bool should be dumped comletely as it is enough to throw exception?
392             static if ( is(typeof(&value.loadUdt!(Archive)) == void delegate(Archive))) {
393                 if (mode == Mode.LOAD) {value.loadUdt(this); return true;}
394             } else alias bool notExistsLoad;
395
396             //Dump function exists
397             static if ( is(typeof(&value.dumpUdt!(Archive)) == void delegate(Archive))) {
398                 if (mode == Mode.DUMP) {value.dumpUdt(this); return true;}
399             } else alias bool notExistsDump;
400
401
402             //BUG: One more place where it would be much better to have alias defining
403             //compile time constants
404             //or enum bool notExistsLoad = true; will work?
405             static if (is(notExistsLoad) || is(notExistsDump)) {
406                 //Exists describe function
407                 static if ( is(typeof(&value.describeUdt!(Archive)) == void delegate(Archive))) {
408                     value.describeUdt(this);
409                 } else {
410                     foreach(i, v; value.tupleof) {
411                         if (mode == Mode.LOAD) {
412                             normalizedType!(typeof(v)) elem;
413                             loadOneField(elem, value.tupleof[i].stringof);
414                             checkType!(typeof(v))(elem);
415
416                             static if (isStaticArray!(typeof(v))) {
417                                 foreach(n, saval; value.tupleof[i]) value.tupleof[i][n] = elem[n];
418                             } else {
419                                 value.tupleof[i]= elem;
420                             }
421
422                         } else {
423                             normalizedType!(typeof(v)) elem = v;
424                             dumpOneField(elem, value.tupleof[i].stringof);
425                         }
426                     }
427                 }
428             }
429             return true;
430         }
431
432
433         /***********************************************************************
434          **********************************************************************/
435         void describe(TYPE)(ref TYPE variable, string name) {
436             bool result;
437             //writefln("NAME: ", name, " TYPE: ", TYPE.stringof);
438             if (mode == Mode.LOAD) result = loadOneField(variable, name);
439                 else result = dumpOneField(variable, name);
440
441             if (!result) throw new SerializerException("Can not describe type '" ~ TYPE.stringof ~ "'.");
442         }
443
444         //BUG: Unfortunatelly doesn't work
445         //void describe(alias TYPE)() {
446         //    pragma(msg, "in alias version");
447         //    pragma(msg, TYPE.stringof);
448         //    describe!(typeof(TYPE))(*TYPE, TYPE.stringof);
449         //}
450
451         /***********************************************************************
452          **********************************************************************/
453         void transform(T)(ref T variable) {
454             if (!loadFields(variable))
455                 throw new Exception("Can not import user defined class.");
456         }
457
458         /***********************************************************************
459             Returns: reference to type description for type T
460          **********************************************************************/
461         TypeDescription!(T) typeDescription(T)() {
462             return registry.get!(TypeDescription, T);
463         }
464
465         //FIXME: should not be public
466         uint level = 0;                     ///current level in nested types
467         STORAGE storage;                    ///storage type (upcasted to interface type, to decrease code bloat)
468         Tracking tracking = Tracking.ON;    ///indicates if references should be tracked
469     }
470 }
Note: See TracBrowser for help on using the browser.