Chromium Code Reviews| 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 * Setup | 10 * Setup |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 170 | 170 |
| 171 /** | 171 /** |
| 172 * When reading, we may need to resolve references to existing objects in | 172 * When reading, we may need to resolve references to existing objects in |
| 173 * the system. The right action may not be to create a new instance of | 173 * the system. The right action may not be to create a new instance of |
| 174 * something, but rather to find an existing instance and connect to it. | 174 * something, but rather to find an existing instance and connect to it. |
| 175 * For example, if we have are serializing an Email message and it has a | 175 * For example, if we have are serializing an Email message and it has a |
| 176 * link to the owning account, it may not be appropriate to try and serialize | 176 * link to the owning account, it may not be appropriate to try and serialize |
| 177 * the account. Instead we should just connect the de-serialized message | 177 * the account. Instead we should just connect the de-serialized message |
| 178 * object to the account object that already exists there. | 178 * object to the account object that already exists there. |
| 179 */ | 179 */ |
| 180 Map<String, dynamic> externalObjects = {}; | 180 Map<String, dynamic> namedObjects = {}; |
| 181 | 181 |
| 182 /** | 182 /** |
| 183 * When we write out data using this serialization, should we also write | 183 * When we write out data using this serialization, should we also write |
| 184 * out a description of the rules. | 184 * out a description of the rules. This is on by default unless using |
| 185 * CustomRule subclasses, in which case it requires additional setup and | |
| 186 * is off by default. | |
| 185 */ | 187 */ |
| 186 bool selfDescribing = true; | 188 bool _selfDescribing; |
| 189 | |
| 190 /** | |
| 191 * When we write out data using this serialization, should we also write | |
| 192 * out a description of the rules. This is on by default unless using | |
| 193 * CustomRule subclasses, in which case it requires additional setup and | |
| 194 * is off by default. | |
| 195 */ | |
| 196 bool get selfDescribing { | |
| 197 if (_selfDescribing != null) return _selfDescribing; | |
| 198 _selfDescribing = !rules.some((x) => x is CustomRule); | |
|
Jennifer Messerly
2012/12/12 20:38:28
Do we know that the set of "rules" is fixed at thi
Alan Knight
2012/12/12 21:19:33
Yes, this was a cheesy attempt to make it default
| |
| 199 return _selfDescribing; | |
| 200 } | |
| 201 | |
| 202 /** | |
| 203 * When we write out data using this serialization, should we also write | |
| 204 * out a description of the rules. This is on by default unless using | |
| 205 * CustomRule subclasses, in which case it requires additional setup and | |
| 206 * is off by default. | |
| 207 */ | |
| 208 set selfDescribing(x) => _selfDescribing = x; | |
| 187 | 209 |
| 188 /** | 210 /** |
| 189 * Creates a new serialization with a default set of rules for primitives | 211 * Creates a new serialization with a default set of rules for primitives |
| 190 * and lists. | 212 * and lists. |
| 191 */ | 213 */ |
| 192 Serialization() { | 214 Serialization() { |
| 193 addDefaultRules(); | 215 addDefaultRules(); |
| 194 } | 216 } |
| 195 | 217 |
| 196 /** | 218 /** |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 248 // it will always find the first one. | 270 // it will always find the first one. |
| 249 addRule(new ListRuleEssential()); | 271 addRule(new ListRuleEssential()); |
| 250 } | 272 } |
| 251 | 273 |
| 252 /** | 274 /** |
| 253 * Add a new SerializationRule [rule]. The addRuleFor method will probably | 275 * Add a new SerializationRule [rule]. The addRuleFor method will probably |
| 254 * handle most simple cases, but for adding an arbitrary rule, including | 276 * handle most simple cases, but for adding an arbitrary rule, including |
| 255 * a SerializationRule subclass which you have created, you can use this | 277 * a SerializationRule subclass which you have created, you can use this |
| 256 * method. | 278 * method. |
| 257 */ | 279 */ |
| 258 void addRule(SerializationRule rule) { | 280 void addRule(SerializationRule rule) { |
|
Jennifer Messerly
2012/12/12 20:38:28
An issue I just noticed (maybe we already have a T
Alan Knight
2012/12/12 21:19:33
Made rules private. If it starts seem like people
| |
| 259 rule.number = rules.length; | 281 rule.number = rules.length; |
| 260 rules.add(rule); | 282 rules.add(rule); |
| 261 } | 283 } |
| 262 | 284 |
| 263 /** | 285 /** |
| 264 * This is the basic method to write out an object graph rooted at | 286 * This is the basic method to write out an object graph rooted at |
| 265 * [object] and return the result. Right now this is hard-coded to return | 287 * [object] and return the result. Right now this is hard-coded to return |
| 266 * a String from a custom [JSON] format, but that is likely to change to be | 288 * a String from a custom [JSON] format, but that is likely to change to be |
| 267 * more pluggable in the near future. | 289 * more pluggable in the near future. |
| 268 */ | 290 */ |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 279 | 301 |
| 280 /** | 302 /** |
| 281 * Write out the tree in a custom flat format, returning a list containing | 303 * Write out the tree in a custom flat format, returning a list containing |
| 282 * only "simple" types: num, String, and bool. | 304 * only "simple" types: num, String, and bool. |
| 283 */ | 305 */ |
| 284 List writeFlat(Object object) { | 306 List writeFlat(Object object) { |
| 285 return newWriter().writeFlat(object); | 307 return newWriter().writeFlat(object); |
| 286 } | 308 } |
| 287 | 309 |
| 288 /** | 310 /** |
| 289 * Read the serialized data from [input] and return a List of the root | 311 * Read the serialized data from [input] and return the root object |
| 290 * objects from the result. If there are objects that need to be resolved | 312 * from the result. If there are objects that need to be resolved |
| 291 * in the current context, they should be provided in [externals] as a | 313 * in the current context, they should be provided in [externals] as a |
| 292 * Map from names to values. In particular, in the current implementation | 314 * Map from names to values. In particular, in the current implementation |
| 293 * any class mirrors needed should be provided in [externals] using the | 315 * any class mirrors needed should be provided in [externals] using the |
| 294 * class name as a key. In addition to the [externals] map provided here, | 316 * class name as a key. In addition to the [externals] map provided here, |
| 295 * values will be looked up in the [externalObjects] map. | 317 * values will be looked up in the [externalObjects] map. |
| 296 */ | 318 */ |
| 297 List read(String input, [Map externals = const {}]) { | 319 read(String input, [Map externals = const {}]) { |
| 298 return newReader().read(input, externals); | 320 return newReader().read(input, externals); |
| 299 } | 321 } |
| 300 | 322 |
| 301 /** | 323 /** |
| 302 * In the most common case there is only a single root object to be read, | |
| 303 * and this method can be used to return just one object rather than | |
| 304 * a List. The [input] and [externals] parameters are the same as for the | |
| 305 * general [read] method. | |
| 306 */ | |
| 307 Object readOne(String input, [Map externals = const {}]) { | |
| 308 return newReader().readOne(input, externals); | |
| 309 } | |
| 310 | |
| 311 /** | |
| 312 * Return a new [Reader] object for this serialization. This is useful if | 324 * Return a new [Reader] object for this serialization. This is useful if |
| 313 * you want to do something more complex with the reader than just returning | 325 * you want to do something more complex with the reader than just returning |
| 314 * the final result. | 326 * the final result. |
| 315 */ | 327 */ |
| 316 Reader newReader() => new Reader(this); | 328 Reader newReader() => new Reader(this); |
| 317 | 329 |
| 318 /** | 330 /** |
| 319 * Return the list of SerializationRule that apply to [object]. For | 331 * Return the list of SerializationRule that apply to [object]. For |
| 320 * internal use, but public because it's used in testing. | 332 * internal use, but public because it's used in testing. |
| 321 */ | 333 */ |
| 322 List<SerializationRule> rulesFor(object) { | 334 List<SerializationRule> rulesFor(object, Writer w) { |
| 323 // This has a couple of edge cases. | 335 // This has a couple of edge cases. |
| 324 // 1) The owning object may have indicated we should use a different | 336 // 1) The owning object may have indicated we should use a different |
| 325 // rule than the default. | 337 // rule than the default. |
| 326 // 2) We may not have a rule, in which case we lazily create a BasicRule. | 338 // 2) We may not have a rule, in which case we lazily create a BasicRule. |
| 327 // 3) Rules are allowed to say mustBePrimary, meaning that they can be used | 339 // 3) Rules are allowed to say mustBePrimary, meaning that they can be used |
| 328 // iff no other rule was chosen first. | 340 // iff no other rule was chosen first. |
| 329 // TODO(alanknight): Can the mustBePrimary mechanism be removed or changed. | 341 // TODO(alanknight): Can the mustBePrimary mechanism be removed or changed. |
| 330 // It adds an order dependency to the rules, and is messy. Reconsider in the | 342 // It adds an order dependency to the rules, and is messy. Reconsider in the |
| 331 // light of a more general mechanism for multiple rules per object. | 343 // light of a more general mechanism for multiple rules per object. |
| 332 // TODO(alanknight): Finding which rules apply seems likely to be a | 344 // TODO(alanknight): Finding which rules apply seems likely to be a |
| 333 // bottleneck, particularly with the current reflective implementation. | 345 // bottleneck, particularly with the current reflective implementation. |
| 334 // Consider how to improve it. e.g. cache the list of rules by class. But | 346 // Consider how to improve it. e.g. cache the list of rules by class. But |
| 335 // be careful of issues like rules which have arbitrary predicates. Or | 347 // be careful of issues like rules which have arbitrary predicates. Or |
| 336 // consider having the arbitrary predicates be secondary to an initial | 348 // consider having the arbitrary predicates be secondary to an initial |
| 337 // class-based lookup mechanism. | 349 // class-based lookup mechanism. |
| 338 var target, candidateRules; | 350 var target, candidateRules; |
| 339 if (object is DesignatedRuleForObject) { | 351 if (object is DesignatedRuleForObject) { |
| 340 target = object.target; | 352 target = object.target; |
| 341 candidateRules = object.possibleRules(rules); | 353 candidateRules = object.possibleRules(rules); |
| 342 } else { | 354 } else { |
| 343 target = object; | 355 target = object; |
| 344 candidateRules = rules; | 356 candidateRules = rules; |
| 345 } | 357 } |
| 346 List applicable = candidateRules.filter((each) => each.appliesTo(target)); | 358 List applicable = candidateRules.filter( |
| 359 (each) => each.appliesTo(target, w)); | |
| 347 | 360 |
| 348 if (applicable.isEmpty) { | 361 if (applicable.isEmpty) { |
| 349 return [addRuleFor(target)]; | 362 return [addRuleFor(target)]; |
| 350 } | 363 } |
| 351 | 364 |
| 352 if (applicable.length == 1) return applicable; | 365 if (applicable.length == 1) return applicable; |
| 353 var first = applicable[0]; | 366 var first = applicable[0]; |
| 354 var finalRules = applicable.filter( | 367 var finalRules = applicable.filter( |
| 355 (x) => !x.mustBePrimary || (x == first)); | 368 (x) => !x.mustBePrimary || (x == first)); |
| 356 | 369 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 367 */ | 380 */ |
| 368 Serialization _ruleSerialization() { | 381 Serialization _ruleSerialization() { |
| 369 // TODO(alanknight): There's an extensibility issue here with new rules. | 382 // TODO(alanknight): There's an extensibility issue here with new rules. |
| 370 // TODO(alanknight): How to handle rules with closures? They have to | 383 // TODO(alanknight): How to handle rules with closures? They have to |
| 371 // exist on the other side, but we might be able to hook them up by name, | 384 // exist on the other side, but we might be able to hook them up by name, |
| 372 // or we might just be able to validate that they're correctly set up | 385 // or we might just be able to validate that they're correctly set up |
| 373 // on the other side. | 386 // on the other side. |
| 374 | 387 |
| 375 // Make some bogus rule instances so we have something to feed rule creation | 388 // Make some bogus rule instances so we have something to feed rule creation |
| 376 // and get their types. If only we had class literals implemented... | 389 // and get their types. If only we had class literals implemented... |
| 377 var closureRule = new ClosureToMapRule.stub([].runtimeType); | 390 var basicRule = new BasicRule(reflect(null).type, '', [], [], []); |
| 378 var basicRule = new BasicRule(reflect(null).type, '', [], [], []); | |
| 379 | 391 |
| 380 var meta = new Serialization() | 392 var meta = new Serialization() |
| 381 ..selfDescribing = false | 393 ..selfDescribing = false |
| 382 ..addRuleFor(new ListRule()) | 394 ..addRuleFor(new ListRule()) |
| 383 ..addRuleFor(new PrimitiveRule()) | 395 ..addRuleFor(new PrimitiveRule()) |
| 384 ..addRuleFor(new ListRuleEssential()) | 396 ..addRuleFor(new ListRuleEssential()) |
| 385 ..addRuleFor(basicRule, | 397 ..addRuleFor(basicRule, |
| 386 constructorFields: ['typeWrapped', | 398 constructorFields: ['typeWrapped', |
| 387 'constructorName', | 399 'constructorName', |
| 388 'constructorFields', 'regularFields', []], | 400 'constructorFields', 'regularFields', []], |
| 389 fields: []) | 401 fields: []) |
| 390 ..addRule(new ClassMirrorRule()); | 402 ..addRule(new NamedObjectRule()) |
| 391 meta.externalObjects = externalObjects; | 403 ..addRule(new MirrorRule()); |
| 404 meta.namedObjects = namedObjects; | |
| 392 return meta; | 405 return meta; |
| 393 } | 406 } |
| 407 | |
| 408 /** Return true if our [namedObjects] collection has an entry for [object].*/ | |
| 409 bool _hasNameFor(object) { | |
| 410 var sentinel = const _Sentinel(); | |
| 411 return _nameFor(object, () => sentinel) != sentinel; | |
| 412 } | |
| 413 | |
| 414 /** | |
| 415 * Return the name we have for [object] in our [namedObjects] collection or | |
| 416 * the result of evaluating [ifAbsent] if there is no entry. | |
| 417 */ | |
| 418 _nameFor(object, [ifAbsent]) { | |
| 419 for (var key in namedObjects.keys) { | |
| 420 if (identical(namedObjects[key], object)) return key; | |
| 421 } | |
| 422 return ifAbsent == null ? null : ifAbsent(); | |
| 423 } | |
| 394 } | 424 } |
| 395 | 425 |
| 396 /** | 426 /** |
| 397 * An exception class for errors during serialization. | 427 * An exception class for errors during serialization. |
| 398 */ | 428 */ |
| 399 class SerializationException implements Exception { | 429 class SerializationException implements Exception { |
| 400 final String message; | 430 final String message; |
| 401 const SerializationException([this.message]); | 431 const SerializationException([this.message]); |
| 402 } | 432 } |
| OLD | NEW |