Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1100)

Side by Side Diff: pkg/serialization/lib/src/serialization_rule.dart

Issue 11293283: Initial version of a serialization framework (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/serialization/lib/src/serialization_helpers.dart ('k') | pkg/serialization/pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of serialization;
6
7 // TODO(alanknight): We should have an example and tests for subclassing
8 // serialization rule rather than using the hard-coded ClosureToMap rule. And
9 // possibly an abstract superclass that's designed to be subclassed that way.
10 /**
11 * The abstract superclass for serialization rules.
12 */
13 abstract class SerializationRule {
14 /**
15 * Rules belong uniquely to a particular Serialization instance, and can
16 * be identified within it by number.
17 */
18 int _number;
19
20 /**
21 * Rules belong uniquely to a particular Serialization instance, and can
22 * be identified within it by number.
23 */
24 int get number => _number;
25
26 /**
27 * Rules belong uniquely to a particular Serialization instance, and can
28 * be identified within it by number.
29 */
30 void set number(x) {
31 if (_number != null) throw
32 new SerializationException("Rule numbers cannot be changed, once set");
33 _number = x;
34 }
35
36 /** Return true if this rule applies to this object, false otherwise. */
37 bool appliesTo(object);
38
39 /**
40 * This extracts the state from the object, calling [f] for each value
41 * as it is extracted, and returning an object representing the whole
42 * state at the end. The state that results will still have direct
43 * pointers to objects, rather than references.
44 */
45 Object extractState(object, void f(value));
46
47 /**
48 * Given the variables representing the state of an object, flatten it
49 * by turning object pointers into Reference objects where needed. This
50 * destructively modifies the state object.
51 *
52 * This has a default implementation which assumes that object is indexable,
53 * so either conforms to Map or List. Subclasses may override to do something
54 * different.
55 */
56 // This has to be a separate operation from extracting, because we extract
57 // as we are traversing the objects, so we don't yet have the objects to
58 // generate references for them. It might be possible to avoid that by
59 // doing a depth-first rather than breadth-first traversal, but I'm not
60 // sure it's worth it.
61 void flatten(state, Writer writer) {
62 keysAndValues(state).forEach((key, value) {
63 var reference = writer._referenceFor(value);
64 state[key] = (reference == null) ? value : reference;
65 });
66 }
67
68 /** Return true if this rule should only be applied when we are the first
69 * rule found that applies to this object. This may or may not be a hack
70 * that will disappear once we have better support for multiple rules.
71 * We want to have multiple different rules that apply to the same object. We
72 * also want to have multiple different rules that might exclusively apply
73 * to the same object. So, we want either ListRule or ListRuleEssential, and
74 * only one of them can be there. But on the other hand, we may want both
75 * ListRule and BasicRule. So we identify the kinds of rules that can share.
76 * If mustBePrimary returns true, then this rule will only be chosen if no
77 * other rule has been found yet. This means that the ordering of rules in
78 * the serialization is significant, which is unpleasant, but we'll have
79 * to see how bad it is.
80 */
81 // TODO(alanknight): Reconsider whether this should be handled differently.
82 get mustBePrimary => false;
83
84 /**
85 * Create the new object corresponding to [state] using the rules
86 * from [reader]. This may involve recursively inflating "essential"
87 * references in the state, which are those that are required for the
88 * object's constructor. It is up to the rule what state is considered
89 * essential.
90 */
91 inflateEssential(state, Reader reader);
92
93 /**
94 * The [object] has already been created. Set any of its non-essential
95 * variables from the representation in [state]. Where there are references
96 * to other objects they are resolved in the context of [reader].
97 */
98 inflateNonEssential(state, object, Reader reader);
99
100 /**
101 * If we have an object [o] as part of our state, should we represent that
102 * directly, or should we make a reference for it. By default we use a
103 * reference for everything.
104 */
105 bool shouldUseReferenceFor(Object o, Writer w) => true;
106
107 /**
108 * This writes the data from our internal representation into a List.
109 * It is used in order to write to a flat format, and is likely to be
110 * folded into a more general mechanism for supporting different output
111 * formats.
112 */
113 // TODO(alanknight): This really shouldn't exist, but is a temporary measure
114 // for writing to a a flat format until that's more fleshed out. It takes
115 // the internal representation of the rule's state, which is particularly
116 // bad. The default implementation treats the ruleData as a List of Lists
117 // of references.
118 void dumpStateInto(List ruleData, List target) {
119 // Needing the intermediate is also bad for performance, but tricky
120 // to do otherwise without a mechanism to precalculate the size.
121 var intermediate = new List();
122 var totalLength = 0;
123 for (var eachList in ruleData) {
124 // TODO(alanknight): Abstract this out better, this really won't scale.
125 if (this is ListRule)
126 intermediate.add(eachList.length);
127 for (var eachRef in eachList) {
128 if (eachRef == null) {
129 intermediate..add(null)..add(null);
130 } else {
131 eachRef.writeToList(intermediate);
132 }
133 }
134 }
135 target.addAll(intermediate);
136 }
137
138 /**
139 * The inverse of dumpStateInto, this reads the rule's state from an
140 * iterator in a flat format.
141 */
142 pullStateFrom(Iterator stream);
143 }
144
145 /**
146 * This rule handles things that implement List. It will recreate them as
147 * whatever the default implemenation of List is on the target platform.
148 */
149 class ListRule extends SerializationRule {
150
151 appliesTo(object) => object is List;
152
153 state(List list) => new List.from(list);
154
155 List extractState(List list, f) {
156 var result = new List();
157 for (var each in list) {
158 result.add(each);
159 f(each);
160 }
161 return result;
162 }
163
164 inflateEssential(List state, Reader r) => new List();
165
166 // For a list, we consider all of its state non-essential and add it
167 // after creation.
168 inflateNonEssential(List state, List newList, Reader r) {
169 populateContents(state, newList, r);
170 }
171
172 void populateContents(List state, List newList, Reader r) {
173 for(var each in state) {
174 newList.add(r.inflateReference(each));
175 }
176 }
177
178 /**
179 * When reading from a flat format we are given [stream] and need to pull as
180 * much data from it as we need. Our format is that we have an integer N
181 * indicating the number of objects and then for each object a length M,
182 * and then M references, where a reference is stored in the stream as two
183 * integers. Or, in the special case of null, two nulls.
184 */
185 pullStateFrom(Iterator stream) {
186 // TODO(alanknight): This is much too close to the basicRule implementation,
187 // and I'd refactor them if I didn't think this whole mechanism needed to
188 // change soon.
189 var dataLength = stream.next();
190 var ruleData = new List();
191 for (var i = 0; i < dataLength; i++) {
192 var subLength = stream.next();
193 var subList = new List();
194 ruleData.add(subList);
195 for (var j = 0; j < subLength; j++) {
196 var a = stream.next();
197 var b = stream.next();
198 if (!(a is int)) {
199 // This wasn't a reference, just use the first object as a literal.
200 // particularly used for the case of null.
201 subList.add(a);
202 } else {
203 subList.add(new Reference(this, a, b));
204 }
205 }
206 }
207 return ruleData;
208 }
209 }
210
211 /**
212 * This is a subclass of ListRule where all of the list's contents are
213 * considered essential state. This is needed if an object X contains a List L,
214 * but it expects L's contents to be fixed when X's constructor is called.
215 */
216 class ListRuleEssential extends ListRule {
217
218 /** Create the new List and also inflate all of its contents. */
219 inflateEssential(List state, Reader r) {
220 var object = super.inflateEssential(state, r);
221 populateContents(state, object, r);
222 return object;
223 }
224
225 /** Does nothing, because all the work has been done in inflateEssential. */
226 inflateNonEssential(state, newList, reader) {}
227
228 bool get mustBePrimary => true;
229 }
230
231 /**
232 * This rule handles primitive types, defined as those that we can normally
233 * represent directly in the output format. We hard-code that to mean
234 * num, String, and bool.
235 */
236 class PrimitiveRule extends SerializationRule {
237 appliesTo(object) {
238 return isPrimitive(object);
239 }
240 extractState(object, Function f) => object;
241 void flatten(object, Writer writer) {}
242 inflateEssential(state, Reader r) => state;
243 inflateNonEssential(object, _, Reader r) {}
244
245 /** Indicate whether we should save pointers to this object as references
246 * or store the object directly. For primitives this depends on the format,
247 * so we delegate to the writer.
248 */
249 bool shouldUseReferenceFor(Object o, Writer w) =>
250 w.shouldUseReferencesForPrimitives;
251
252 /**
253 * This writes the data from our internal representation into a List.
254 * It is used in order to write to a flat format, and is likely to be
255 * folded into a more general mechanism for supporting different output
256 * formats. For primitives, the ruleData is our list of all the
257 * primitives and just add it into the target.
258 */
259 void dumpStateInto(List ruleData, List target) {
260 target.addAll(ruleData);
261 }
262
263 /**
264 * When reading from a flat format we are given [stream] and need to pull as
265 * much data from it as we need. Our format is that we have an integer N
266 * indicating the number of objects and then N simple objects.
267 */
268 pullStateFrom(Iterator stream) {
269 var dataLength = stream.next();
270 var ruleData = new List();
271 for (var i = 0; i < dataLength; i++) {
272 ruleData.add(stream.next());
273 }
274 return ruleData;
275 }
276 }
277
278 /** Helper function for PrimitiveRule to tell which objects it applies to. */
279 bool isPrimitive(Object object) {
280 return object is num || object is String || object is bool;
281 }
282
283 /** Typedef for the object construction closure used in ClosureToMapRule. */
284 typedef Object ConstructType(Map m);
285
286 /** Typedef for the state-getting closure used in ClosureToMapRule. */
287 typedef Map<String, Object> GetStateType(Object o);
288
289 /** Typedef for the state-setting closure used in ClosureToMapRule. */
290 typedef void NonEssentialStateType(Object o, Map m);
291
292 /**
293 * This is a rule where the extraction and creation are hard-coded as
294 * closures. The result is expected to be a map indexed by field name.
295 */
296 class ClosureToMapRule extends SerializationRule {
297
298 /** The runtimeType of objects that this rule applies to. Used in appliesTo.*/
299 final Type type;
300
301 /** The function for constructing new objects when reading. */
302 ConstructType construct;
303
304 /** The function for returning an object's state as a Map. */
305 GetStateType getState;
306
307 /** The function for setting an object's state from a Map. */
308 NonEssentialStateType setNonEssentialState;
309
310 /**
311 * Create a ClosureToMapRule for the given [type] which gets an object's
312 * state by calling [getState], creates a new object by calling [construct]
313 * and sets the new object's state by calling [setNonEssentialState].
314 */
315 ClosureToMapRule(this.type, this.getState, this.construct,
316 this.setNonEssentialState);
317
318 /**
319 * If we deserialize a ClosureToMapRule we can't actually use it, because
320 * we don't have the closures, so generate a stub that just returns the
321 * raw state object.
322 */
323 ClosureToMapRule.stub(this.type) {
324 getState = (x) { throw new SerializationException(
325 'Closures cannot be serialized'); };
326 construct = (state) => state;
327 setNonEssentialState = (object, state) {};
328 }
329
330 bool appliesTo(object) => object.runtimeType == type;
331
332 extractState(object, Function f) {
333 Map state = getState(object);
334 values(state).forEach(f);
335 return state;
336 }
337
338 // TODO(alanknight): We're inflating twice here. How to avoid doing
339 // that without giving the user even more stuff to specify.
340 // Worse than that, by inflating everything in advance, we are are
341 // forcing all the state to be essential.
342 Object inflateEssential(Map<String, Object> state, Reader r) {
343 var inflated = values(state).map((x) => r.inflateReference(x));
344 return construct(inflated);
345 }
346
347 void inflateNonEssential(state, object, Reader r) {
348 if (setNonEssentialState == null) return;
349 var inflated = values(state).map((x) => r.inflateReference(x));
350 setNonEssentialState(inflated, object);
351 }
352 }
353
354 /**
355 * This rule handles things we can't pass directly, but only by reference.
356 * It extracts an identifier we can use to pass them.
357 */
358 class ClassMirrorRule extends SerializationRule {
359 // TODO(alanknight): This probably generalizes to any named object.
360 bool appliesTo(object) {
361 return object is ClassMirror;
362 }
363 extractState(object, Function f) => f(object.simpleName);
364 void flatten(object, Writer writer) {}
365 inflateEssential(state, Reader r) => r.externalObjectNamed(state);
366 inflateNonEssential(state, object, Reader r) {}
367 }
OLDNEW
« no previous file with comments | « pkg/serialization/lib/src/serialization_helpers.dart ('k') | pkg/serialization/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698