OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of serialization; | 5 part of serialization; |
6 | 6 |
7 // TODO(alanknight): We should have an example and tests for subclassing | 7 // TODO(alanknight): We should have an example and tests for subclassing |
8 // serialization rule rather than using the hard-coded ClosureToMap rule. And | 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. | 9 // possibly an abstract superclass that's designed to be subclassed that way. |
10 /** | 10 /** |
(...skipping 10 matching lines...) Expand all Loading... | |
21 * Rules belong uniquely to a particular Serialization instance, and can | 21 * Rules belong uniquely to a particular Serialization instance, and can |
22 * be identified within it by number. | 22 * be identified within it by number. |
23 */ | 23 */ |
24 int get number => _number; | 24 int get number => _number; |
25 | 25 |
26 /** | 26 /** |
27 * Rules belong uniquely to a particular Serialization instance, and can | 27 * Rules belong uniquely to a particular Serialization instance, and can |
28 * be identified within it by number. | 28 * be identified within it by number. |
29 */ | 29 */ |
30 void set number(x) { | 30 void set number(x) { |
31 if (_number != null) throw | 31 if (_number != null && _number != x) throw |
32 new SerializationException("Rule numbers cannot be changed, once set"); | 32 new SerializationException("Rule numbers cannot be changed, once set"); |
33 _number = x; | 33 _number = x; |
34 } | 34 } |
35 | 35 |
36 /** | 36 /** |
37 * Return true if this rule applies to this object, in the context | 37 * Return true if this rule applies to this object, in the context |
38 * where we're writing it, false otherwise. | 38 * where we're writing it, false otherwise. |
39 */ | 39 */ |
40 bool appliesTo(object, Writer writer); | 40 bool appliesTo(object, Writer writer); |
41 | 41 |
42 /** | 42 /** |
43 * This extracts the state from the object, calling [f] for each value | 43 * This extracts the state from the object, calling [f] for each value |
44 * as it is extracted, and returning an object representing the whole | 44 * as it is extracted, and returning an object representing the whole |
45 * state at the end. The state that results will still have direct | 45 * state at the end. The state that results will still have direct |
46 * pointers to objects, rather than references. | 46 * pointers to objects, rather than references. |
47 */ | 47 */ |
48 extractState(object, void f(value)); | 48 extractState(object, void f(value)); |
49 | 49 |
50 /** | 50 /** |
51 * Allows rules to tell us how they expect to store their state. If this | |
52 * isn't specified we can also just look at the data to tell. | |
53 */ | |
54 bool get storesStateAsLists => false; | |
55 bool get storesStateAsMaps => false; | |
56 bool get storesStateAsPrimitives => false; | |
57 | |
58 /** | |
51 * Given the variables representing the state of an object, flatten it | 59 * Given the variables representing the state of an object, flatten it |
52 * by turning object pointers into Reference objects where needed. This | 60 * by turning object pointers into Reference objects where needed. This |
53 * destructively modifies the state object. | 61 * destructively modifies the state object. |
54 * | 62 * |
55 * This has a default implementation which assumes that object is indexable, | 63 * This has a default implementation which assumes that object is indexable, |
56 * so either conforms to Map or List. Subclasses may override to do something | 64 * so either conforms to Map or List. Subclasses may override to do something |
57 * different. | 65 * different, including returning a new state object to be used in place |
66 * of the original. | |
58 */ | 67 */ |
59 // This has to be a separate operation from extracting, because we extract | 68 // This has to be a separate operation from extracting, because we extract |
60 // as we are traversing the objects, so we don't yet have the objects to | 69 // as we are traversing the objects, so we don't yet have the objects to |
61 // generate references for them. It might be possible to avoid that by | 70 // generate references for them. It might be possible to avoid that by |
62 // doing a depth-first rather than breadth-first traversal, but I'm not | 71 // doing a depth-first rather than breadth-first traversal, but I'm not |
63 // sure it's worth it. | 72 // sure it's worth it. |
64 void flatten(state, Writer writer) { | 73 flatten(state, Writer writer) { |
65 keysAndValues(state).forEach((key, value) { | 74 keysAndValues(state).forEach((key, value) { |
66 var reference = writer._referenceFor(value); | 75 var reference = writer._referenceFor(value); |
67 state[key] = (reference == null) ? value : reference; | 76 state[key] = reference; |
68 }); | 77 }); |
69 } | 78 } |
70 | 79 |
71 /** Return true if this rule should only be applied when we are the first | 80 /** Return true if this rule should only be applied when we are the first |
72 * rule found that applies to this object. This may or may not be a hack | 81 * rule found that applies to this object. This may or may not be a hack |
73 * that will disappear once we have better support for multiple rules. | 82 * that will disappear once we have better support for multiple rules. |
74 * We want to have multiple different rules that apply to the same object. We | 83 * We want to have multiple different rules that apply to the same object. We |
75 * also want to have multiple different rules that might exclusively apply | 84 * also want to have multiple different rules that might exclusively apply |
76 * to the same object. So, we want either ListRule or ListRuleEssential, and | 85 * to the same object. So, we want either ListRule or ListRuleEssential, and |
77 * only one of them can be there. But on the other hand, we may want both | 86 * only one of them can be there. But on the other hand, we may want both |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
124 } | 133 } |
125 | 134 |
126 /** | 135 /** |
127 * This rule handles things that implement List. It will recreate them as | 136 * This rule handles things that implement List. It will recreate them as |
128 * whatever the default implemenation of List is on the target platform. | 137 * whatever the default implemenation of List is on the target platform. |
129 */ | 138 */ |
130 class ListRule extends SerializationRule { | 139 class ListRule extends SerializationRule { |
131 | 140 |
132 appliesTo(object, Writer w) => object is List; | 141 appliesTo(object, Writer w) => object is List; |
133 | 142 |
134 state(List list) => new List.from(list); | 143 bool get storesStateAsLists => true; |
135 | 144 |
136 List extractState(List list, f) { | 145 List extractState(List list, f) { |
137 var result = new List(); | 146 var result = new List(); |
138 for (var each in list) { | 147 for (var each in list) { |
139 result.add(each); | 148 result.add(each); |
140 f(each); | 149 f(each); |
141 } | 150 } |
142 return result; | 151 return result; |
143 } | 152 } |
144 | 153 |
(...skipping 28 matching lines...) Expand all Loading... | |
173 return object; | 182 return object; |
174 } | 183 } |
175 | 184 |
176 /** Does nothing, because all the work has been done in inflateEssential. */ | 185 /** Does nothing, because all the work has been done in inflateEssential. */ |
177 inflateNonEssential(state, newList, reader) {} | 186 inflateNonEssential(state, newList, reader) {} |
178 | 187 |
179 bool get mustBePrimary => true; | 188 bool get mustBePrimary => true; |
180 } | 189 } |
181 | 190 |
182 /** | 191 /** |
192 * This rule handles things that implement Map. It will recreate them as | |
193 * whatever the default implemenation of Map is on the target platform. If a | |
194 * map has string keys it will attempt to retain it as a map for JSON formats, | |
195 * otherwise it will store it as a list of references to keys and values. | |
196 */ | |
197 class MapRule extends SerializationRule { | |
198 | |
199 appliesTo(object, Writer w) => object is Map; | |
200 | |
201 bool get storesStateAsMaps => true; | |
202 | |
203 extractState(Map map, f) { | |
204 // Note that we make a copy here because flattening may be destructive. | |
205 var newMap = new Map.from(map); | |
206 newMap.forEach((key, value) { | |
207 f(key); | |
208 f(value); | |
209 }); | |
210 return newMap; | |
211 } | |
212 | |
213 /** | |
214 * Change the keys and values of [state] into references in [writer]. | |
215 * If [state] is a map whose keys are all strings then we leave the keys | |
216 * as is so that JSON formats will be more readable. If the keys are | |
217 * arbitrary then we need to turn them into references. | |
218 */ | |
219 flatten(Map state, Writer writer) { | |
220 bool keysAreAllStrings = state.keys.every((x) => x is String); | |
Jennifer Messerly
2013/01/25 01:19:59
out of curiousity, why do we check all keys? Could
Alan Knight
2013/01/26 01:33:38
I don't think that would work, at least not with t
| |
221 if (keysAreAllStrings) { | |
222 keysAndValues(state).forEach( | |
Jennifer Messerly
2013/01/25 01:19:59
I would have expected a return in both branches. C
Alan Knight
2013/01/26 01:33:38
Sure. The normal case for flatten() is that it des
| |
223 (key, value) => state[key] = writer._referenceFor(value)); | |
224 } else { | |
225 var newState = []; | |
226 keysAndValues(state).forEach((key, value) { | |
227 newState.add(writer._referenceFor(key)); | |
228 newState.add(writer._referenceFor(value)); | |
229 }); | |
230 return newState; | |
231 } | |
232 } | |
233 | |
234 inflateEssential(state, Reader r) => new Map(); | |
235 | |
236 // For a map, we consider all of its state non-essential and add it | |
237 // after creation. | |
238 inflateNonEssential(state, Map newMap, Reader r) { | |
239 if (state is List) { | |
240 inflateNonEssentialFromList(state, newMap, r); | |
241 } else { | |
242 inflateNonEssentialFromMap(state, newMap, r); | |
243 } | |
244 } | |
245 | |
246 void inflateNonEssentialFromList(List state, Map newMap, Reader r) { | |
247 var key; | |
248 for (var each in state) { | |
249 if (key == null) { | |
250 key = each; | |
251 } else { | |
252 newMap[r.inflateReference(key)] = r.inflateReference(each); | |
253 key = null; | |
254 } | |
255 } | |
256 } | |
257 | |
258 void inflateNonEssentialFromMap(Map state, Map newMap, Reader r) { | |
259 state.forEach((key, value) { | |
260 newMap[r.inflateReference(key)] = r.inflateReference(value); | |
261 }); | |
262 } | |
263 | |
264 bool get hasVariableLengthEntries => true; | |
265 } | |
266 | |
267 /** | |
183 * This rule handles primitive types, defined as those that we can normally | 268 * This rule handles primitive types, defined as those that we can normally |
184 * represent directly in the output format. We hard-code that to mean | 269 * represent directly in the output format. We hard-code that to mean |
185 * num, String, and bool. | 270 * num, String, and bool. |
186 */ | 271 */ |
187 class PrimitiveRule extends SerializationRule { | 272 class PrimitiveRule extends SerializationRule { |
188 appliesTo(object, Writer w) { | 273 appliesTo(object, Writer w) { |
189 return isPrimitive(object); | 274 return isPrimitive(object); |
190 } | 275 } |
191 extractState(object, Function f) => object; | 276 extractState(object, Function f) => object; |
192 void flatten(object, Writer writer) {} | 277 flatten(object, Writer writer) {} |
193 inflateEssential(state, Reader r) => state; | 278 inflateEssential(state, Reader r) => state; |
194 inflateNonEssential(object, _, Reader r) {} | 279 inflateNonEssential(object, _, Reader r) {} |
195 | 280 |
281 bool get storesStateAsPrimitives => true; | |
282 | |
196 /** | 283 /** |
197 * Indicate whether we should save pointers to this object as references | 284 * Indicate whether we should save pointers to this object as references |
198 * or store the object directly. For primitives this depends on the format, | 285 * or store the object directly. For primitives this depends on the format, |
199 * so we delegate to the writer. | 286 * so we delegate to the writer. |
200 */ | 287 */ |
201 bool shouldUseReferenceFor(object, Writer w) => | 288 bool shouldUseReferenceFor(object, Writer w) => |
202 w.shouldUseReferencesForPrimitives; | 289 w.shouldUseReferencesForPrimitives; |
203 | 290 |
204 bool get hasVariableLengthEntries => false; | 291 bool get hasVariableLengthEntries => false; |
205 } | 292 } |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
267 | 354 |
268 /** Extract the state of the named objects as just the object itself. */ | 355 /** Extract the state of the named objects as just the object itself. */ |
269 extractState(object, Function f) => [object]; | 356 extractState(object, Function f) => [object]; |
270 | 357 |
271 /** When we flatten the state we save it as the name. */ | 358 /** When we flatten the state we save it as the name. */ |
272 // TODO(alanknight): This seems questionable. In a truly flat format we may | 359 // TODO(alanknight): This seems questionable. In a truly flat format we may |
273 // want to have extracted the name as a string first and flatten it into a | 360 // want to have extracted the name as a string first and flatten it into a |
274 // reference to that. But that requires adding the Writer as a parameter to | 361 // reference to that. But that requires adding the Writer as a parameter to |
275 // extractState, and I'm reluctant to add yet another parameter until | 362 // extractState, and I'm reluctant to add yet another parameter until |
276 // proven necessary. | 363 // proven necessary. |
277 void flatten(state, Writer writer) { | 364 flatten(state, Writer writer) { |
278 state[0] = nameFor(state.first, writer); | 365 state[0] = nameFor(state.first, writer); |
279 } | 366 } |
280 | 367 |
281 /** Look up the named object and return it. */ | 368 /** Look up the named object and return it. */ |
282 inflateEssential(state, Reader r) => r.objectNamed(state.first); | 369 inflateEssential(state, Reader r) => r.objectNamed(state.first); |
283 | 370 |
284 /** Set any non-essential state on the object. For this rule, a no-op. */ | 371 /** Set any non-essential state on the object. For this rule, a no-op. */ |
285 inflateNonEssential(state, object, Reader r) {} | 372 inflateNonEssential(state, object, Reader r) {} |
286 | 373 |
287 /** Return the name for this object in the Writer. */ | 374 /** Return the name for this object in the Writer. */ |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
438 _throw() { | 525 _throw() { |
439 throw new UnsupportedError("Not modifiable"); | 526 throw new UnsupportedError("Not modifiable"); |
440 } | 527 } |
441 operator []=(x, y) => _throw(); | 528 operator []=(x, y) => _throw(); |
442 add(x) => _throw(); | 529 add(x) => _throw(); |
443 addLast(x) => _throw(); | 530 addLast(x) => _throw(); |
444 addAll(x) => _throw(); | 531 addAll(x) => _throw(); |
445 sort([f]) => _throw(); | 532 sort([f]) => _throw(); |
446 clear() => _throw(); | 533 clear() => _throw(); |
447 removeAt(x) => _throw(); | 534 removeAt(x) => _throw(); |
535 remove(x) => _throw(); | |
448 removeLast() => _throw(); | 536 removeLast() => _throw(); |
449 remove(x) => _throw(); | |
450 removeAll(x) => _throw(); | 537 removeAll(x) => _throw(); |
451 retainAll(x) => _throw(); | 538 retainAll(x) => _throw(); |
452 removeMatching(x) => _throw(); | 539 removeMatching(x) => _throw(); |
453 retainMatching(x) => _throw(); | 540 retainMatching(x) => _throw(); |
454 getRange(x, y) => _throw(); | 541 getRange(x, y) => _throw(); |
455 setRange(x, y, z, [a]) => _throw(); | 542 setRange(x, y, z, [a]) => _throw(); |
456 removeRange(x, y) => _throw(); | 543 removeRange(x, y) => _throw(); |
457 insertRange(x, y, [z]) => _throw(); | 544 insertRange(x, y, [z]) => _throw(); |
545 get reversed => _throw(); | |
458 void set length(x) => _throw(); | 546 void set length(x) => _throw(); |
459 List get reversed => _throw(); | |
460 } | 547 } |
OLD | NEW |