Mercurial > pub > Impl
comparison Lib/IMPL/Serialization.pm @ 0:03e58a454b20
Создан репозитарий
author | Sergey |
---|---|
date | Tue, 14 Jul 2009 12:54:37 +0400 |
parents | |
children | 16ada169ca75 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:03e58a454b20 |
---|---|
1 | |
2 # 20060222 | |
3 # Ìîäóëü äëÿ ñåðèàëèçàöèè ñòðóêòóð äàííûõ | |
4 # (ö) Sourcer, cin.sourcer@gmail.com | |
5 # revision 3 (20090517) | |
6 | |
7 | |
8 package IMPL::Serialization; | |
9 use strict; | |
10 | |
11 package IMPL::Serialization::Context; | |
12 use base qw(IMPL::Object); | |
13 | |
14 use IMPL::Class::Property; | |
15 use IMPL::Class::Property::Direct; | |
16 use IMPL::Exception; | |
17 use Scalar::Util qw(refaddr); | |
18 | |
19 BEGIN { | |
20 private _direct property ObjectWriter => prop_all; # îáúåêò, çàïèñûâàþùèé äàííûå â ïîòîê | |
21 private _direct property Context => prop_all; # êîíòåêñò (îáúåêòû êîòîðûå óæå ñåðèàëèçîâàíû, èõ èäåíòèôèêàòîðû) | |
22 private _direct property NextID => prop_all;# ñëåäóþùèé èäåíòèôèêàòîð äëÿ îáúåêòà | |
23 | |
24 # ïðîöåäóðà, êîòîðàÿ çíàåò, êàê ñåðèàëèçîâûâàòü îïðåäåëåííûå òèïû. Ïåðâûì ïàðàìåòðîì | |
25 # ïîëó÷àåì ññûëêó íà IMPL::Serialization::Context, âòîðûì ïàðàìåòðîì ññûëêó íà îáúåêò | |
26 public _direct property Serializer => prop_all; | |
27 | |
28 private _direct property State => prop_all; # ñîñòîÿíèå êîíòåêñòà ñåðèàëèçàöèè | |
29 } | |
30 | |
31 # êîíòåêñò çàêðûò, ò.å. íèêàêîé îáúåêò íå íà÷àò | |
32 sub STATE_CLOSED () { 0 } | |
33 # êîíòåêñò îòêðûò, ò.å. îáúåêò íà÷àò, íî â íåì åùå íè÷åãî íå ëåæèò | |
34 sub STATE_OPENED () { 1 } | |
35 # êîíòåêñò îòêðûò è â íåãî ìîãóò áûòü äîáàâëåíû òîëüêî ïîäîáúåêòû | |
36 sub STATE_COMPLEX () { 2 } | |
37 # êîíòåêñò îòêðûò è â íåãî óæå íè÷åãî íå ìîæåò áûòü äîáàâëåíî, òàì ëåæàò äàííûå | |
38 sub STATE_DATA () { 3 } | |
39 | |
40 sub CTOR { | |
41 my ($this,%args) = @_; | |
42 | |
43 $this->{$ObjectWriter} = $args{'ObjectWriter'}; | |
44 #$this->{$Context} = {}; | |
45 $this->{$NextID} = 1; | |
46 $this->{$Serializer} = ($args{'Serializer'} ? $args{'Serializer'} : \&DefaultSerializer ); | |
47 $this->{$State} = STATE_CLOSED; | |
48 | |
49 return 1; | |
50 } | |
51 | |
52 sub AddVar { | |
53 my ($this,$sName,$Var) = @_; | |
54 | |
55 die new Exception ('Invalid operation') if $this->{$State} == STATE_DATA; | |
56 | |
57 if (not ref $Var) { | |
58 # íåìíîãî äóáëèðóåòñÿ òî, ÷òî ñíèçó, íî ýòî ðàäè òîãî, ÷òîáû îáúåêòû, êîòîðûå èäóò | |
59 # íå ïî ññûëêå, íå ïîëó÷àëè èäåíòèôèêàòîðà, èì îí íå íóæåí | |
60 my $prevState = $this->{$State}; | |
61 | |
62 $this->{$ObjectWriter}->BeginObject(name => $sName);#, type => 'SCALAR'); | |
63 $this->{$State} = STATE_OPENED; | |
64 | |
65 $this->{$Serializer}->($this,\$Var); | |
66 | |
67 $this->{$ObjectWriter}->EndObject(); | |
68 | |
69 if ($prevState == STATE_OPENED) { | |
70 $this->{$State} = STATE_COMPLEX; | |
71 } else { | |
72 $this->{$State} = $prevState; | |
73 } | |
74 return 0; | |
75 } | |
76 | |
77 my $PrevState = $this->{$State}; | |
78 | |
79 my $ObjID = $this->{$Context}->{refaddr $Var}; | |
80 if ($ObjID) { | |
81 $this->{$ObjectWriter}->BeginObject(name => $sName, refid => $ObjID); | |
82 $this->{$ObjectWriter}->EndObject(); | |
83 return $ObjID; | |
84 } | |
85 | |
86 $ObjID = $this->{$NextID}; | |
87 $this->{$NextID} = $ObjID + 1; | |
88 | |
89 $this->{$Context}->{refaddr $Var} = $ObjID; | |
90 | |
91 $this->{$ObjectWriter}->BeginObject(name => $sName, type => ref($Var), id => $ObjID); | |
92 | |
93 $this->{$State} = STATE_OPENED; | |
94 $this->{$Serializer}->($this,$Var); | |
95 | |
96 $this->{$ObjectWriter}->EndObject(); | |
97 | |
98 if ($PrevState == STATE_OPENED) { | |
99 $this->{$State} = STATE_COMPLEX; | |
100 } else { | |
101 $this->{$State} = $PrevState; | |
102 } | |
103 | |
104 return $ObjID; | |
105 } | |
106 | |
107 sub SetData { | |
108 my ($this,$Data,$Type) = @_; | |
109 | |
110 die new Exception ('The object should be a scalar value') if ref $Data; | |
111 die new Exception ('Invalid operation') if $this->{$State} != STATE_OPENED; | |
112 | |
113 $this->{$ObjectWriter}->SetData($Data,$Type); | |
114 | |
115 $this->{$State} = STATE_DATA; | |
116 | |
117 return 1; | |
118 } | |
119 | |
120 sub DefaultSerializer { | |
121 my ($Context, $refObj) = @_; | |
122 | |
123 if (ref($refObj) eq 'SCALAR') { | |
124 $Context->SetData($$refObj, 'SCALAR'); | |
125 } elsif (ref($refObj) eq 'ARRAY') { | |
126 $Context->AddVar('item',$_) foreach @$refObj; | |
127 } elsif (ref($refObj) eq 'HASH') { | |
128 while (my ($key,$value) = each %$refObj) { | |
129 $Context->AddVar($key,$value); | |
130 } | |
131 } elsif (ref($refObj) eq 'REF') { | |
132 $Context->AddVar('ref',$$refObj); | |
133 } else { | |
134 if (ref $refObj and $refObj->UNIVARSAL::can('save')) { | |
135 $refObj->save($Context); | |
136 } else { | |
137 die new Exception('Cant serialize the object of the type: '.ref($refObj)); | |
138 } | |
139 } | |
140 | |
141 return 1; | |
142 } | |
143 | |
144 package IMPL::Deserialization::Context; | |
145 use base qw(IMPL::Object); | |
146 | |
147 use IMPL::Class::Property; | |
148 use IMPL::Class::Property::Direct; | |
149 use IMPL::Exception; | |
150 | |
151 BEGIN { | |
152 # óæå äåñåðèàëèçîâàííûå îáúåêòû, õåø, êëþ÷ - èäåíòèôèêàòîð, çíà÷åíèå - ññûëêà. | |
153 private _direct property Context => prop_all; | |
154 | |
155 # òåêóùèé îáúåêò. èíôîðìàöèÿ äëÿ äåñåðèàëèçàöèè | |
156 # { | |
157 # Type => 'typename', | |
158 # Name => 'object_name', | |
159 # Data => $Data, | |
160 # Id => 'object_id' | |
161 # } | |
162 private _direct property CurrentObject => prop_all; | |
163 | |
164 # ñòåê îáúåêòîâ. ñþäà äîáàâëÿþòñÿ îïèñàíèÿ îáúåêòîâ ïî ìåðå âñòðå÷àíèÿ íîâûõ îáúåêòîâ. | |
165 private _direct property ObjectsPath => prop_all; | |
166 | |
167 # ñþäà ïîïàäåò êîðåíü ãðàôà îáúåêòîâ | |
168 public _direct property Root => prop_get; | |
169 | |
170 # ñîçäàåò îáúåêò è âîçâðàùàåò íà íåãî ññûëêó | |
171 # ObjectFactory($Type,$DeserializationData,$refSurogate) | |
172 # $Type - èìÿ òèïà äàííûõ | |
173 # $DeserializationData - ëèáî ññûëêà íà ìàññèâ ñ äàííûìè äëÿ äåñåðèàëèçàöèè ïîëåé, | |
174 # ëèáî ñêàëÿð ñîäåðæàùèé äàííûå. | |
175 # $refSurogate - ññûëêà íà ïðåäâàðèòåëüíî ñîçäàííûé, íå èíèöèàëèçèðîâàííûé îáúåêò. | |
176 # ìîæåò ïðèíèìàòü çíà÷åíèå undef | |
177 private _direct property ObjectFactory => prop_all; | |
178 | |
179 # Ñîçäàåò íåèíèöèàëèçèðîâàííûå îáúåêòû. | |
180 # SurogateHelper($Type) | |
181 # $Type èìÿ òèïïà, ÷åé ñóðîãàò íóæíî ñîçäàòü. | |
182 private _direct property SurogateHelper => prop_all; | |
183 } | |
184 | |
185 sub CTOR { | |
186 my ($this,%args) = @_; | |
187 $this->{$CurrentObject} = undef; | |
188 $this->{$Root} = undef; | |
189 } | |
190 | |
191 sub OnObjectBegin { | |
192 my ($this,$name,$rhProps) = @_; | |
193 | |
194 die new Exception("Invalid data from an ObjectReader","An object reader should pass a referense to a hash which contains attributes of an object") if (ref $rhProps ne 'HASH'); | |
195 die new Exception("Trying to create second root object") if not $this->{$CurrentObject} and $this->{$Root}; | |
196 | |
197 if ($rhProps->{'refid'}) { | |
198 my $refObj = $this->{$Context}->{$rhProps->{'refid'}}; | |
199 die new Exception("A reference to a not existing object found") if not $refObj; | |
200 my $rhCurrentObj = $this->{$CurrentObject}; | |
201 | |
202 die new Exception("Found a reference to an object as a root of an object's graph") if not $rhCurrentObj; | |
203 | |
204 if ($rhCurrentObj->{'Data'}) { | |
205 die new Exception("Invalid serializaed data","Plain deserialization data for an object already exist") if not ref $rhCurrentObj->{'Data'}; | |
206 push @{$rhCurrentObj->{'Data'}}, $name,$refObj; | |
207 } else { | |
208 $rhCurrentObj->{'Data'} = [$name,$refObj]; | |
209 } | |
210 | |
211 # ýòî çàòåì, ÷òî áóäåò âûçâàí OnObjectEnd äëÿ îáúåêòà, êîòîðûé áûë ïðîñòîé ññûëêîé. ò.î. ìû íå íàðóøèì ñòåê | |
212 push @{$this->{$ObjectsPath}},$rhCurrentObj; | |
213 $this->{$CurrentObject} = undef; | |
214 | |
215 } else { | |
216 push @{$this->{$ObjectsPath}},$this->{$CurrentObject} if $this->{$CurrentObject}; | |
217 | |
218 $this->{$CurrentObject} = { | |
219 Name => $name, | |
220 Type => $rhProps->{'type'} || 'SCALAR', | |
221 Id => $rhProps->{'id'}, | |
222 refId => $rhProps->{'refid'} | |
223 }; | |
224 $this->{$Context}->{$rhProps->{'id'}} = $this->{$SurogateHelper} ? $this->{$SurogateHelper}->($rhProps->{'type'}) : DefaultSurogateHelper($rhProps->{'type'}) if defined $rhProps->{'id'}; | |
225 } | |
226 | |
227 return 1; | |
228 } | |
229 | |
230 sub OnObjectData { | |
231 my ($this,$data) = @_; | |
232 | |
233 my $rhObject = $this->{$CurrentObject}; | |
234 | |
235 die new Exception("Trying to set data for an object which not exists") if not $rhObject; | |
236 | |
237 die new Exception("Deserialization data already exists for a current object", "ObjectName= $rhObject->{'Name'}") if $rhObject->{'Data'}; | |
238 | |
239 $rhObject->{'Data'} = $data; | |
240 | |
241 return 1; | |
242 } | |
243 { | |
244 my $AutoId = 0; | |
245 sub OnObjectEnd { | |
246 my ($this,$name) = @_; | |
247 | |
248 my $rhObject = $this->{$CurrentObject}; | |
249 my $rhPrevObject = pop @{$this->{$ObjectsPath}}; | |
250 | |
251 # åñëè òåêóùèé îáúåêò íå îïðåäåëåí, à ïðåäûäóùèé - îïðåäåëåí, çíà÷èò òåêóùèé - ýòî ññûëêà | |
252 # ïðîñòî âîññòàíàâëèâàåì ïðåäûäóùèé â òåêóùèé è íè÷åãî áîëåå íå äåëàåì | |
253 if ((not defined($rhObject)) && $rhPrevObject) { | |
254 $this->{$CurrentObject} = $rhPrevObject; | |
255 return 1; | |
256 } | |
257 | |
258 my $refObj = $this->{$ObjectFactory} ?$this->{$ObjectFactory}->($rhObject->{'Type'},$rhObject->{'Data'},$rhObject->{'Id'} ? $this->{$Context}->{$rhObject->{'Id'}} : undef) : DefaultFactory($rhObject->{'Type'},$rhObject->{'Data'},$rhObject->{'Id'} ? $this->{$Context}->{$rhObject->{'Id'}} : undef); | |
259 | |
260 die new Exception("Trying to close a non existing oject") if not $rhObject; | |
261 | |
262 my $Data; | |
263 | |
264 if ($rhObject->{'Id'}) { | |
265 $this->{$Context}->{$rhObject->{'Id'}} = $refObj; | |
266 $Data = $refObj; | |
267 } else { | |
268 if (ref $refObj ne 'SCALAR') { | |
269 $rhObject->{Id} = "auto$AutoId"; | |
270 $AutoId ++; | |
271 $this->{$Context}->{$rhObject->{'Id'}} = $refObj; | |
272 $Data = $refObj; | |
273 } else { | |
274 $Data = ${$refObj}; | |
275 } | |
276 } | |
277 | |
278 if (not $rhPrevObject) { | |
279 $this->{$Root} = $Data; | |
280 } else { | |
281 if ($rhPrevObject->{'Data'}) { | |
282 die new Exception("Trying append a reference to an object to the plain data") if not ref $rhPrevObject->{'Data'}; | |
283 push @{$rhPrevObject->{'Data'}},$rhObject->{'Name'},$Data; | |
284 } else { | |
285 $rhPrevObject->{'Data'} = [$rhObject->{'Name'},$Data]; | |
286 } | |
287 } | |
288 | |
289 $this->{$CurrentObject} = $rhPrevObject; | |
290 | |
291 return 1; | |
292 } | |
293 } | |
294 | |
295 sub _is_class { | |
296 no strict 'refs'; | |
297 scalar keys %{"$_[0]::"} ? 1 : 0; | |
298 } | |
299 | |
300 sub DefaultSurogateHelper { | |
301 my ($Type) = @_; | |
302 | |
303 if ($Type eq 'SCALAR' or $Type eq 'REF') { | |
304 my $var; | |
305 return \$var; | |
306 } elsif ($Type eq 'ARRAY') { | |
307 return []; | |
308 } elsif ($Type eq 'HASH') { | |
309 return {}; | |
310 } else { | |
311 eval "require $Type" unless _is_class($Type); | |
312 if ($Type->UNIVERSAL::can('surrogate')) { | |
313 return $Type->surrogate(); | |
314 } else { | |
315 return bless {}, $Type; | |
316 } | |
317 } | |
318 } | |
319 | |
320 # deserialization context: | |
321 # [ | |
322 # 'var_name',value, | |
323 # .... | |
324 # ] | |
325 | |
326 sub DefaultFactory { | |
327 my ($Type,$Data,$refSurogate) = @_; | |
328 | |
329 if ($Type eq 'SCALAR') { | |
330 die new Exception("SCALAR needs a plain data for a deserialization") if ref $Data; | |
331 if ($refSurogate) { | |
332 $$refSurogate = $Data; | |
333 return $refSurogate; | |
334 } else { | |
335 return \$Data; | |
336 } | |
337 } elsif ($Type eq 'ARRAY') { | |
338 die new Exception("Invalid a deserialization context when deserializing ARRAY") if not ref $Data and defined $Data; | |
339 if (not ref $refSurogate) { | |
340 my @Array; | |
341 $refSurogate = \@Array; | |
342 } | |
343 for (my $i = 0; $i < scalar(@{$Data})/2; $i++) { | |
344 push @$refSurogate,$Data->[$i*2+1]; | |
345 } | |
346 return $refSurogate; | |
347 } elsif ($Type eq 'HASH') { | |
348 die new Exception("Invalid a deserialization context when deserializing HASH") if not ref $Data and defined $Data; | |
349 if (not ref $refSurogate) { | |
350 $refSurogate = {}; | |
351 } | |
352 for (my $i = 0; $i< @$Data; $i+= 2) { | |
353 $refSurogate->{$Data->[$i]} = $Data->[$i+1]; | |
354 } | |
355 return $refSurogate; | |
356 } elsif ($Type eq 'REF') { | |
357 die new Exception("Invalid a deserialization context when deserializing REF") if not ref $Data and defined $Data; | |
358 if (not ref $refSurogate) { | |
359 my $ref = $Data->[1]; | |
360 return \$ref; | |
361 } else { | |
362 $$refSurogate = $Data->[1]; | |
363 return $refSurogate; | |
364 } | |
365 } else { | |
366 eval "require $Type" unless _is_class($Type); | |
367 if ( $Type->UNIVERSAL::can('restore') ) { | |
368 return $Type->restore($Data,$refSurogate); | |
369 } else { | |
370 die new Exception("Don't know how to deserialize $Type"); | |
371 } | |
372 } | |
373 } | |
374 | |
375 package IMPL::Serializer; | |
376 use base qw(IMPL::Object); | |
377 | |
378 use IMPL::Class::Property; | |
379 use IMPL::Class::Property::Direct; | |
380 use IMPL::Exception; | |
381 | |
382 BEGIN { | |
383 private _direct property Formatter => prop_all; | |
384 } | |
385 | |
386 sub CTOR { | |
387 my ($this,%args) = @_; | |
388 $this->Formatter($args{'Formatter'}) or die new Exception("Omitted mandatory parameter 'Formatter'"); | |
389 } | |
390 | |
391 sub Serialize { | |
392 my $this = shift; | |
393 my ($hStream,$Object) = @_; | |
394 my $ObjWriter = $this->Formatter()->CreateWriter($hStream); | |
395 my $Context = new IMPL::Serialization::Context(ObjectWriter => $ObjWriter); | |
396 $Context->AddVar('root',$Object); | |
397 return 1; | |
398 } | |
399 | |
400 sub Deserialize { | |
401 my $this = shift; | |
402 my ($hStream) = @_; | |
403 my $Context = new IMPL::Deserialization::Context(); | |
404 my $ObjReader = $this->Formatter()->CreateReader($hStream,$Context); | |
405 $ObjReader->Parse(); | |
406 return $Context->Root(); | |
407 } | |
408 | |
409 1; |