| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of serialization; | |
| 6 | |
| 7 /** | |
| 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 | |
| 10 * [write] method. | |
| 11 */ | |
| 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. | |
| 14 // Consider having an abstract superclass with the basic functionality and | |
| 15 // simple serialization subclasses where we know there aren't cycles. | |
| 16 class Writer implements ReaderOrWriter { | |
| 17 /** | |
| 18 * The [serialization] holds onto the rules that define how objects | |
| 19 * are serialized. | |
| 20 */ | |
| 21 final Serialization serialization; | |
| 22 | |
| 23 /** The [trace] object keeps track of the objects to be visited while finding | |
| 24 * the full set of objects to be written.*/ | |
| 25 Trace trace; | |
| 26 | |
| 27 /** | |
| 28 * When we write out objects, should we also write out a description | |
| 29 * of the rules for the serialization. This defaults to the corresponding | |
| 30 * value on the Serialization. | |
| 31 */ | |
| 32 bool selfDescribing; | |
| 33 | |
| 34 final Format format; | |
| 35 | |
| 36 /** | |
| 37 * Objects that cannot be represented in-place in the serialized form need | |
| 38 * to have references to them stored. The [Reference] objects are computed | |
| 39 * once and stored here for each object. This provides some space-saving, | |
| 40 * but also serves to record which objects we have already seen. | |
| 41 */ | |
| 42 final Map<dynamic, Reference> references = | |
| 43 new HashMap<Object, Reference>.identity(); | |
| 44 | |
| 45 /** | |
| 46 * The state of objects that need to be serialized is stored here. | |
| 47 * Each rule has a number, and rules keep track of the objects that they | |
| 48 * serialize, in order. So the state of any object can be found by indexing | |
| 49 * from the rule number and the object number within the rule. | |
| 50 * The actual representation of the state is determined by the rule. Lists | |
| 51 * and Maps are common, but it is arbitrary. | |
| 52 */ | |
| 53 final List<List> states = new List<List>(); | |
| 54 | |
| 55 /** Return the list of rules we use. */ | |
| 56 List<SerializationRule> get rules => serialization.rules; | |
| 57 | |
| 58 /** | |
| 59 * Creates a new [Writer] that uses the rules from its parent | |
| 60 * [Serialization]. Serializations do not keep any state | |
| 61 * related to a particular read/write, so the same one can be used | |
| 62 * for multiple different Readers/Writers. | |
| 63 */ | |
| 64 Writer(this.serialization, [Format newFormat]) : | |
| 65 format = (newFormat == null) ? const SimpleMapFormat() : newFormat { | |
| 66 trace = new Trace(this); | |
| 67 selfDescribing = serialization.selfDescribing; | |
| 68 } | |
| 69 | |
| 70 /** | |
| 71 * This is the main API for a [Writer]. It writes the objects and returns | |
| 72 * the serialized representation, as determined by [format]. | |
| 73 */ | |
| 74 write(anObject) { | |
| 75 trace.addRoot(anObject); | |
| 76 trace.traceAll(); | |
| 77 _flatten(); | |
| 78 return format.generateOutput(this); | |
| 79 } | |
| 80 | |
| 81 /** | |
| 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 | |
| 84 * anything that requires a [Reference] into one. Since only the rules | |
| 85 * know the representation they use for state, delegate to them. | |
| 86 */ | |
| 87 void _flatten() { | |
| 88 for (var eachRule in rules) { | |
| 89 _growStates(eachRule); | |
| 90 var index = eachRule.number; | |
| 91 var statesForThisRule = states[index]; | |
| 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 } | |
| 98 } | |
| 99 } | |
| 100 } | |
| 101 | |
| 102 /** | |
| 103 * As the [trace] processes each object, it will call this method on us. | |
| 104 * We find the rules for this object, and record the state of the object | |
| 105 * as determined by each rule. | |
| 106 */ | |
| 107 void _process(object, Trace trace) { | |
| 108 var real = (object is DesignatedRuleForObject) ? object.target : object; | |
| 109 for (var eachRule in serialization.rulesFor(object, this)) { | |
| 110 _record(real, eachRule); | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 /** | |
| 115 * Record the state of [object] as determined by [rule] and keep | |
| 116 * track of it. Generate a [Reference] for this object if required. | |
| 117 * When it's required is up to the particular rule, but generally everything | |
| 118 * gets a reference except a primitive. | |
| 119 * Note that at this point the states are just the same as the fields of the | |
| 120 * object, and haven't been flattened. | |
| 121 */ | |
| 122 void _record(object, SerializationRule rule) { | |
| 123 if (rule.shouldUseReferenceFor(object, this)) { | |
| 124 references.putIfAbsent(object, () => | |
| 125 new Reference(this, rule.number, _nextObjectNumberFor(rule))); | |
| 126 var state = rule.extractState(object, trace.note, this); | |
| 127 _addStateForRule(rule, state); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 /** | |
| 132 * Should we store primitive objects directly or create references for them. | |
| 133 * That depends on which format we're using, so a flat format will want | |
| 134 * references, but the Map format can store them directly. | |
| 135 */ | |
| 136 bool get shouldUseReferencesForPrimitives | |
| 137 => format.shouldUseReferencesForPrimitives; | |
| 138 | |
| 139 /** | |
| 140 * Returns a serialized version of the [SerializationRule]s used to write | |
| 141 * the data, if [selfDescribing] is true, otherwise returns null. | |
| 142 */ | |
| 143 serializedRules() { | |
| 144 if (!selfDescribing) return null; | |
| 145 var meta = serialization.ruleSerialization(); | |
| 146 var writer = new Writer(meta, format); | |
| 147 writer.selfDescribing = false; | |
| 148 return writer.write(serialization.rules); | |
| 149 } | |
| 150 | |
| 151 /** Record a [state] entry for a particular rule. */ | |
| 152 void _addStateForRule(eachRule, state) { | |
| 153 _growStates(eachRule); | |
| 154 states[eachRule.number].add(state); | |
| 155 } | |
| 156 | |
| 157 /** Find what the object number for the thing we're about to add will be.*/ | |
| 158 int _nextObjectNumberFor(SerializationRule rule) { | |
| 159 _growStates(rule); | |
| 160 return states[rule.number].length; | |
| 161 } | |
| 162 | |
| 163 /** | |
| 164 * We store the states in a List, indexed by rule number. But rules can be | |
| 165 * dynamically added, so we may have to grow the list. | |
| 166 */ | |
| 167 void _growStates(eachRule) { | |
| 168 while (states.length <= eachRule.number) states.add(new List()); | |
| 169 } | |
| 170 | |
| 171 /** | |
| 172 * Return true if we have an object number for this object. This is used to | |
| 173 * tell if we have processed the object or not. This relies on checking if we | |
| 174 * have a reference or not. That saves some space by not having to keep track | |
| 175 * of simple objects, but means that if someone refers to the identical string | |
| 176 * from several places, we will process it several times, and store it | |
| 177 * several times. That seems an acceptable tradeoff, and in cases where it | |
| 178 * isn't, it's possible to apply a rule for String, or even for Strings larger | |
| 179 * than x, which gives them references. | |
| 180 */ | |
| 181 bool _hasIndexFor(object) { | |
| 182 return _objectNumberFor(object) != -1; | |
| 183 } | |
| 184 | |
| 185 /** | |
| 186 * Given an object, find what number it has. The number is valid only in | |
| 187 * the context of a particular rule, and if the rule has more than one, | |
| 188 * this will return the one for the primary rule, defined as the one that | |
| 189 * is listed in its canonical reference. | |
| 190 */ | |
| 191 int _objectNumberFor(object) { | |
| 192 var reference = references[object]; | |
| 193 return (reference == null) ? -1 : reference.objectNumber; | |
| 194 } | |
| 195 | |
| 196 /** | |
| 197 * Return a list of [Reference] objects pointing to our roots. This will be | |
| 198 * stored in the output under "roots" in the default format. | |
| 199 */ | |
| 200 List _rootReferences() => trace.roots.map(_referenceFor).toList(); | |
| 201 | |
| 202 /** | |
| 203 * Given an object, return a reference for it if one exists. If there's | |
| 204 * no reference, return the object itself. Once we have finished the tracing | |
| 205 * step, all objects that should have a reference (roughly speaking, | |
| 206 * non-primitives) can be relied on to have a reference. | |
| 207 */ | |
| 208 _referenceFor(object) { | |
| 209 var result = references[object]; | |
| 210 return (result == null) ? object : result; | |
| 211 } | |
| 212 | |
| 213 /** | |
| 214 * Return true if the [Serialization.namedObjects] collection has a | |
| 215 * reference to [object]. | |
| 216 */ | |
| 217 // TODO(alanknight): Should the writer also have its own namedObjects | |
| 218 // collection specific to the particular write, or is that just adding | |
| 219 // complexity for little value? | |
| 220 bool hasNameFor(object) => serialization._hasNameFor(object); | |
| 221 | |
| 222 /** | |
| 223 * Return the name we have for this object in the [Serialization.namedObjects] | |
| 224 * collection. | |
| 225 */ | |
| 226 String nameFor(object) => serialization._nameFor(object); | |
| 227 | |
| 228 // For debugging/testing purposes. Find what state a reference points to. | |
| 229 stateForReference(Reference r) => states[r.ruleNumber][r.objectNumber]; | |
| 230 | |
| 231 /** Return the state pointed to by [reference]. */ | |
| 232 resolveReference(reference) => stateForReference(reference); | |
| 233 } | |
| 234 | |
| 235 /** | |
| 236 * An abstract class for Reader and Writer, which primarily exists so we can | |
| 237 * type things that will refer to one or the other, depending on which | |
| 238 * operation we're doing. | |
| 239 */ | |
| 240 abstract class ReaderOrWriter { | |
| 241 /** Return the list of serialization rules we are using.*/ | |
| 242 List<SerializationRule> get rules; | |
| 243 | |
| 244 /** Return the internal collection of object state and [Reference] objects. */ | |
| 245 List<List> get states; | |
| 246 | |
| 247 /** | |
| 248 * Return the object, or state, that ref points to, depending on which | |
| 249 * we're generating. | |
| 250 */ | |
| 251 resolveReference(Reference ref); | |
| 252 } | |
| 253 | |
| 254 /** | |
| 255 * The main class responsible for reading. It holds | |
| 256 * onto the necessary state and to the objects that have been inflated. | |
| 257 */ | |
| 258 class Reader implements ReaderOrWriter { | |
| 259 | |
| 260 /** | |
| 261 * The serialization that specifies how we read. Note that in contrast | |
| 262 * to the Writer, this is not final. This is because we may be created | |
| 263 * with an empty [Serialization] and then read the rules from the data, | |
| 264 * if [selfDescribing] is true. | |
| 265 */ | |
| 266 Serialization serialization; | |
| 267 | |
| 268 /** | |
| 269 * When we read objects, should we read a description of the rules if | |
| 270 * present. This defaults to the corresponding value on the Serialization. | |
| 271 */ | |
| 272 bool selfDescribing; | |
| 273 | |
| 274 /** | |
| 275 * The state of objects that have been serialized is stored here. | |
| 276 * Each rule has a number, and rules keep track of the objects that they | |
| 277 * serialize, in order. So the state of any object can be found by indexing | |
| 278 * from the rule number and the object number within the rule. | |
| 279 * The actual representation of the state is determined by the rule. Lists | |
| 280 * and Maps are common, but it is arbitrary. See [Writer.states]. | |
| 281 */ | |
| 282 List<List> _data; | |
| 283 | |
| 284 /** Return the internal collection of object state and [Reference] objects. */ | |
| 285 get states => _data; | |
| 286 | |
| 287 /** | |
| 288 * The resulting objects, indexed according to the same scheme as | |
| 289 * _data, where each rule has a number, and rules keep track of the objects | |
| 290 * that they serialize, in order. | |
| 291 */ | |
| 292 List<List> objects; | |
| 293 | |
| 294 final Format format; | |
| 295 | |
| 296 /** | |
| 297 * Creates a new [Reader] that uses the rules from its parent | |
| 298 * [Serialization]. Serializations do not keep any state related to | |
| 299 * a particular read or write operation, so the same one can be used | |
| 300 * for multiple different Writers/Readers. | |
| 301 */ | |
| 302 Reader(this.serialization, [Format newFormat]) : | |
| 303 format = (newFormat == null) ? const SimpleMapFormat() : newFormat { | |
| 304 selfDescribing = serialization.selfDescribing; | |
| 305 } | |
| 306 | |
| 307 /** | |
| 308 * When we read, we may need to look up objects by name in order to link to | |
| 309 * them. This is particularly true if we have references to classes, | |
| 310 * functions, mirrors, or other non-portable entities. The map in which we | |
| 311 * look things up can be provided as an argument to read, but we can also | |
| 312 * provide a map here, and objects will be looked up in both places. | |
| 313 */ | |
| 314 Map namedObjects; | |
| 315 | |
| 316 /** | |
| 317 * Look up the reference to an external object. This can be held either in | |
| 318 * the reader-specific list of externals or in the serializer's | |
| 319 */ | |
| 320 objectNamed(key, [Function ifAbsent]) { | |
| 321 var map = (namedObjects.containsKey(key)) | |
| 322 ? namedObjects : serialization.namedObjects; | |
| 323 if (!map.containsKey(key)) { | |
| 324 (ifAbsent == null ? keyNotFound : ifAbsent)(key); | |
| 325 } | |
| 326 return map[key]; | |
| 327 } | |
| 328 | |
| 329 void keyNotFound(key) { | |
| 330 throw new SerializationException( | |
| 331 'Cannot find named object to link to: $key'); | |
| 332 } | |
| 333 | |
| 334 /** | |
| 335 * Return the list of rules to be used when writing. These come from the | |
| 336 * [serialization]. | |
| 337 */ | |
| 338 List<SerializationRule> get rules => serialization.rules; | |
| 339 | |
| 340 /** | |
| 341 * Internal use only, for testing purposes. Set the data for this reader | |
| 342 * to a List of Lists whose size must match the number of rules. | |
| 343 */ | |
| 344 // When we set the data, initialize the object storage to a matching size. | |
| 345 void set data(List<List> newData) { | |
| 346 _data = newData; | |
| 347 objects = _data.map((x) => new List(x.length)).toList(); | |
| 348 } | |
| 349 | |
| 350 /** | |
| 351 * This is the primary method for a [Reader]. It takes the input data, | |
| 352 * decodes it according to [format] and returns the root object. | |
| 353 */ | |
| 354 read(rawInput, [Map externals = const {}]) { | |
| 355 namedObjects = externals; | |
| 356 var input = format.read(rawInput, this); | |
| 357 data = input["data"]; | |
| 358 rules.forEach(inflateForRule); | |
| 359 return inflateReference(input["roots"].first); | |
| 360 } | |
| 361 | |
| 362 /** | |
| 363 * If the data we are reading from has rules written to it, read them back | |
| 364 * and set them as the rules we will use. | |
| 365 */ | |
| 366 void readRules(newRules) { | |
| 367 // TODO(alanknight): Replacing the serialization is kind of confusing. | |
| 368 if (newRules == null) return; | |
| 369 var reader = serialization.ruleSerialization().newReader(format); | |
| 370 List rulesWeRead = reader.read(newRules, namedObjects); | |
| 371 if (rulesWeRead != null && !rulesWeRead.isEmpty) { | |
| 372 serialization = new Serialization.blank(); | |
| 373 rulesWeRead.forEach(serialization.addRule); | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 /** | |
| 378 * Inflate all of the objects for [rule]. Does the essential state for all | |
| 379 * objects first, then the non-essential state. This avoids cycles in | |
| 380 * non-essential state, because all the objects will have already been | |
| 381 * created. | |
| 382 */ | |
| 383 void inflateForRule(rule) { | |
| 384 var dataForThisRule = _data[rule.number]; | |
| 385 keysAndValues(dataForThisRule).forEach((position, state) { | |
| 386 inflateOne(rule, position, state); | |
| 387 }); | |
| 388 keysAndValues(dataForThisRule).forEach((position, state) { | |
| 389 rule.inflateNonEssential(state, allObjectsForRule(rule)[position], this); | |
| 390 }); | |
| 391 } | |
| 392 | |
| 393 /** | |
| 394 * Create a new object, based on [rule] and [state], which will | |
| 395 * be stored in [position] in the storage for [rule]. This will | |
| 396 * follow references and recursively inflate them, leaving Sentinel objects | |
| 397 * to detect cycles. | |
| 398 */ | |
| 399 inflateOne(SerializationRule rule, position, state) { | |
| 400 var existing = allObjectsForRule(rule)[position]; | |
| 401 // We may already be in progress and hitting this in a cycle. | |
| 402 if (existing is _Sentinel) { | |
| 403 throw new SerializationException('Cycle in essential state'); | |
| 404 } | |
| 405 // We may have already inflated this object, at least its essential state. | |
| 406 if (existing != null) return existing; | |
| 407 | |
| 408 // Put a sentinel there to mark this in case of recursion. | |
| 409 allObjectsForRule(rule)[position] = const _Sentinel(); | |
| 410 var newObject = rule.inflateEssential(state, this); | |
| 411 allObjectsForRule(rule)[position] = newObject; | |
| 412 return newObject; | |
| 413 } | |
| 414 | |
| 415 /** | |
| 416 * The parameter [possibleReference] might be a reference. If it isn't, just | |
| 417 * return it. If it is, then inflate the target of the reference and return | |
| 418 * the resulting object. | |
| 419 */ | |
| 420 inflateReference(possibleReference) { | |
| 421 // If this is a primitive, return it directly. | |
| 422 // TODO This seems too complicated. | |
| 423 return asReference(possibleReference, | |
| 424 ifReference: (reference) { | |
| 425 var rule = ruleFor(reference); | |
| 426 var state = _stateFor(reference); | |
| 427 inflateOne(rule, reference.objectNumber, state); | |
| 428 return _objectFor(reference); | |
| 429 }); | |
| 430 } | |
| 431 | |
| 432 /** Return the object pointed to by [reference]. */ | |
| 433 resolveReference(reference) => inflateReference(reference); | |
| 434 | |
| 435 /** | |
| 436 * Given [reference], return what we have stored as an object for it. Note | |
| 437 * that, depending on the current state, this might be null or a Sentinel. | |
| 438 */ | |
| 439 _objectFor(Reference reference) => | |
| 440 objects[reference.ruleNumber][reference.objectNumber]; | |
| 441 | |
| 442 /** Given [rule], return the storage for its objects. */ | |
| 443 allObjectsForRule(SerializationRule rule) => objects[rule.number]; | |
| 444 | |
| 445 /** Given [reference], return the the state we have stored for it. */ | |
| 446 _stateFor(Reference reference) => | |
| 447 _data[reference.ruleNumber][reference.objectNumber]; | |
| 448 | |
| 449 /** Given a reference, return the rule it references. */ | |
| 450 SerializationRule ruleFor(Reference reference) => | |
| 451 serialization.rules[reference.ruleNumber]; | |
| 452 | |
| 453 /** | |
| 454 * Return the primitive rule we are using. This is an ugly mechanism to | |
| 455 * support the extra information to reconstruct objects in the | |
| 456 * [SimpleJsonFormat]. | |
| 457 */ | |
| 458 SerializationRule _primitiveRule() { | |
| 459 for (var each in rules) { | |
| 460 if (each.runtimeType == PrimitiveRule) { | |
| 461 return each; | |
| 462 } | |
| 463 } | |
| 464 throw new SerializationException("No PrimitiveRule found"); | |
| 465 } | |
| 466 | |
| 467 /** | |
| 468 * Given a possible reference [anObject], call either [ifReference] or | |
| 469 * [ifNotReference], depending if it's a reference or not. This is the | |
| 470 * primary place that knows about the serialized representation of a | |
| 471 * reference. | |
| 472 */ | |
| 473 asReference(anObject, {Function ifReference: doNothing, | |
| 474 Function ifNotReference : doNothing}) { | |
| 475 if (anObject is Reference) return ifReference(anObject); | |
| 476 if (anObject is Map && anObject["__Ref"] != null) { | |
| 477 var ref = | |
| 478 new Reference(this, anObject["rule"], anObject["object"]); | |
| 479 return ifReference(ref); | |
| 480 } else { | |
| 481 return ifNotReference(anObject); | |
| 482 } | |
| 483 } | |
| 484 } | |
| 485 | |
| 486 /** | |
| 487 * This serves as a marker to indicate a object that is in the process of | |
| 488 * being de-serialized. So if we look for an object slot and find one of these, | |
| 489 * we know we've hit a cycle. | |
| 490 */ | |
| 491 class _Sentinel { | |
| 492 const _Sentinel(); | |
| 493 } | |
| 494 | |
| 495 /** | |
| 496 * This represents the transitive closure of the referenced objects to be | |
| 497 * used for serialization. It works closely in conjunction with the Writer, | |
| 498 * and is kept as a separate object primarily for the possibility of wanting | |
| 499 * to plug in different sorts of tracing rules. | |
| 500 */ | |
| 501 class Trace { | |
| 502 // TODO(alanknight): It seems likely that the mechanism for cutting off | |
| 503 // tracings is by specifying rules. So is there any reason any more to have | |
| 504 // this as a separate class? | |
| 505 final Writer writer; | |
| 506 | |
| 507 /** | |
| 508 * This class works by doing a breadth-first traversal of the objects, | |
| 509 * with the traversal order maintained in [queue]. | |
| 510 */ | |
| 511 final Queue queue = new Queue(); | |
| 512 | |
| 513 /** The root objects from which we will be tracing. */ | |
| 514 final List roots = []; | |
| 515 | |
| 516 Trace(this.writer); | |
| 517 | |
| 518 void addRoot(object) { | |
| 519 roots.add(object); | |
| 520 } | |
| 521 | |
| 522 /** A convenience method to add a single root and trace it in one step. */ | |
| 523 void trace(object) { | |
| 524 addRoot(object); | |
| 525 traceAll(); | |
| 526 } | |
| 527 | |
| 528 /** | |
| 529 * Process all of the objects reachable from our roots via state that the | |
| 530 * serialization rules access. | |
| 531 */ | |
| 532 void traceAll() { | |
| 533 queue.addAll(roots); | |
| 534 while (!queue.isEmpty) { | |
| 535 var next = queue.removeFirst(); | |
| 536 if (!hasProcessed(next)) writer._process(next, this); | |
| 537 } | |
| 538 } | |
| 539 | |
| 540 /** | |
| 541 * Has this object been seen yet? We test for this by checking if the | |
| 542 * writer has a reference for it. See comment for _hasIndexFor. | |
| 543 */ | |
| 544 bool hasProcessed(object) { | |
| 545 return writer._hasIndexFor(object); | |
| 546 } | |
| 547 | |
| 548 /** Note that we've seen [value], and add it to the queue to be processed. */ | |
| 549 note(value) { | |
| 550 if (value != null) { | |
| 551 queue.add(value); | |
| 552 } | |
| 553 return value; | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 /** | |
| 558 * Any pointers to objects that can't be represented directly in the | |
| 559 * serialization format has to be stored as a reference. A reference encodes | |
| 560 * the rule number of the rule that saved it in the Serialization that was used | |
| 561 * for writing, and the object number within that rule. | |
| 562 */ | |
| 563 class Reference { | |
| 564 /** The [Reader] or [Writer] that owns this reference. */ | |
| 565 final ReaderOrWriter parent; | |
| 566 /** The position of the rule that controls this reference in [parent]. */ | |
| 567 final int ruleNumber; | |
| 568 /** The index of the referred-to object in the storage of [parent] */ | |
| 569 final int objectNumber; | |
| 570 | |
| 571 Reference(this.parent, this.ruleNumber, this.objectNumber) { | |
| 572 if (ruleNumber == null || objectNumber == null) { | |
| 573 throw new SerializationException("Invalid Reference"); | |
| 574 } | |
| 575 if (parent.rules.length < ruleNumber) { | |
| 576 throw new SerializationException("Invalid Reference"); | |
| 577 } | |
| 578 } | |
| 579 | |
| 580 /** | |
| 581 * Return the thing this reference points to. Assumes that we have a valid | |
| 582 * parent and that it is a Reader, as inflating is not meaningful when | |
| 583 * writing. | |
| 584 */ | |
| 585 inflated() => parent.resolveReference(this); | |
| 586 | |
| 587 /** | |
| 588 * Convert the reference to a map in JSON format. This is specific to the | |
| 589 * custom JSON format we define, and must be consistent with the | |
| 590 * [Reader.asReference] method. | |
| 591 */ | |
| 592 // TODO(alanknight): This is a hack both in defining a toJson specific to a | |
| 593 // particular representation, and the use of a bogus sentinel "__Ref" | |
| 594 Map<String, int> toJson() => { | |
| 595 "__Ref" : 0, | |
| 596 "rule" : ruleNumber, | |
| 597 "object" : objectNumber | |
| 598 }; | |
| 599 | |
| 600 /** Write our information to [list]. Useful in writing to flat formats.*/ | |
| 601 void writeToList(List list) { | |
| 602 list.add(ruleNumber); | |
| 603 list.add(objectNumber); | |
| 604 } | |
| 605 | |
| 606 String toString() => "Reference($ruleNumber, $objectNumber)"; | |
| 607 } | |
| 608 | |
| 609 /** | |
| 610 * This is used during tracing to indicate that an object should be processed | |
| 611 * using a particular rule, rather than the one that might ordinarily be | |
| 612 * found for it. This normally only makes sense if the object is uniquely | |
| 613 * referenced, and is a more or less internal collection. See ListRuleEssential | |
| 614 * for an example. It knows how to return its object and how to filter. | |
| 615 */ | |
| 616 class DesignatedRuleForObject { | |
| 617 final Function rulePredicate; | |
| 618 final target; | |
| 619 | |
| 620 DesignatedRuleForObject(this.target, this.rulePredicate); | |
| 621 | |
| 622 List possibleRules(List rules) => rules.where(rulePredicate).toList(); | |
| 623 } | |
| OLD | NEW |