| 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 /** | 7 /** |
| 8 * This writes out the state of the objects to an external format. It holds | 8 * This writes out the state of the objects to an external format. It holds |
| 9 * all of the intermediate state needed. The primary API for it is the | 9 * all of the intermediate state needed. The primary API for it is the |
| 10 * [write] method. | 10 * [write] method. |
| 11 */ | 11 */ |
| 12 // TODO(alanknight): For simple serialization formats this does a lot of work | 12 // TODO(alanknight): For simple serialization formats this does a lot of work |
| 13 // that isn't necessary, e.g. detecting cycles and maintaining references. | 13 // that isn't necessary, e.g. detecting cycles and maintaining references. |
| 14 // Consider having an abstract superclass with the basic functionality and | 14 // Consider having an abstract superclass with the basic functionality and |
| 15 // simple serialization subclasses where we know there aren't cycles. | 15 // simple serialization subclasses where we know there aren't cycles. |
| 16 class Writer { | 16 class Writer implements ReaderOrWriter { |
| 17 /** | 17 /** |
| 18 * The [serialization] holds onto the rules that define how objects | 18 * The [serialization] holds onto the rules that define how objects |
| 19 * are serialized. | 19 * are serialized. |
| 20 */ | 20 */ |
| 21 final Serialization serialization; | 21 final Serialization serialization; |
| 22 | 22 |
| 23 /** The [trace] object keeps track of the objects to be visited while finding | 23 /** The [trace] object keeps track of the objects to be visited while finding |
| 24 * the full set of objects to be written.*/ | 24 * the full set of objects to be written.*/ |
| 25 Trace trace; | 25 Trace trace; |
| 26 | 26 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 81 /** | 81 /** |
| 82 * Given that we have fully populated the list of [states], and more | 82 * Given that we have fully populated the list of [states], and more |
| 83 * importantly, the list of [references], go through each state and turn | 83 * importantly, the list of [references], go through each state and turn |
| 84 * anything that requires a [Reference] into one. Since only the rules | 84 * anything that requires a [Reference] into one. Since only the rules |
| 85 * know the representation they use for state, delegate to them. | 85 * know the representation they use for state, delegate to them. |
| 86 */ | 86 */ |
| 87 void _flatten() { | 87 void _flatten() { |
| 88 for (var eachRule in rules) { | 88 for (var eachRule in rules) { |
| 89 _growStates(eachRule); | 89 _growStates(eachRule); |
| 90 var index = eachRule.number; | 90 var index = eachRule.number; |
| 91 for (var eachState in states[index]) { | 91 var statesForThisRule = states[index]; |
| 92 eachRule.flatten(eachState, this); | 92 for (var i = 0; i < statesForThisRule.length; i++) { |
| 93 var eachState = statesForThisRule[i]; |
| 94 var newState = eachRule.flatten(eachState, this); |
| 95 if (newState != null) { |
| 96 statesForThisRule[i] = newState; |
| 97 } |
| 93 } | 98 } |
| 94 } | 99 } |
| 95 } | 100 } |
| 96 | 101 |
| 97 /** | 102 /** |
| 98 * As the [trace] processes each object, it will call this method on us. | 103 * As the [trace] processes each object, it will call this method on us. |
| 99 * We find the rules for this object, and record the state of the object | 104 * We find the rules for this object, and record the state of the object |
| 100 * as determined by each rule. | 105 * as determined by each rule. |
| 101 */ | 106 */ |
| 102 void _process(object, Trace trace) { | 107 void _process(object, Trace trace) { |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 } | 194 } |
| 190 | 195 |
| 191 /** | 196 /** |
| 192 * Return a list of [Reference] objects pointing to our roots. This will be | 197 * Return a list of [Reference] objects pointing to our roots. This will be |
| 193 * stored in the output under "roots" in the default format. | 198 * stored in the output under "roots" in the default format. |
| 194 */ | 199 */ |
| 195 _rootReferences() => trace.roots.mappedBy(_referenceFor).toList(); | 200 _rootReferences() => trace.roots.mappedBy(_referenceFor).toList(); |
| 196 | 201 |
| 197 /** | 202 /** |
| 198 * Given an object, return a reference for it if one exists. If there's | 203 * Given an object, return a reference for it if one exists. If there's |
| 199 * no reference, return null. Once we have finished the tracing step, all | 204 * no reference, return the object itself. Once we have finished the tracing |
| 200 * objects that should have a reference (roughly speaking, non-primitives) | 205 * step, all objects that should have a reference (roughly speaking, |
| 201 * can be relied on to have a reference. | 206 * non-primitives) can be relied on to have a reference. |
| 202 */ | 207 */ |
| 203 _referenceFor(object) => references[object]; | 208 _referenceFor(object) { |
| 209 var result = references[object]; |
| 210 return (result == null) ? object : result; |
| 211 } |
| 204 | 212 |
| 205 /** | 213 /** |
| 206 * Return true if the [namedObjects] collection has a reference to [object]. | 214 * Return true if the [namedObjects] collection has a reference to [object]. |
| 207 */ | 215 */ |
| 208 // TODO(alanknight): Should the writer also have its own namedObjects | 216 // TODO(alanknight): Should the writer also have its own namedObjects |
| 209 // collection specific to the particular write, or is that just adding | 217 // collection specific to the particular write, or is that just adding |
| 210 // complexity for little value? | 218 // complexity for little value? |
| 211 hasNameFor(object) => serialization._hasNameFor(object); | 219 hasNameFor(object) => serialization._hasNameFor(object); |
| 212 | 220 |
| 213 /** | 221 /** |
| 214 * Return the name we have for this object in the [namedObjects] collection. | 222 * Return the name we have for this object in the [namedObjects] collection. |
| 215 */ | 223 */ |
| 216 nameFor(object) => serialization._nameFor(object); | 224 nameFor(object) => serialization._nameFor(object); |
| 217 | 225 |
| 218 // For debugging/testing purposes. Find what state a reference points to. | 226 // For debugging/testing purposes. Find what state a reference points to. |
| 219 stateForReference(Reference r) => states[r.ruleNumber][r.objectNumber]; | 227 stateForReference(Reference r) => states[r.ruleNumber][r.objectNumber]; |
| 228 |
| 229 /** Return the state pointed to by [reference]. */ |
| 230 resolveReference(reference) => stateForReference(reference); |
| 231 } |
| 232 |
| 233 /** |
| 234 * An abstract class for Reader and Writer, which primarily exists so we can |
| 235 * type things that will refer to one or the other, depending on which |
| 236 * operation we're doing. |
| 237 */ |
| 238 abstract class ReaderOrWriter { |
| 239 /** Return the list of serialization rules we are using.*/ |
| 240 List<SerializationRule> get rules; |
| 241 |
| 242 /** |
| 243 * Return the object, or state, that ref points to, depending on which |
| 244 * we're generating. |
| 245 */ |
| 246 resolveReference(Reference ref); |
| 220 } | 247 } |
| 221 | 248 |
| 222 /** | 249 /** |
| 223 * The main class responsible for reading. It holds | 250 * The main class responsible for reading. It holds |
| 224 * onto the necessary state and to the objects that have been inflated. | 251 * onto the necessary state and to the objects that have been inflated. |
| 225 */ | 252 */ |
| 226 class Reader { | 253 class Reader implements ReaderOrWriter { |
| 227 | 254 |
| 228 /** | 255 /** |
| 229 * The serialization that specifies how we read. Note that in contrast | 256 * The serialization that specifies how we read. Note that in contrast |
| 230 * to the Writer, this is not final. This is because we may be created | 257 * to the Writer, this is not final. This is because we may be created |
| 231 * with an empty [Serialization] and then read the rules from the data, | 258 * with an empty [Serialization] and then read the rules from the data, |
| 232 * if [selfDescribing] is true. | 259 * if [selfDescribing] is true. |
| 233 */ | 260 */ |
| 234 Serialization serialization; | 261 Serialization serialization; |
| 235 | 262 |
| 236 /** | 263 /** |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 381 // TODO This seems too complicated. | 408 // TODO This seems too complicated. |
| 382 return asReference(possibleReference, | 409 return asReference(possibleReference, |
| 383 ifReference: (reference) { | 410 ifReference: (reference) { |
| 384 var rule = ruleFor(reference); | 411 var rule = ruleFor(reference); |
| 385 var state = _stateFor(reference); | 412 var state = _stateFor(reference); |
| 386 inflateOne(rule, reference.objectNumber, state); | 413 inflateOne(rule, reference.objectNumber, state); |
| 387 return _objectFor(reference); | 414 return _objectFor(reference); |
| 388 }); | 415 }); |
| 389 } | 416 } |
| 390 | 417 |
| 418 /** Return the object pointed to by [reference]. */ |
| 419 resolveReference(reference) => inflateReference(reference); |
| 420 |
| 391 /** | 421 /** |
| 392 * Given [reference], return what we have stored as an object for it. Note | 422 * Given [reference], return what we have stored as an object for it. Note |
| 393 * that, depending on the current state, this might be null or a Sentinel. | 423 * that, depending on the current state, this might be null or a Sentinel. |
| 394 */ | 424 */ |
| 395 _objectFor(Reference reference) => | 425 _objectFor(Reference reference) => |
| 396 objects[reference.ruleNumber][reference.objectNumber]; | 426 objects[reference.ruleNumber][reference.objectNumber]; |
| 397 | 427 |
| 398 /** Given [rule], return the storage for its objects. */ | 428 /** Given [rule], return the storage for its objects. */ |
| 399 allObjectsForRule(SerializationRule rule) => objects[rule.number]; | 429 allObjectsForRule(SerializationRule rule) => objects[rule.number]; |
| 400 | 430 |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 511 } | 541 } |
| 512 | 542 |
| 513 /** | 543 /** |
| 514 * Any pointers to objects that can't be represented directly in the | 544 * Any pointers to objects that can't be represented directly in the |
| 515 * serialization format has to be stored as a reference. A reference encodes | 545 * serialization format has to be stored as a reference. A reference encodes |
| 516 * the rule number of the rule that saved it in the Serialization that was used | 546 * the rule number of the rule that saved it in the Serialization that was used |
| 517 * for writing, and the object number within that rule. | 547 * for writing, and the object number within that rule. |
| 518 */ | 548 */ |
| 519 class Reference { | 549 class Reference { |
| 520 /** The [Reader] or [Writer] that owns this reference. */ | 550 /** The [Reader] or [Writer] that owns this reference. */ |
| 521 final parent; | 551 final ReaderOrWriter parent; |
| 522 /** The position of the rule that controls this reference in [parent]. */ | 552 /** The position of the rule that controls this reference in [parent]. */ |
| 523 final int ruleNumber; | 553 final int ruleNumber; |
| 524 /** The index of the referred-to object in the storage of [parent] */ | 554 /** The index of the referred-to object in the storage of [parent] */ |
| 525 final int objectNumber; | 555 final int objectNumber; |
| 526 | 556 |
| 527 const Reference(this.parent, this.ruleNumber, this.objectNumber); | 557 Reference(this.parent, this.ruleNumber, this.objectNumber) { |
| 558 if (ruleNumber == null || objectNumber == null) { |
| 559 throw new SerializationException("Invalid Reference"); |
| 560 } |
| 561 if (parent.rules.length < ruleNumber) { |
| 562 throw new SerializationException("Invalid Reference"); |
| 563 } |
| 564 } |
| 565 |
| 566 /** |
| 567 * Return the thing this reference points to. Assumes that we have a valid |
| 568 * parent and that it is a Reader, as inflating is not meaningful when |
| 569 * writing. |
| 570 */ |
| 571 inflated() => parent.resolveReference(this); |
| 528 | 572 |
| 529 /** | 573 /** |
| 530 * Convert the reference to a map in JSON format. This is specific to the | 574 * Convert the reference to a map in JSON format. This is specific to the |
| 531 * custom JSON format we define, and must be consistent with the | 575 * custom JSON format we define, and must be consistent with the |
| 532 * [asReference] method. | 576 * [asReference] method. |
| 533 */ | 577 */ |
| 534 // TODO(alanknight): This is a hack both in defining a toJson specific to a | 578 // TODO(alanknight): This is a hack both in defining a toJson specific to a |
| 535 // particular representation, and the use of a bogus sentinel "__Ref" | 579 // particular representation, and the use of a bogus sentinel "__Ref" |
| 536 toJson() => { | 580 toJson() => { |
| 537 "__Ref" : true, | 581 "__Ref" : true, |
| 538 "rule" : ruleNumber, | 582 "rule" : ruleNumber, |
| 539 "object" : objectNumber | 583 "object" : objectNumber |
| 540 }; | 584 }; |
| 541 | 585 |
| 542 /** Write our information to [list]. Useful in writing to flat formats.*/ | 586 /** Write our information to [list]. Useful in writing to flat formats.*/ |
| 543 writeToList(List list) { | 587 writeToList(List list) { |
| 544 list.add(ruleNumber); | 588 list.add(ruleNumber); |
| 545 list.add(objectNumber); | 589 list.add(objectNumber); |
| 546 } | 590 } |
| 547 | 591 |
| 548 toString() => "Reference $ruleNumber, $objectNumber"; | 592 toString() => "Reference($ruleNumber, $objectNumber)"; |
| 549 } | 593 } |
| 550 | 594 |
| 551 /** | 595 /** |
| 552 * This is used during tracing to indicate that an object should be processed | 596 * This is used during tracing to indicate that an object should be processed |
| 553 * using a particular rule, rather than the one that might ordinarily be | 597 * using a particular rule, rather than the one that might ordinarily be |
| 554 * found for it. This normally only makes sense if the object is uniquely | 598 * found for it. This normally only makes sense if the object is uniquely |
| 555 * referenced, and is a more or less internal collection. See ListRuleEssential | 599 * referenced, and is a more or less internal collection. See ListRuleEssential |
| 556 * for an example. It knows how to return its object and how to filter. | 600 * for an example. It knows how to return its object and how to filter. |
| 557 */ | 601 */ |
| 558 class DesignatedRuleForObject { | 602 class DesignatedRuleForObject { |
| 559 Function rulePredicate; | 603 Function rulePredicate; |
| 560 final target; | 604 final target; |
| 561 | 605 |
| 562 DesignatedRuleForObject(this.target, this.rulePredicate); | 606 DesignatedRuleForObject(this.target, this.rulePredicate); |
| 563 | 607 |
| 564 possibleRules(List rules) => rules.where(rulePredicate).toList(); | 608 possibleRules(List rules) => rules.where(rulePredicate).toList(); |
| 565 } | 609 } |
| OLD | NEW |