| 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 /** | 5 /** |
| 6 * A general-purpose serialization facility for Dart objects. | 6 * A general-purpose serialization facility for Dart objects. |
| 7 * | 7 * |
| 8 * A [Serialization] is defined in terms of [SerializationRule]s and supports | 8 * A [Serialization] is defined in terms of [SerializationRule]s and supports |
| 9 * reading and writing to different formats. | 9 * reading and writing to different formats. |
| 10 * | 10 * |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 * addressToMap, createAddress, fillInAddress); | 102 * addressToMap, createAddress, fillInAddress); |
| 103 * | 103 * |
| 104 * In this case we have created standalone functions rather than | 104 * In this case we have created standalone functions rather than |
| 105 * methods in a subclass and we pass them to the constructor of | 105 * methods in a subclass and we pass them to the constructor of |
| 106 * [ClosureRule]. In this case we've also had them use maps rather than | 106 * [ClosureRule]. In this case we've also had them use maps rather than |
| 107 * lists for the state, but either would work as long as the rule is | 107 * lists for the state, but either would work as long as the rule is |
| 108 * consistent with the representation it uses. We pass it the runtimeType | 108 * consistent with the representation it uses. We pass it the runtimeType |
| 109 * of the object, and functions equivalent to the methods on [CustomRule] | 109 * of the object, and functions equivalent to the methods on [CustomRule] |
| 110 * | 110 * |
| 111 * ## Constant values | 111 * ## Constant values |
| 112 * | 112 * |
| 113 * There are cases where the constructor needs values that we can't easily get | 113 * There are cases where the constructor needs values that we can't easily get |
| 114 * from the serialized object. For example, we may just want to pass null, or a | 114 * from the serialized object. For example, we may just want to pass null, or a |
| 115 * constant value. To support this, we can specify as constructor fields | 115 * constant value. To support this, we can specify as constructor fields |
| 116 * values that aren't field names. If any value isn't a String, or is a string | 116 * values that aren't field names. If any value isn't a String, or is a string |
| 117 * that doesn't correspond to a field name, it will be | 117 * that doesn't correspond to a field name, it will be |
| 118 * treated as a constant and passed unaltered to the constructor. | 118 * treated as a constant and passed unaltered to the constructor. |
| 119 * | 119 * |
| 120 * In some cases a non-constructor field should not be set using field | 120 * In some cases a non-constructor field should not be set using field |
| 121 * access or a setter, but should be done by calling a method. For example, it | 121 * access or a setter, but should be done by calling a method. For example, it |
| 122 * may not be possible to set a List field "foo", and you need to call an | 122 * may not be possible to set a List field "foo", and you need to call an |
| 123 * addFoo() method for each entry in the list. In these cases, if you are using | 123 * addFoo() method for each entry in the list. In these cases, if you are using |
| 124 * a BasicRule for the object you can call the setFieldWith() method. | 124 * a BasicRule for the object you can call the setFieldWith() method. |
| 125 * | 125 * |
| 126 * s..addRuleFor(fooHolderInstance).setFieldWith("foo", | 126 * s..addRuleFor(FooHolder).setFieldWith("foo", |
| 127 * (parent, value) => for (var each in value) parent.addFoo(value)); | 127 * (parent, value) => for (var each in value) parent.addFoo(value)); |
| 128 * | 128 * |
| 129 * ## Writing | 129 * ## Writing |
| 130 * | 130 * |
| 131 * To write objects, we use the write() method. | 131 * To write objects, we use the write() method. |
| 132 * | 132 * |
| 133 * var output = serialization.write(someObject); | 133 * var output = serialization.write(someObject); |
| 134 * | 134 * |
| 135 * By default this uses a representation in which objects are represented as | 135 * By default this uses a representation in which objects are represented as |
| 136 * maps keyed by field name, but in which references between objects have been | 136 * maps keyed by field name, but in which references between objects have been |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 345 * method. | 345 * method. |
| 346 */ | 346 */ |
| 347 void addRule(SerializationRule rule) { | 347 void addRule(SerializationRule rule) { |
| 348 rule.number = _rules.length; | 348 rule.number = _rules.length; |
| 349 _rules.add(rule); | 349 _rules.add(rule); |
| 350 } | 350 } |
| 351 | 351 |
| 352 /** | 352 /** |
| 353 * This writes out an object graph rooted at [object] and returns the result. | 353 * This writes out an object graph rooted at [object] and returns the result. |
| 354 * The [format] parameter determines the form of the result. The default | 354 * The [format] parameter determines the form of the result. The default |
| 355 * format returns a String in [json] format. | 355 * format is [SimpleMapFormat] and returns a Map. |
| 356 */ | 356 */ |
| 357 write(Object object, {Format format}) { | 357 write(Object object, {Format format}) { |
| 358 return newWriter(format).write(object); | 358 return newWriter(format).write(object); |
| 359 } | 359 } |
| 360 | 360 |
| 361 /** | 361 /** |
| 362 * Return a new [Writer] object for this serialization. This is useful if you | 362 * Return a new [Writer] object for this serialization. This is useful if you |
| 363 * want to do something more complex with the writer than just returning | 363 * want to do something more complex with the writer than just returning |
| 364 * the final result. | 364 * the final result. |
| 365 */ | 365 */ |
| 366 Writer newWriter([Format format]) => new Writer(this, format); | 366 Writer newWriter([Format format]) => new Writer(this, format); |
| 367 | 367 |
| 368 /** | 368 /** |
| 369 * Read the serialized data from [input] and return the root object | 369 * Read the serialized data from [input] and return the root object |
| 370 * from the result. The [input] can be of any type that the [Format] | 370 * from the result. The [input] can be of any type that the [Format] |
| 371 * reads/writes, but normally will be a [List], [Map], or a simple type. | 371 * reads/writes, but normally will be a [List], [Map], or a simple type. |
| 372 * The [format] parameter determines the form of the result. The default | 372 * The [format] parameter determines the form of the result. The default |
| 373 * format returns a String in [json] format. | 373 * format is [SimpleMapFormat] and expects a Map as input. |
| 374 * If there are objects that need to be resolved | 374 * If there are objects that need to be resolved |
| 375 * in the current context, they should be provided in [externals] as a | 375 * in the current context, they should be provided in [externals] as a |
| 376 * Map from names to values. In particular, in the current implementation | 376 * Map from names to values. In particular, in the current implementation |
| 377 * any class mirrors needed should be provided in [externals] using the | 377 * any class mirrors needed should be provided in [externals] using the |
| 378 * class name as a key. In addition to the [externals] map provided here, | 378 * class name as a key. In addition to the [externals] map provided here, |
| 379 * values will be looked up in the [namedObjects] map. | 379 * values will be looked up in the [namedObjects] map. |
| 380 */ | 380 */ |
| 381 read(input, {Format format, Map externals: const {}}) { | 381 read(input, {Format format, Map externals: const {}}) { |
| 382 return newReader(format).read(input, externals); | 382 return newReader(format).read(input, externals); |
| 383 } | 383 } |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 440 * If there are new rule classes created, they will need to be described | 440 * If there are new rule classes created, they will need to be described |
| 441 * here. | 441 * here. |
| 442 */ | 442 */ |
| 443 Serialization ruleSerialization() { | 443 Serialization ruleSerialization() { |
| 444 // TODO(alanknight): There's an extensibility issue here with new rules. | 444 // TODO(alanknight): There's an extensibility issue here with new rules. |
| 445 // TODO(alanknight): How to handle rules with closures? They have to | 445 // TODO(alanknight): How to handle rules with closures? They have to |
| 446 // exist on the other side, but we might be able to hook them up by name, | 446 // exist on the other side, but we might be able to hook them up by name, |
| 447 // or we might just be able to validate that they're correctly set up | 447 // or we might just be able to validate that they're correctly set up |
| 448 // on the other side. | 448 // on the other side. |
| 449 | 449 |
| 450 // Make some bogus rule instances so we have something to feed rule creation | |
| 451 // and get their types. If only we had class literals implemented... | |
| 452 var basicRule = new BasicRule(reflect(null).type, '', [], [], []); | |
| 453 | |
| 454 var meta = new Serialization() | 450 var meta = new Serialization() |
| 455 ..selfDescribing = false | 451 ..selfDescribing = false |
| 456 ..addRuleFor(new ListRule()) | 452 ..addRuleFor(ListRule) |
| 457 ..addRuleFor(new MapRule()) | 453 ..addRuleFor(MapRule) |
| 458 ..addRuleFor(new PrimitiveRule()) | 454 ..addRuleFor(PrimitiveRule) |
| 459 ..addRuleFor(new ListRuleEssential()) | 455 ..addRuleFor(ListRuleEssential) |
| 460 ..addRuleFor(basicRule, | 456 ..addRuleFor(BasicRule, |
| 461 constructorFields: ['type', | 457 constructorFields: ['type', |
| 462 'constructorName', | 458 'constructorName', |
| 463 'constructorFields', 'regularFields', []], | 459 'constructorFields', 'regularFields', []], |
| 464 fields: []) | 460 fields: []) |
| 465 ..addRule(new NamedObjectRule()) | 461 ..addRule(new NamedObjectRule()) |
| 466 ..addRule(new MirrorRule()) | 462 ..addRule(new MirrorRule()) |
| 467 ..addRuleFor(new MirrorRule()) | 463 ..addRuleFor(MirrorRule) |
| 468 ..addRuleFor(new SymbolRule()) | 464 ..addRuleFor(SymbolRule) |
| 469 ..addRuleFor(new DateTimeRule()); | 465 ..addRuleFor(DateTimeRule); |
| 470 meta.namedObjects = namedObjects; | 466 meta.namedObjects = namedObjects; |
| 471 return meta; | 467 return meta; |
| 472 } | 468 } |
| 473 | 469 |
| 474 /** Return true if our [namedObjects] collection has an entry for [object].*/ | 470 /** Return true if our [namedObjects] collection has an entry for [object].*/ |
| 475 bool _hasNameFor(object) { | 471 bool _hasNameFor(object) { |
| 476 var sentinel = const _Sentinel(); | 472 var sentinel = const _Sentinel(); |
| 477 return _nameFor(object, () => sentinel) != sentinel; | 473 return _nameFor(object, () => sentinel) != sentinel; |
| 478 } | 474 } |
| 479 | 475 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 490 } | 486 } |
| 491 | 487 |
| 492 /** | 488 /** |
| 493 * An exception class for errors during serialization. | 489 * An exception class for errors during serialization. |
| 494 */ | 490 */ |
| 495 class SerializationException implements Exception { | 491 class SerializationException implements Exception { |
| 496 final String message; | 492 final String message; |
| 497 const SerializationException(this.message); | 493 const SerializationException(this.message); |
| 498 String toString() => "SerializationException($message)"; | 494 String toString() => "SerializationException($message)"; |
| 499 } | 495 } |
| OLD | NEW |