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 27 matching lines...) Expand all Loading... |
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), Writer w); |
49 | 49 |
50 /** | 50 /** |
51 * Allows rules to tell us how they expect to store their state. If this | 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. | 52 * isn't specified we can also just look at the data to tell. |
53 */ | 53 */ |
54 bool get storesStateAsLists => false; | 54 bool get storesStateAsLists => false; |
55 bool get storesStateAsMaps => false; | 55 bool get storesStateAsMaps => false; |
56 bool get storesStateAsPrimitives => false; | 56 bool get storesStateAsPrimitives => false; |
57 | 57 |
58 /** | 58 /** |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 /** | 135 /** |
136 * 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 |
137 * whatever the default implemenation of List is on the target platform. | 137 * whatever the default implemenation of List is on the target platform. |
138 */ | 138 */ |
139 class ListRule extends SerializationRule { | 139 class ListRule extends SerializationRule { |
140 | 140 |
141 appliesTo(object, Writer w) => object is List; | 141 appliesTo(object, Writer w) => object is List; |
142 | 142 |
143 bool get storesStateAsLists => true; | 143 bool get storesStateAsLists => true; |
144 | 144 |
145 List extractState(List list, f) { | 145 List extractState(List list, f, Writer w) { |
146 var result = new List(); | 146 var result = new List(); |
147 for (var each in list) { | 147 for (var each in list) { |
148 result.add(each); | 148 result.add(each); |
149 f(each); | 149 f(each); |
150 } | 150 } |
151 return result; | 151 return result; |
152 } | 152 } |
153 | 153 |
154 inflateEssential(List state, Reader r) => new List(); | 154 inflateEssential(List state, Reader r) => new List(); |
155 | 155 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
193 * whatever the default implemenation of Map is on the target platform. If a | 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, | 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. | 195 * otherwise it will store it as a list of references to keys and values. |
196 */ | 196 */ |
197 class MapRule extends SerializationRule { | 197 class MapRule extends SerializationRule { |
198 | 198 |
199 appliesTo(object, Writer w) => object is Map; | 199 appliesTo(object, Writer w) => object is Map; |
200 | 200 |
201 bool get storesStateAsMaps => true; | 201 bool get storesStateAsMaps => true; |
202 | 202 |
203 extractState(Map map, f) { | 203 extractState(Map map, f, Writer w) { |
204 // Note that we make a copy here because flattening may be destructive. | 204 // Note that we make a copy here because flattening may be destructive. |
205 var newMap = new Map.from(map); | 205 var newMap = new Map.from(map); |
206 newMap.forEach((key, value) { | 206 newMap.forEach((key, value) { |
207 f(key); | 207 f(key); |
208 f(value); | 208 f(value); |
209 }); | 209 }); |
210 return newMap; | 210 return newMap; |
211 } | 211 } |
212 | 212 |
213 /** | 213 /** |
214 * Change the keys and values of [state] into references in [writer]. | 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 | 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 | 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 and replace the | 217 * arbitrary then we need to turn them into references and replace the |
218 * state with a new Map whose keys are the references. | 218 * state with a new Map whose keys are the references. |
219 */ | 219 */ |
220 flatten(Map state, Writer writer) { | 220 flatten(Map state, Writer writer) { |
221 bool keysAreAllStrings = state.keys.every((x) => x is String); | 221 bool keysAreAllStrings = state.keys.every((x) => x is String); |
222 if (keysAreAllStrings) { | 222 if (keysAreAllStrings && !writer.shouldUseReferencesForPrimitives) { |
223 keysAndValues(state).forEach( | 223 keysAndValues(state).forEach( |
224 (key, value) => state[key] = writer._referenceFor(value)); | 224 (key, value) => state[key] = writer._referenceFor(value)); |
225 return state; | 225 return state; |
226 } else { | 226 } else { |
227 var newState = []; | 227 var newState = []; |
228 keysAndValues(state).forEach((key, value) { | 228 keysAndValues(state).forEach((key, value) { |
229 newState.add(writer._referenceFor(key)); | 229 newState.add(writer._referenceFor(key)); |
230 newState.add(writer._referenceFor(value)); | 230 newState.add(writer._referenceFor(value)); |
231 }); | 231 }); |
232 return newState; | 232 return newState; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 | 268 |
269 /** | 269 /** |
270 * This rule handles primitive types, defined as those that we can normally | 270 * This rule handles primitive types, defined as those that we can normally |
271 * represent directly in the output format. We hard-code that to mean | 271 * represent directly in the output format. We hard-code that to mean |
272 * num, String, and bool. | 272 * num, String, and bool. |
273 */ | 273 */ |
274 class PrimitiveRule extends SerializationRule { | 274 class PrimitiveRule extends SerializationRule { |
275 appliesTo(object, Writer w) { | 275 appliesTo(object, Writer w) { |
276 return isPrimitive(object); | 276 return isPrimitive(object); |
277 } | 277 } |
278 extractState(object, Function f) => object; | 278 extractState(object, Function f, Writer w) => object; |
279 flatten(object, Writer writer) {} | 279 flatten(object, Writer writer) {} |
280 inflateEssential(state, Reader r) => state; | 280 inflateEssential(state, Reader r) => state; |
281 inflateNonEssential(object, _, Reader r) {} | 281 inflateNonEssential(object, _, Reader r) {} |
282 | 282 |
283 bool get storesStateAsPrimitives => true; | 283 bool get storesStateAsPrimitives => true; |
284 | 284 |
285 /** | 285 /** |
286 * Indicate whether we should save pointers to this object as references | 286 * Indicate whether we should save pointers to this object as references |
287 * or store the object directly. For primitives this depends on the format, | 287 * or store the object directly. For primitives this depends on the format, |
288 * so we delegate to the writer. | 288 * so we delegate to the writer. |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 class NamedObjectRule extends SerializationRule { | 348 class NamedObjectRule extends SerializationRule { |
349 /** | 349 /** |
350 * Return true if this rule applies to the object. Checked by looking up | 350 * Return true if this rule applies to the object. Checked by looking up |
351 * in the namedObjects collection. | 351 * in the namedObjects collection. |
352 */ | 352 */ |
353 bool appliesTo(object, Writer writer) { | 353 bool appliesTo(object, Writer writer) { |
354 return writer.hasNameFor(object); | 354 return writer.hasNameFor(object); |
355 } | 355 } |
356 | 356 |
357 /** Extract the state of the named objects as just the object itself. */ | 357 /** Extract the state of the named objects as just the object itself. */ |
358 extractState(object, Function f) => [object]; | 358 extractState(object, Function f, Writer writer) { |
| 359 var result = [nameFor(object, writer)]; |
| 360 f(result.first); |
| 361 return result; |
| 362 } |
359 | 363 |
360 /** When we flatten the state we save it as the name. */ | 364 /** When we flatten the state we save it as the name. */ |
361 // TODO(alanknight): This seems questionable. In a truly flat format we may | 365 // TODO(alanknight): This seems questionable. In a truly flat format we may |
362 // want to have extracted the name as a string first and flatten it into a | 366 // want to have extracted the name as a string first and flatten it into a |
363 // reference to that. But that requires adding the Writer as a parameter to | 367 // reference to that. But that requires adding the Writer as a parameter to |
364 // extractState, and I'm reluctant to add yet another parameter until | 368 // extractState, and I'm reluctant to add yet another parameter until |
365 // proven necessary. | 369 // proven necessary. |
366 flatten(state, Writer writer) { | 370 flatten(state, Writer writer) { |
367 state[0] = nameFor(state.first, writer); | 371 state[0] = writer._referenceFor(state[0]); |
368 } | 372 } |
369 | 373 |
370 /** Look up the named object and return it. */ | 374 /** Look up the named object and return it. */ |
371 inflateEssential(state, Reader r) => r.objectNamed(state.first); | 375 inflateEssential(state, Reader r) => |
| 376 r.objectNamed(r.resolveReference(state.first)); |
372 | 377 |
373 /** Set any non-essential state on the object. For this rule, a no-op. */ | 378 /** Set any non-essential state on the object. For this rule, a no-op. */ |
374 inflateNonEssential(state, object, Reader r) {} | 379 inflateNonEssential(state, object, Reader r) {} |
375 | 380 |
376 /** Return the name for this object in the Writer. */ | 381 /** Return the name for this object in the Writer. */ |
377 nameFor(object, Writer writer) => writer.nameFor(object); | 382 nameFor(object, Writer writer) => writer.nameFor(object); |
378 } | 383 } |
379 | 384 |
380 /** | 385 /** |
381 * This rule handles the special case of Mirrors, restricted to those that | 386 * This rule handles the special case of Mirrors. It stores the mirror by its |
382 * have a simpleName. It knows that it applies to any such mirror and | 387 * qualifiedName and attempts to look it up in both the namedObjects |
383 * automatically uses its simpleName as the key into the namedObjects. | 388 * collection, or if it's not found there, by looking it up in the mirror |
384 * When reading, the user is still responsible for adding the appropriate | 389 * system. When reading, the user is responsible for supplying the appropriate |
385 * mirrors to namedObject. | 390 * values in [namedObjects] or in the [externals] paramter to |
| 391 * [Serialization.read]. |
386 */ | 392 */ |
387 class MirrorRule extends NamedObjectRule { | 393 class MirrorRule extends NamedObjectRule { |
388 bool appliesTo(object, Writer writer) => object is DeclarationMirror; | 394 bool appliesTo(object, Writer writer) => object is DeclarationMirror; |
389 nameFor(DeclarationMirror object, Writer writer) => object.simpleName; | 395 nameFor(DeclarationMirror object, Writer writer) => object.qualifiedName; |
| 396 |
| 397 inflateEssential(state, Reader r) { |
| 398 var qualifiedName = r.resolveReference(state.first); |
| 399 var lookupFull = r.objectNamed(qualifiedName, (x) => null); |
| 400 if (lookupFull != null) return lookupFull; |
| 401 var lib = qualifiedName.substring(0, qualifiedName.indexOf(".")); |
| 402 var type = qualifiedName.substring(qualifiedName.indexOf(".") + 1); |
| 403 var lookup = r.objectNamed(type, (x) => null); |
| 404 if (lookup != null) return lookup; |
| 405 var libMirror = currentMirrorSystem().libraries[lib]; |
| 406 return libMirror.classes[type]; |
| 407 } |
390 } | 408 } |
391 | 409 |
392 /** | 410 /** |
393 * This provides an abstract superclass for writing your own rules specific to | 411 * This provides an abstract superclass for writing your own rules specific to |
394 * a class. It makes some assumptions about behaviour, and so can have a | 412 * a class. It makes some assumptions about behaviour, and so can have a |
395 * simpler set of methods that need to be implemented in order to subclass it. | 413 * simpler set of methods that need to be implemented in order to subclass it. |
396 * | 414 * |
397 */ | 415 */ |
398 abstract class CustomRule extends SerializationRule { | 416 abstract class CustomRule extends SerializationRule { |
399 // TODO(alanknight): It would be nice if we could provide an implementation | 417 // TODO(alanknight): It would be nice if we could provide an implementation |
(...skipping 23 matching lines...) Expand all Loading... |
423 */ | 441 */ |
424 create(List state); | 442 create(List state); |
425 | 443 |
426 /** | 444 /** |
427 * Set any state in [object] which wasn't set in the constructor. Between | 445 * Set any state in [object] which wasn't set in the constructor. Between |
428 * this method and [create] all of the information in [state] should be set | 446 * this method and [create] all of the information in [state] should be set |
429 * in the new object. | 447 * in the new object. |
430 */ | 448 */ |
431 void setState(object, List state); | 449 void setState(object, List state); |
432 | 450 |
433 extractState(instance, Function f) { | 451 extractState(instance, Function f, Writer w) { |
434 var state = getState(instance); | 452 var state = getState(instance); |
435 for (var each in values(state)) { | 453 for (var each in values(state)) { |
436 f(each); | 454 f(each); |
437 } | 455 } |
438 return state; | 456 return state; |
439 } | 457 } |
440 | 458 |
441 inflateEssential(state, Reader r) => create(_lazy(state, r)); | 459 inflateEssential(state, Reader r) => create(_lazy(state, r)); |
442 | 460 |
443 void inflateNonEssential(state, object, Reader r) => | 461 void inflateNonEssential(state, object, Reader r) { |
444 setState(object, _lazy(state, r)); | 462 setState(object, _lazy(state, r)); |
| 463 } |
445 | 464 |
446 // We don't want to have to make the end user tell us how long the list is | 465 // We don't want to have to make the end user tell us how long the list is |
447 // separately, so write it out for each object, even though they're all | 466 // separately, so write it out for each object, even though they're all |
448 // expected to be the same length. | 467 // expected to be the same length. |
449 get hasVariableLengthEntries => true; | 468 get hasVariableLengthEntries => true; |
450 } | 469 } |
451 | 470 |
452 /** Create a lazy list/map that will inflate its items on demand in [r]. */ | 471 /** Create a lazy list/map that will inflate its items on demand in [r]. */ |
453 _lazy(l, Reader r) { | 472 _lazy(l, Reader r) { |
454 if (l is List) return new _LazyList(l, r); | 473 if (l is List) return new _LazyList(l, r); |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
540 retainAll(x) => _throw(); | 559 retainAll(x) => _throw(); |
541 removeMatching(x) => _throw(); | 560 removeMatching(x) => _throw(); |
542 retainMatching(x) => _throw(); | 561 retainMatching(x) => _throw(); |
543 getRange(x, y) => _throw(); | 562 getRange(x, y) => _throw(); |
544 setRange(x, y, z, [a]) => _throw(); | 563 setRange(x, y, z, [a]) => _throw(); |
545 removeRange(x, y) => _throw(); | 564 removeRange(x, y) => _throw(); |
546 insertRange(x, y, [z]) => _throw(); | 565 insertRange(x, y, [z]) => _throw(); |
547 get reversed => _throw(); | 566 get reversed => _throw(); |
548 void set length(x) => _throw(); | 567 void set length(x) => _throw(); |
549 } | 568 } |
OLD | NEW |