| 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 * This provides a general-purpose serialization facility for Dart objects. A | 6 * This provides a general-purpose serialization facility for Dart objects. A |
| 7 * [Serialization] is defined in terms of [SerializationRule]s and supports | 7 * [Serialization] is defined in terms of [SerializationRule]s and supports |
| 8 * reading and writing to different formats. | 8 * reading and writing to different formats. |
| 9 * | 9 * |
| 10 * ## Installing ## | 10 * ## Installing ## |
| (...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 251 | 251 |
| 252 /** | 252 /** |
| 253 * When we write out data using this serialization, should we also write | 253 * When we write out data using this serialization, should we also write |
| 254 * out a description of the rules. This is on by default unless using | 254 * out a description of the rules. This is on by default unless using |
| 255 * CustomRule subclasses, in which case it requires additional setup and | 255 * CustomRule subclasses, in which case it requires additional setup and |
| 256 * is off by default. | 256 * is off by default. |
| 257 */ | 257 */ |
| 258 bool get selfDescribing { | 258 bool get selfDescribing { |
| 259 // TODO(alanknight): Should this be moved to the format? | 259 // TODO(alanknight): Should this be moved to the format? |
| 260 // TODO(alanknight): Allow self-describing in the presence of CustomRule. | 260 // TODO(alanknight): Allow self-describing in the presence of CustomRule. |
| 261 // TODO(alanknight): Don't do duplicate work creating a writer and |
| 262 // serialization here and then re-creating when we actually serialize. |
| 261 if (_selfDescribing != null) return _selfDescribing; | 263 if (_selfDescribing != null) return _selfDescribing; |
| 262 _selfDescribing = !rules.any((x) => x is CustomRule && x is! SymbolRule); | 264 var meta = ruleSerialization(); |
| 265 var w = meta.newWriter(); |
| 266 _selfDescribing = !rules.any((rule) => |
| 267 meta.rulesFor(rule, w, create: false).isEmpty); |
| 263 return _selfDescribing; | 268 return _selfDescribing; |
| 264 } | 269 } |
| 265 | 270 |
| 266 /** | 271 /** |
| 267 * When we write out data using this serialization, should we also write | 272 * When we write out data using this serialization, should we also write |
| 268 * out a description of the rules. This is on by default unless using | 273 * out a description of the rules. This is on by default unless using |
| 269 * CustomRule subclasses, in which case it requires additional setup and | 274 * CustomRule subclasses, in which case it requires additional setup and |
| 270 * is off by default. | 275 * is off by default. |
| 271 */ | 276 */ |
| 272 void set selfDescribing(bool value) { | 277 void set selfDescribing(bool value) { |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 331 | 336 |
| 332 /** Set up the default rules, for lists and primitives. */ | 337 /** Set up the default rules, for lists and primitives. */ |
| 333 void addDefaultRules() { | 338 void addDefaultRules() { |
| 334 addRule(new PrimitiveRule()); | 339 addRule(new PrimitiveRule()); |
| 335 addRule(new ListRule()); | 340 addRule(new ListRule()); |
| 336 // Both these rules apply to lists, so unless otherwise indicated, | 341 // Both these rules apply to lists, so unless otherwise indicated, |
| 337 // it will always find the first one. | 342 // it will always find the first one. |
| 338 addRule(new ListRuleEssential()); | 343 addRule(new ListRuleEssential()); |
| 339 addRule(new MapRule()); | 344 addRule(new MapRule()); |
| 340 addRule(new SymbolRule()); | 345 addRule(new SymbolRule()); |
| 346 addRule(new DateTimeRule()); |
| 341 } | 347 } |
| 342 | 348 |
| 343 /** | 349 /** |
| 344 * Add a new SerializationRule [rule]. The addRuleFor method will probably | 350 * Add a new SerializationRule [rule]. The addRuleFor method will probably |
| 345 * handle most simple cases, but for adding an arbitrary rule, including | 351 * handle most simple cases, but for adding an arbitrary rule, including |
| 346 * a SerializationRule subclass which you have created, you can use this | 352 * a SerializationRule subclass which you have created, you can use this |
| 347 * method. | 353 * method. |
| 348 */ | 354 */ |
| 349 void addRule(SerializationRule rule) { | 355 void addRule(SerializationRule rule) { |
| 350 rule.number = rules.length; | 356 rule.number = rules.length; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 387 * Return a new [Reader] object for this serialization. This is useful if | 393 * Return a new [Reader] object for this serialization. This is useful if |
| 388 * you want to do something more complex with the reader than just returning | 394 * you want to do something more complex with the reader than just returning |
| 389 * the final result. | 395 * the final result. |
| 390 */ | 396 */ |
| 391 Reader newReader([Format format]) => new Reader(this, format); | 397 Reader newReader([Format format]) => new Reader(this, format); |
| 392 | 398 |
| 393 /** | 399 /** |
| 394 * Return the list of SerializationRule that apply to [object]. For | 400 * Return the list of SerializationRule that apply to [object]. For |
| 395 * internal use, but public because it's used in testing. | 401 * internal use, but public because it's used in testing. |
| 396 */ | 402 */ |
| 397 Iterable<SerializationRule> rulesFor(object, Writer w) { | 403 Iterable<SerializationRule> rulesFor(object, Writer w, {create : true}) { |
| 398 // This has a couple of edge cases. | 404 // This has a couple of edge cases. |
| 399 // 1) The owning object may have indicated we should use a different | 405 // 1) The owning object may have indicated we should use a different |
| 400 // rule than the default. | 406 // rule than the default. |
| 401 // 2) We may not have a rule, in which case we lazily create a BasicRule. | 407 // 2) We may not have a rule, in which case we lazily create a BasicRule. |
| 402 // 3) Rules are allowed to say mustBePrimary, meaning that they can be used | 408 // 3) Rules are allowed to say mustBePrimary, meaning that they can be used |
| 403 // iff no other rule was chosen first. | 409 // iff no other rule was chosen first. |
| 404 // TODO(alanknight): Can the mustBePrimary mechanism be removed or changed. | 410 // TODO(alanknight): Can the mustBePrimary mechanism be removed or changed. |
| 405 // It adds an order dependency to the rules, and is messy. Reconsider in the | 411 // It adds an order dependency to the rules, and is messy. Reconsider in the |
| 406 // light of a more general mechanism for multiple rules per object. | 412 // light of a more general mechanism for multiple rules per object. |
| 407 // TODO(alanknight): Finding which rules apply seems likely to be a | 413 // TODO(alanknight): Finding which rules apply seems likely to be a |
| 408 // bottleneck, particularly with the current reflective implementation. | 414 // bottleneck, particularly with the current reflective implementation. |
| 409 // Consider how to improve it. e.g. cache the list of rules by class. But | 415 // Consider how to improve it. e.g. cache the list of rules by class. But |
| 410 // be careful of issues like rules which have arbitrary predicates. Or | 416 // be careful of issues like rules which have arbitrary predicates. Or |
| 411 // consider having the arbitrary predicates be secondary to an initial | 417 // consider having the arbitrary predicates be secondary to an initial |
| 412 // class-based lookup mechanism. | 418 // class-based lookup mechanism. |
| 413 var target, candidateRules; | 419 var target, candidateRules; |
| 414 if (object is DesignatedRuleForObject) { | 420 if (object is DesignatedRuleForObject) { |
| 415 target = object.target; | 421 target = object.target; |
| 416 candidateRules = object.possibleRules(rules); | 422 candidateRules = object.possibleRules(rules); |
| 417 } else { | 423 } else { |
| 418 target = object; | 424 target = object; |
| 419 candidateRules = rules; | 425 candidateRules = rules; |
| 420 } | 426 } |
| 421 Iterable applicable = candidateRules.where( | 427 Iterable applicable = candidateRules.where( |
| 422 (each) => each.appliesTo(target, w)); | 428 (each) => each.appliesTo(target, w)); |
| 423 | 429 |
| 424 if (applicable.isEmpty) { | 430 if (applicable.isEmpty) { |
| 425 return [addRuleFor(target)]; | 431 return create ? [addRuleFor(target)] : applicable; |
| 426 } | 432 } |
| 427 | 433 |
| 428 if (applicable.length == 1) return applicable; | 434 if (applicable.length == 1) return applicable; |
| 429 var first = applicable.first; | 435 var first = applicable.first; |
| 430 var finalRules = applicable.where( | 436 var finalRules = applicable.where( |
| 431 (x) => !x.mustBePrimary || (x == first)); | 437 (x) => !x.mustBePrimary || (x == first)); |
| 432 | 438 |
| 433 if (finalRules.isEmpty) throw new SerializationException( | 439 if (finalRules.isEmpty) throw new SerializationException( |
| 434 'No valid rule found for object $object'); | 440 'No valid rule found for object $object'); |
| 435 return finalRules; | 441 return finalRules; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 459 ..addRuleFor(new PrimitiveRule()) | 465 ..addRuleFor(new PrimitiveRule()) |
| 460 ..addRuleFor(new ListRuleEssential()) | 466 ..addRuleFor(new ListRuleEssential()) |
| 461 ..addRuleFor(basicRule, | 467 ..addRuleFor(basicRule, |
| 462 constructorFields: ['type', | 468 constructorFields: ['type', |
| 463 'constructorName', | 469 'constructorName', |
| 464 'constructorFields', 'regularFields', []], | 470 'constructorFields', 'regularFields', []], |
| 465 fields: []) | 471 fields: []) |
| 466 ..addRule(new NamedObjectRule()) | 472 ..addRule(new NamedObjectRule()) |
| 467 ..addRule(new MirrorRule()) | 473 ..addRule(new MirrorRule()) |
| 468 ..addRuleFor(new MirrorRule()) | 474 ..addRuleFor(new MirrorRule()) |
| 469 ..addRuleFor(new SymbolRule()); | 475 ..addRuleFor(new SymbolRule()) |
| 476 ..addRuleFor(new DateTimeRule()); |
| 470 meta.namedObjects = namedObjects; | 477 meta.namedObjects = namedObjects; |
| 471 return meta; | 478 return meta; |
| 472 } | 479 } |
| 473 | 480 |
| 474 /** Return true if our [namedObjects] collection has an entry for [object].*/ | 481 /** Return true if our [namedObjects] collection has an entry for [object].*/ |
| 475 bool _hasNameFor(object) { | 482 bool _hasNameFor(object) { |
| 476 var sentinel = const _Sentinel(); | 483 var sentinel = const _Sentinel(); |
| 477 return _nameFor(object, () => sentinel) != sentinel; | 484 return _nameFor(object, () => sentinel) != sentinel; |
| 478 } | 485 } |
| 479 | 486 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 490 } | 497 } |
| 491 | 498 |
| 492 /** | 499 /** |
| 493 * An exception class for errors during serialization. | 500 * An exception class for errors during serialization. |
| 494 */ | 501 */ |
| 495 class SerializationException implements Exception { | 502 class SerializationException implements Exception { |
| 496 final String message; | 503 final String message; |
| 497 const SerializationException([this.message]); | 504 const SerializationException([this.message]); |
| 498 toString() => "SerializationException($message)"; | 505 toString() => "SerializationException($message)"; |
| 499 } | 506 } |
| OLD | NEW |