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/archive/JsonArchive.d

Revision 89, 31.3 kB (checked in by aarti_pl, 13 years ago)

- refactored strings to string enum
- updated test
- minor changes and corrections to compile with DMD 2.053

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.archive.JsonArchive;
17
18 //------------------------------------------------------------------------------
19 // BUG: DSSS can't pick this out from the template below
20 //      http://www.dsource.org/projects/dsss/ticket/193
21 import doost.text.Escaper;
22
23 /*******************************************************************************
24     This is JSON format archive for serializer.
25
26     Internal policy for returning bool from loaders/dumpers:
27     1. true - value was properly loaded/dumped
28     2. exception - loading of value was not succesfull (token not found or found
29        but is wrongly formatted)
30
31  ******************************************************************************/
32
33 template JsonArchive(ARCHIVE) {
34 private:
35     //Static functions and definitions
36
37     import std.regexp;
38     import std.typetuple;
39     import std.conv;
40     import std.range;
41     import std.utf;
42     import std.string : format;
43
44     import doost.api.Common;
45     import doost.util.serializer.Registry;
46     import doost.text.Escaper;
47     import doost.text.Scanner;
48     import doost.text.ElementSet;
49     import doost.text.SequenceSet;
50     import doost.text.Matcher;
51
52     import doost.util.serializer.Exception;
53
54     alias TypeTuple!(string, wstring, dstring) AvailableStorageTypes;
55
56     //------------------------------------------------------------------------------
57
58     private static struct Skip {
59         static STORAGETYPE space = " ";
60         static STORAGETYPE newline = "\n";
61         static STORAGETYPE indent = "    ";
62         static auto skip = ElementSet!(dchar).whitespace;
63     }
64
65     private static struct Bool {
66         static STORAGETYPE positive = "true";
67         static STORAGETYPE negative = "false";
68     }
69
70     private static struct String {
71         static auto delimiter = SequenceSet!(STORAGETYPE).strdelim;
72         static auto escdesc = &scanEscapeSequence!(STORAGE);
73         static auto allchars = ElementSet!(dchar).extended_alphanum;
74         static auto midchars = ElementSet!(dchar).digit;
75         static ELEMENTTYPE sdelimiter = '"';
76         static ELEMENTTYPE cdelimiter = '\'';
77     }
78
79     private static struct Region {
80         static auto begin = SequenceSet!(STORAGETYPE).openingbrackets;
81         static auto end = SequenceSet!(STORAGETYPE).closingbrackets;
82     }
83
84     private static struct Reference {
85         static STORAGETYPE defnull = "null";
86         static ELEMENTTYPE defref = '$';
87         static ELEMENTTYPE pointer = '*';
88     }
89
90     private static struct Array {
91         static ELEMENTTYPE begin = '[';
92         static ELEMENTTYPE separator = ',';
93         static ELEMENTTYPE end = ']';
94     }
95
96     private static struct Udt {
97         static STORAGETYPE ver = "versionUdt";
98         static ELEMENTTYPE begin = '{';
99         static ELEMENTTYPE kvseparator = ':';
100         static ELEMENTTYPE separator = ',';
101         static ELEMENTTYPE end = '}';
102     }
103
104     //--------------------------------------------------------------------------
105
106         bool mismatch = false;
107         STORAGETYPE rest = STORAGETYPE.init;
108         uint indent = 0;
109         bool line_begin = true;
110
111         //fields of specific class with values: content[name of fields]
112         STORAGETYPE[STORAGETYPE] memberStructFields;
113
114         Archive dup_infoext() {
115             Archive result = new Archive;
116             result.mismatch = false;
117             result.rest = STORAGETYPE.init;
118             return result;
119         }
120
121     //--------------------------------------------------------------------------
122
123     /*******************************************************************************
124      ******************************************************************************/
125     static class TypeDescription(T) : Registration {
126         mixin TypeDescriptionBase!(T) DEFAULT;
127
128         public typeof(this) dup_info() {
129             typeof(this) result = DEFAULT.dup_info;
130             return result;
131         }
132     }
133
134     /***************************************************************************
135      **************************************************************************/
136     bool init() {
137         return true;
138         //BUG: po dodaniu przecinka w ostatniej linijce dla tablic asocjacyjnych wyskakuje błÄ
139 d
140     }
141
142     /***************************************************************************
143         This function is started every time when serialization work is started.
144         It is good place to put code which will load/dump headers.
145      **************************************************************************/
146     bool start() {
147         return true;
148     }
149
150     /***************************************************************************
151         If this function exists in Archive it is called on every traverse, at the
152         beginning of traverse.
153      **************************************************************************/
154     bool begin(VALUE)(ref VALUE value) {
155         return true;
156     }
157
158     /***************************************************************************
159         This function is started every time when serialization work is finished.
160         It is good place to put code which will load/dump footers.
161      **************************************************************************/
162     bool finish(VALUE)(VALUE value) {
163         return true;
164     }
165
166     /***************************************************************************
167         Return the indentation string for the current indentation level.
168         Does not use or modify the line_begin flag.
169      **************************************************************************/
170     STORAGETYPE indentString() {
171         STORAGETYPE indent_str = STORAGETYPE.init;
172         for(int i=0; i<indent; i++) {
173             indent_str ~= Skip.indent;
174         }
175         return indent_str;
176     }
177
178     /***************************************************************************
179         Write the current indentation string to the storage.
180         Postcondition: line_begin flag set to false.
181      **************************************************************************/
182     bool dumpIndent() {
183         line_begin = false;
184         storage.put(indentString());
185         return true;
186     }
187
188     /***************************************************************************
189      **************************************************************************/
190     bool loadString(VALUE)(ref VALUE value) {
191         skip(storage, Skip.skip, uint.max);
192
193         auto m = scanString!(STORAGE)(storage, String.delimiter, String.escdesc, String.allchars, String.midchars);
194         if (m.status != Matcher.matched)
195             throw new ParsingException("Expected string or char in input, but it wasn't found.");
196
197         STORAGETYPE val;
198         val = munch(storage, m);
199
200         auto delim = munch(val, String.delimiter);
201         auto len = delim.length;
202
203         if (len>0) {
204             val = val[0..$-len];
205             val = unescape(val);
206         }
207
208         value = to!(VALUE)(val);
209
210         return true;
211     }
212
213     /***************************************************************************
214      **************************************************************************/
215     bool dumpString(VALUE)(ref VALUE value) {
216         if (line_begin) dumpIndent();
217         storage.put( String.sdelimiter ~
218                             to!(STORAGETYPE)(escape(value)) ~
219                             String.sdelimiter);
220         return true;
221     }
222
223     /***************************************************************************
224      **************************************************************************/
225     Matcher scanBool(ref STORAGE input) {
226         Matcher mp;
227
228         mp = scan(input, Bool.positive); if (mp.status == Matcher.matched) return mp;
229         mp = scan(input, Bool.negative); if (mp.status == Matcher.matched) return mp;
230
231         return Matcher(0, Matcher.mismatch);
232     }
233
234     /***************************************************************************
235         Loads bool from Storage. Not intended to be used by library user.
236
237         Returns: true on success
238         Throws: ParsingException
239      **************************************************************************/
240     bool loadBool(VALUE)(ref VALUE value) {
241         skip(storage, Skip.skip, uint.max);
242         STORAGETYPE literal;
243
244         auto mp = scanBool(storage);
245         if (mp.status == Matcher.matched) {
246             literal = munch(storage, mp);
247         } else throw new ParsingException("Unknown bool literal.");
248
249         if (literal == "true") value = true;
250             else value = false;
251
252         return true;
253     }
254
255     /***************************************************************************
256         Dumps bool to Storage. Not intended to be used by library user.
257
258         Returns: true on success
259      **************************************************************************/
260     bool dumpBool(VALUE)(ref VALUE value) {
261         if (line_begin) dumpIndent();
262         storage.put((value == true) ? Bool.positive
263                             : Bool.negative);
264         return true;
265     }
266
267     /***************************************************************************
268      **************************************************************************/
269     bool loadNumber(VALUE)(ref VALUE value) {
270         skip(storage, Skip.skip, uint.max);
271         auto m = scanNumber(storage);
272         if (m.status != Matcher.matched) throw new ParsingException("Expected number in source, but it wasn't found.");
273
274         value = to!(VALUE)(munch(storage, m));
275         return true;
276     }
277
278     /***************************************************************************
279      **************************************************************************/
280     bool dumpNumber(VALUE)(ref VALUE value) {
281         if (line_begin) dumpIndent();
282         storage.put(to!(STORAGETYPE)(value));
283         return true;
284     }
285
286     /***************************************************************************
287      **************************************************************************/
288     bool loadChar(VALUE)(ref VALUE value) {
289         STORAGETYPE val;
290         loadString(val);
291
292         size_t idx = 0;
293         dchar c = decode(val, idx);
294
295         idx++;
296         if (idx < val.length) throw new ParsingException(format(ErrorStrings.WRONG_CHAR_LITERAL, val));
297
298         static if (is(Unqual!VALUE == char)) {
299             if (c > 127) throw new SerializerException(format(ErrorStrings.CHAR_CONV_ERROR, cast(uint)c, VALUE.stringof));
300             value = cast(VALUE)c;
301         } else
302         static if (is(Unqual!VALUE == wchar)) {
303             if (c > 32767) throw new SerializerException(format(ErrorStrings.CHAR_CONV_ERROR, cast(uint)c, VALUE.stringof));
304             value = cast(VALUE)c;
305         } else
306         static if (is(Unqual!VALUE == dchar)) {
307             value = c;
308         } else {
309             static assert(false, "Wrong type passed to loadChar method: " ~ VALUE.stringof);
310         }
311
312         return true;
313     }
314
315     /***************************************************************************
316      **************************************************************************/
317     bool dumpChar(VALUE)(ref VALUE value) {
318         if (line_begin) dumpIndent();
319         storage.put(String.cdelimiter ~ to!(STORAGETYPE)(value) ~ String.cdelimiter);
320         return true;
321     }
322
323     /***************************************************************************
324      **************************************************************************/
325     bool loadElement(VALUE, SEP)(ref VALUE value, SEP esep) {
326         if (mismatch) throw new ParsingException("Missing elements separator.");
327         bool matched = traverse(value);
328         if (!matched) return false;
329
330         skip(storage, Skip.skip, uint.max);
331         mismatch = !skip(storage, esep);
332         return true;
333     }
334
335     /***************************************************************************
336      **************************************************************************/
337     bool dumpElement(VALUE, SEP)(ref VALUE value, SEP sep) {
338         if (line_begin) dumpIndent();
339         storage.put(rest);
340         bool res = traverse(value);
341         rest = sep ~ Skip.space;
342         return res;
343     }
344
345     /***************************************************************************
346      **************************************************************************/
347     bool loadArray(VALUE)(ref VALUE value) {
348         skip(storage, Skip.skip, uint.max);
349
350         if (!skip(storage, Array.begin))
351             throw new ParsingException("Missing array literal begining delimiter.");
352
353         ElementType!(VALUE) elem;
354         VALUE result;
355         mismatch = false;
356         do {
357             skip(storage, Skip.skip, uint.max);
358             if (skip(storage, Array.end)) break;
359             loadElement(elem, Array.separator);
360             result ~= elem;
361         } while(true);
362
363         value = result;
364         return true;
365     }
366
367     /***************************************************************************
368      **************************************************************************/
369     bool dumpArray(VALUE)(ref VALUE value) {
370         if (line_begin) dumpIndent();
371         storage.put(Array.begin);
372
373         rest = STORAGETYPE.init;
374         foreach(v; value)
375             dumpElement(v, Array.separator);
376
377         storage~=Array.end;
378         return true;
379     }
380
381     /***************************************************************************
382      **************************************************************************/
383     bool loadAssociativeElement(KEY, VALUE, KVSEP, SEP)(ref KEY key, ref VALUE value, KVSEP kvsep, SEP esep) {
384         if (mismatch) throw new ParsingException("Missing associative elements separator.");
385         bool matched = traverse(key);
386         if(!matched) return false;
387
388         skip(storage, Skip.skip, uint.max);
389         if (!skip(storage, kvsep))
390             throw new ParsingException("Missing associative array literal key-value separator.");
391
392         traverse(value);
393
394         skip(storage, Skip.skip, uint.max);
395         if (!skip(storage, esep)) mismatch = true;
396
397         return matched;
398     }
399
400     /***************************************************************************
401      **************************************************************************/
402     bool dumpAssociativeElement(KEY, VALUE, KVSEP, SEP)(ref KEY key, ref VALUE value, KVSEP kvsep, SEP esep) {
403         storage.put(rest);
404         traverse(key);
405         if (line_begin) dumpIndent();
406         storage.put(Skip.space ~ kvsep ~ Skip.space);
407         traverse(value);
408         rest = esep ~ Skip.newline ~ indentString();
409
410         return true;
411     }
412
413     /***************************************************************************
414      **************************************************************************/
415     bool loadAssociativeArray(VALUE)(ref VALUE value) {
416         alias typeof(VALUE.init.values[0]) ValueType;
417         alias typeof(VALUE.init.keys[0]) KeyType;
418
419         skip(storage, Skip.skip, uint.max);
420         if (!skip(storage, Udt.begin))
421             throw new ParsingException("Missing associative array literal begining delimiter.");
422
423         KeyType key;
424         ValueType val;
425         VALUE result;
426
427         mismatch = false;
428         do {
429             skip(storage, Skip.skip, uint.max);
430             if (skip(storage, Udt.end)) break;
431             loadAssociativeElement(key, val, Udt.kvseparator, Udt.separator);
432             result[key] = val;
433         } while(true);
434
435         value = result;
436         return true;
437     }
438
439     /***************************************************************************
440      **************************************************************************/
441     bool dumpAssociativeArray(VALUE)(ref VALUE value) {
442         alias typeof(VALUE.init.values[0]) ValueType;
443         alias typeof(VALUE.init.keys[0]) KeyType;
444
445         KeyType key;
446         ValueType val;
447
448         if (line_begin) dumpIndent();
449         storage.put(Udt.begin ~ Skip.newline);
450         line_begin = true;
451         indent++;
452
453         rest = STORAGETYPE.init;
454         foreach(k, v; value) {
455             dumpAssociativeElement(k, v, Udt.kvseparator, Udt.separator);
456         }
457         indent--;
458         storage.put(Skip.newline);
459         line_begin = true;
460         if (line_begin) dumpIndent();
461         storage.put(Udt.end);
462
463         return true;
464     }
465
466     /***************************************************************************
467      **************************************************************************/
468     Matcher scanRef(ref STORAGE input) {
469         Matcher m;
470         m = scan(input, Reference.defref);
471         if (m.status != Matcher.matched) return m;
472         m = scan(input, ElementSet!(dchar).digit, 0u, uint.max);
473         return m;
474     }
475
476
477     /***************************************************************************
478      **************************************************************************/
479     Matcher scanNull(ref STORAGE input) {
480         return scan(input, Reference.defnull);
481     }
482
483     /***************************************************************************
484      **************************************************************************/
485     bool loadRef(VALUE)(ref VALUE value) {
486         skip(storage, Skip.skip, uint.max);
487         if (skip(storage, Reference.defnull)) {
488             value = null;
489             return true;
490         }
491
492         if (tracking == Tracking.ON) {
493             if (skip(storage, Reference.defref)) {
494                 uint symbol;
495                 if (!traverse(symbol)) throw new ParsingException("Lack of symbol!");
496                 value = registry.get!(TypeDescription, VALUE).address(symbol);
497                 return true;
498             }
499         }
500
501         static if (!is(VALUE == class)) {
502             skip(storage, Skip.skip, uint.max);
503             if (!skip(storage, Reference.pointer))
504                 throw new ParsingException("Lack of pointer symbol.");
505         }
506         return false;
507     }
508
509     /***************************************************************************
510      **************************************************************************/
511     bool dumpRef(VALUE)(ref VALUE value) {
512         if (value is null) {
513             if (line_begin) dumpIndent();
514             storage.put(Reference.defnull);
515             return true;
516         }
517
518         if (tracking == Tracking.ON) {
519             uint symbol = registry.get!(TypeDescription, VALUE).symbol(value);
520             if (symbol > 0) {
521                 if (line_begin) dumpIndent();
522                 storage.put(Reference.defref ~ to!(STORAGETYPE)(symbol));
523                 return true;
524             }
525         }
526
527         static if (!is(VALUE == class)) {
528             if (line_begin) dumpIndent();
529             storage.put(Reference.pointer);
530         }
531
532         return false;
533     }
534
535     /***************************************************************************
536      **************************************************************************/
537     bool loadVersion(ref uint stored_ver) {
538         stored_ver = 0;
539
540         if (Udt.ver in memberStructFields) {
541             try {
542                 stored_ver = to!(uint)(memberStructFields[Udt.ver]);
543             } catch(Exception e) {
544                 throw new ParsingException("Wrong version format: \n" ~ e.toString);
545             }
546             return true;
547         }
548         return false;
549     }
550
551     /***************************************************************************
552      **************************************************************************/
553     bool dumpVersion(ref uint defined_ver) {
554         if (defined_ver>0) memberStructFields[Udt.ver] = to!(STORAGETYPE)(defined_ver);
555         return true;
556     }
557
558     /***************************************************************************
559      **************************************************************************/
560     string normalizeName(string name) {
561         int pos = std.string.lastIndexOf(name, '.');
562         if (pos > 0) name = name[pos+1..$];
563         return name;
564     }
565
566     /***************************************************************************
567      **************************************************************************/
568     bool loadOneField(VALUE)(ref VALUE value, string n) {
569         n = normalizeName(n);
570         STORAGETYPE name = to!STORAGETYPE(n);
571
572         if (!(name in memberStructFields)) {
573             throw new ParsingException("Field is not defined in input: " ~ n);
574         }
575
576         *storage.frame() = memberStructFields[name] ~ *storage.frame();
577
578         if (!traverse(value))
579             throw new ParsingException("Parsing error");
580
581         return true;
582     }
583
584     /***************************************************************************
585      **************************************************************************/
586     bool dumpOneField(VALUE)(ref VALUE value, string name) {
587         name = normalizeName(name);
588
589         if (!dumpAssociativeElement(name, value, Udt.kvseparator, Udt.separator))
590             throw new SerializerException("Can not dump'" ~ VALUE.stringof ~ "'.");
591         return true;
592     }
593
594     /***************************************************************************
595      **************************************************************************/
596     bool loadFields(VALUE)(ref VALUE value) {
597         //FIXME: czy na pewno ten mismatch jest tu potrzebny?
598         mismatch = false;
599         processMemberStructFields(value);
600
601         return true;
602     }
603
604     /***************************************************************************
605      **************************************************************************/
606     bool dumpFields(VALUE)(ref VALUE value) {
607         if (line_begin) dumpIndent();
608         storage.put(String.sdelimiter
609                         ~   to!STORAGETYPE(VALUE.stringof)
610                         ~   String.sdelimiter
611                         ~   Skip.space
612                         ~   Udt.kvseparator
613                         ~   Skip.space
614                         ~   Udt.begin
615                         ~   Skip.newline);
616         line_begin = true;
617         indent++;
618
619         if (Udt.ver in memberStructFields) {
620             if (line_begin) dumpIndent();
621             storage.put(String.sdelimiter
622                             ~   Udt.ver
623                             ~   String.sdelimiter
624                             ~   Skip.space
625                             ~   Udt.kvseparator
626                             ~   Skip.space
627                             ~   memberStructFields[Udt.ver]
628                             ~   Udt.separator
629                             ~   Skip.newline);
630             line_begin = true;
631         }
632
633         rest = STORAGETYPE.init;
634         processMemberStructFields(value);
635
636         indent--;
637         storage.put(Skip.newline);
638         line_begin = true;
639         if (line_begin) dumpIndent();
640         storage.put(Udt.end ~  Skip.newline);
641         line_begin = true;
642         return true;
643     }
644
645     //BUG: remove differences between char and string (implicit conversion) ?? string s = char; ok
646
647     /***************************************************************************
648      **************************************************************************/
649     bool tokenizeMemberStructFields(string n, STORAGETYPE[STORAGETYPE] includedclasses) {
650         STORAGETYPE name = to!STORAGETYPE(n);
651         if (!(name in includedclasses))
652             throw new ParsingException("class is not defined in source");
653
654         skip(storage, Skip.skip, uint.max);
655
656         memberStructFields = null;
657
658         STORAGETYPE key;
659         STORAGETYPE val;
660
661         *storage.frame() = includedclasses[name] ~ *storage.frame();
662
663         if (!skip(storage, Udt.begin))
664             throw new ParsingException("No Udt begining delimiter");
665
666         bool last;
667
668         do {
669             skip(storage, Skip.skip, uint.max);
670             if (skip(storage, Udt.end)) break;
671             if (last) throw new ParsingException("Missing Udt separator");
672
673             key = STORAGETYPE.init;
674             loadString(key);
675
676             skip(storage, Skip.skip, uint.max);
677             if (!skip(storage, Udt.kvseparator))
678                 throw new ParsingException("No kvseparator");
679             skip(storage, Skip.skip, uint.max);
680
681             auto mp = scanValue(storage);
682             if (mp.status == Matcher.matched) {
683                 val = munch(storage, mp);
684             } else throw new ParsingException("No value for Udt field");
685
686             if (!skip(storage, Udt.separator)) {
687                 last = true;
688             }
689
690             if (key in memberStructFields)
691                 throw new ParsingException("Duplicated field entries in source.");
692
693             memberStructFields[key] = val;
694         } while(true);
695
696         return true;
697
698     }
699
700     //TODO: do importu. moÅŒna zarejestrować nowÄ
701  nazwę do starej klasy memberStruct
702     //tak, aby w imporcie było to automatycznie mapowane
703     /***************************************************************************
704      **************************************************************************/
705     bool loadUdt(VALUE)(ref VALUE value) {
706         skip(storage, Skip.skip, uint.max);
707
708         if (!skip(storage, Udt.begin))
709             throw new ParsingException(format(ErrorStrings.MISSING_BEGINNING_DELIMITER, Udt.begin));
710
711         STORAGETYPE[STORAGETYPE] includedclasses; // all classes which are part of higher level class
712         STORAGETYPE key;
713         STORAGETYPE val;
714         bool last = false;
715
716         Matcher m;
717
718         do {
719             key = STORAGETYPE.init;
720             if (skip(storage, Udt.end)) break;
721
722             loadString(key);
723
724             if (last) throw new ParsingException("No Udt separator");
725
726             if (key in includedclasses)
727                 throw new ParsingException("Duplicated class information!");
728
729             skip(storage, Skip.skip, uint.max);
730             if (!skip(storage, Udt.kvseparator))
731                 throw new ParsingException("Missing key - value separator");
732
733             skip(storage, Skip.skip, uint.max);
734             m = scanRegion!(STORAGE)(storage, Region.begin, Region.end, String.delimiter, String.escdesc, String.allchars, String.midchars);
735
736             //BUG: nie moÅŒna porównać char i string
737             if (m.status == Matcher.matched && storage.peek() == Udt.begin) val = munch(storage, m);
738                 else throw new ParsingException("Unknown token");
739
740             skip(storage, Skip.skip, uint.max);
741
742             if (!skip(storage, Udt.separator)) {
743                 last = true;
744             }
745
746             includedclasses[key] = val;
747         } while(true);
748
749         tokenizeMemberStructFields(VALUE.stringof, includedclasses);
750         processMemberStruct(value);
751
752         static if( is( VALUE == class )) {
753             //BUG: a way to create class without using constructor
754             //BUG: a way to access all fields from class neverthless of protection attributes
755
756             foreach(i, BASE; BaseTypeTuple!(VALUE)) {
757                 static if (!is(BASE == interface) && BASE.tupleof.length != 0)
758                 {
759                     BASE res = cast(BASE)value;
760                     tokenizeMemberStructFields(BASE.stringof, includedclasses);
761                     processMemberStruct(res);
762                 }
763             }
764         }
765
766         return true;
767     }
768
769     /***************************************************************************
770      **************************************************************************/
771     bool dumpUdt(VALUE)(ref VALUE value) {
772         if (line_begin) dumpIndent();
773         storage.put( Udt.begin ~ Skip.newline);
774         indent++;
775         line_begin = true;
776
777         processMemberStruct(value);
778
779         static if( is( VALUE == class )) {
780             foreach(i, BASE; BaseTypeTuple!(VALUE)) {
781                 static if (is (BASE == interface) || BASE.tupleof.length == 0) {}
782                 else {
783                     if (line_begin) dumpIndent();
784                     storage.put(Udt.separator ~ Skip.newline);
785                     line_begin = true;
786                     BASE val = cast(BASE)value;
787                     processMemberStruct(val);
788                 }
789             }
790         }
791
792         indent--;
793         if (line_begin) dumpIndent();
794         storage.put(Udt.end);
795
796         return true;
797     }
798
799     /***************************************************************************
800      **************************************************************************/
801     Matcher scanValue(ref STORAGE input) {
802         Matcher m;
803         //arrays + udt
804         m = scanRegion!(STORAGE)(input, Region.begin, Region.end, String.delimiter, String.escdesc, String.allchars, String.midchars);
805         if (m.status == Matcher.matched) return m;
806
807         m = scanNull(input);
808         if (m.status == Matcher.matched) return m;
809
810         m = scanRef(input);
811         if (m.status == Matcher.matched) return m;
812
813         m = scanNumber(input);
814         if (m.status == Matcher.matched) return m;
815
816         //Char will be threated as String
817         m = scanString!(STORAGE)(input, String.delimiter, String.escdesc, String.allchars, String.midchars);
818         if (m.status == Matcher.matched) return m;
819
820         return Matcher(0, Matcher.mismatch);
821     }
822 }
823
824 /*******************************************************************************
825  ******************************************************************************/
826 template JsonArchiveExt(ARCHIVE) {
827 private:
828     import std.date;
829     import std.string;
830     import doost.api.Common;
831     import doost.util.serializer.archive.JsonArchive;
832     import doost.text.Scanner;
833
834     string[] days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
835
836     mixin JsonArchive!(ARCHIVE);
837
838     /***************************************************************************
839      **************************************************************************/
840     //TODO: funkcje load.. powinny zwracać matchera
841     bool loadDate(VALUE)(ref VALUE value) {
842         skip(storage, Skip.skip, uint.max);
843
844         auto m = scanDate(storage);
845         if (!cast(bool)m) throw new SerializerException("Can not load date!");
846
847         auto str = munch(storage, m);
848         value.parse(str);
849         return true;
850     }
851
852     /***************************************************************************
853      **************************************************************************/
854     bool dumpDate(VALUE)(ref VALUE value) {
855         if (value.weekday>0 && value.weekday<8) storage.put(days[value.weekday-1]~", ");
856
857         storage.put(format("%4s-%02s-%02s %02s:%02s:%02s",
858             value.year, value.month, value.day, value.hour, value.minute,
859             value.second));
860
861         if (value.tzcorrection > int.min && value.tzcorrection!=0) {
862             storage.put(" ");
863             if (value.tzcorrection>0) storage.put("+");
864             storage.put(format(value.tzcorrection));
865         }
866         return true;
867     }
868
869     //BUG: not possible to do it
870     //alias TextArchive!(ARCHIVE).Pattern Pattern;
871     //mixin(generator("Pattern(T : Date)", ["pattern", r"\d\d\d\d-[\d]?\d-[\d]?\d[ \d\d:\d\d:\d\d[.\d\d\d]?]?"]));
872
873     /***************************************************************************
874      **************************************************************************/
875     //BUG: can not mixin this as traverse because symbol is already defined
876     bool extend(VALUE: Date)(ref VALUE value) {
877         if (mode == Mode.LOAD) return loadDate!(VALUE)(value);
878             else return dumpDate!(VALUE)(value);
879     }
880 }
Note: See TracBrowser for help on using the browser.