| 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 // TODO(alanknight): We should have an example and tests for subclassing | |
| 8 // serialization rule rather than using the hard-coded ClosureToMap rule. And | |
| 9 // possibly an abstract superclass that's designed to be subclassed that way. | |
| 10 /** | |
| 11 * The abstract superclass for serialization rules. | |
| 12 */ | |
| 13 abstract class SerializationRule { | |
| 14 /** | |
| 15 * Rules belong uniquely to a particular Serialization instance, and can | |
| 16 * be identified within it by number. | |
| 17 */ | |
| 18 int _number; | |
| 19 | |
| 20 /** | |
| 21 * Rules belong uniquely to a particular Serialization instance, and can | |
| 22 * be identified within it by number. | |
| 23 */ | |
| 24 int get number => _number; | |
| 25 | |
| 26 /** | |
| 27 * Rules belong uniquely to a particular Serialization instance, and can | |
| 28 * be identified within it by number. | |
| 29 */ | |
| 30 void set number(int value) { | |
| 31 if (_number != null && _number != value) throw | |
| 32 new SerializationException("Rule numbers cannot be changed, once set"); | |
| 33 _number = value; | |
| 34 } | |
| 35 | |
| 36 /** | |
| 37 * Return true if this rule applies to this object, in the context | |
| 38 * where we're writing it, false otherwise. | |
| 39 */ | |
| 40 bool appliesTo(object, Writer writer); | |
| 41 | |
| 42 /** | |
| 43 * This extracts the state from the object, calling [f] for each value | |
| 44 * as it is extracted, and returning an object representing the whole | |
| 45 * state at the end. The state that results will still have direct | |
| 46 * pointers to objects, rather than references. | |
| 47 */ | |
| 48 extractState(object, void f(value), Writer w); | |
| 49 | |
| 50 /** | |
| 51 * Allows rules to tell us how they expect to store their state. If this | |
| 52 * isn't specified we can also just look at the data to tell. | |
| 53 */ | |
| 54 bool get storesStateAsLists => false; | |
| 55 bool get storesStateAsMaps => false; | |
| 56 bool get storesStateAsPrimitives => false; | |
| 57 | |
| 58 /** | |
| 59 * Given the variables representing the state of an object, flatten it | |
| 60 * by turning object pointers into Reference objects where needed. This | |
| 61 * destructively modifies the state object. | |
| 62 * | |
| 63 * This has a default implementation which assumes that object is indexable, | |
| 64 * so either conforms to Map or List. Subclasses may override to do something | |
| 65 * different, including returning a new state object to be used in place | |
| 66 * of the original. | |
| 67 */ | |
| 68 // This has to be a separate operation from extracting, because we extract | |
| 69 // as we are traversing the objects, so we don't yet have the objects to | |
| 70 // generate references for them. It might be possible to avoid that by | |
| 71 // doing a depth-first rather than breadth-first traversal, but I'm not | |
| 72 // sure it's worth it. | |
| 73 flatten(state, Writer writer) { | |
| 74 keysAndValues(state).forEach((key, value) { | |
| 75 var reference = writer._referenceFor(value); | |
| 76 state[key] = reference; | |
| 77 }); | |
| 78 } | |
| 79 | |
| 80 /** Return true if this rule should only be applied when we are the first | |
| 81 * rule found that applies to this object. This may or may not be a hack | |
| 82 * that will disappear once we have better support for multiple rules. | |
| 83 * We want to have multiple different rules that apply to the same object. We | |
| 84 * also want to have multiple different rules that might exclusively apply | |
| 85 * to the same object. So, we want either ListRule or ListRuleEssential, and | |
| 86 * only one of them can be there. But on the other hand, we may want both | |
| 87 * ListRule and BasicRule. So we identify the kinds of rules that can share. | |
| 88 * If mustBePrimary returns true, then this rule will only be chosen if no | |
| 89 * other rule has been found yet. This means that the ordering of rules in | |
| 90 * the serialization is significant, which is unpleasant, but we'll have | |
| 91 * to see how bad it is. | |
| 92 */ | |
| 93 // TODO(alanknight): Reconsider whether this should be handled differently. | |
| 94 bool get mustBePrimary => false; | |
| 95 | |
| 96 /** | |
| 97 * Create the new object corresponding to [state] using the rules | |
| 98 * from [reader]. This may involve recursively inflating "essential" | |
| 99 * references in the state, which are those that are required for the | |
| 100 * object's constructor. It is up to the rule what state is considered | |
| 101 * essential. | |
| 102 */ | |
| 103 inflateEssential(state, Reader reader); | |
| 104 | |
| 105 /** | |
| 106 * The [object] has already been created. Set any of its non-essential | |
| 107 * variables from the representation in [state]. Where there are references | |
| 108 * to other objects they are resolved in the context of [reader]. | |
| 109 */ | |
| 110 void inflateNonEssential(state, object, Reader reader); | |
| 111 | |
| 112 /** | |
| 113 * If we have [object] as part of our state, should we represent that | |
| 114 * directly, or should we make a reference for it. By default, true. | |
| 115 * This may also delegate to [writer]. | |
| 116 */ | |
| 117 bool shouldUseReferenceFor(object, Writer writer) => true; | |
| 118 | |
| 119 /** | |
| 120 * Return true if the data this rule returns is variable length, so a | |
| 121 * length needs to be written for it if the format requires that. Return | |
| 122 * false if the results are always the same length. | |
| 123 */ | |
| 124 bool get hasVariableLengthEntries => true; | |
| 125 | |
| 126 /** | |
| 127 * If the data is fixed length, return it here. The format may or may not | |
| 128 * make use of this, depending on whether it already has enough information | |
| 129 * to determine the length on its own. If [hasVariableLengthEntries] is true | |
| 130 * this is ignored. | |
| 131 */ | |
| 132 int get dataLength => 0; | |
| 133 } | |
| 134 | |
| 135 /** | |
| 136 * This rule handles things that implement List. It will recreate them as | |
| 137 * whatever the default implemenation of List is on the target platform. | |
| 138 */ | |
| 139 class ListRule extends SerializationRule { | |
| 140 | |
| 141 bool appliesTo(object, Writer w) => object is List; | |
| 142 | |
| 143 bool get storesStateAsLists => true; | |
| 144 | |
| 145 List extractState(List list, f, Writer w) { | |
| 146 var result = new List(); | |
| 147 for (var each in list) { | |
| 148 result.add(each); | |
| 149 f(each); | |
| 150 } | |
| 151 return result; | |
| 152 } | |
| 153 | |
| 154 inflateEssential(List state, Reader r) => new List(); | |
| 155 | |
| 156 // For a list, we consider all of its state non-essential and add it | |
| 157 // after creation. | |
| 158 void inflateNonEssential(List state, List newList, Reader r) { | |
| 159 populateContents(state, newList, r); | |
| 160 } | |
| 161 | |
| 162 void populateContents(List state, List newList, Reader r) { | |
| 163 for(var each in state) { | |
| 164 newList.add(r.inflateReference(each)); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 bool get hasVariableLengthEntries => true; | |
| 169 } | |
| 170 | |
| 171 /** | |
| 172 * This is a subclass of ListRule where all of the list's contents are | |
| 173 * considered essential state. This is needed if an object X contains a List L, | |
| 174 * but it expects L's contents to be fixed when X's constructor is called. | |
| 175 */ | |
| 176 class ListRuleEssential extends ListRule { | |
| 177 | |
| 178 /** Create the new List and also inflate all of its contents. */ | |
| 179 inflateEssential(List state, Reader r) { | |
| 180 var object = super.inflateEssential(state, r); | |
| 181 populateContents(state, object, r); | |
| 182 return object; | |
| 183 } | |
| 184 | |
| 185 /** Does nothing, because all the work has been done in inflateEssential. */ | |
| 186 void inflateNonEssential(state, newList, reader) {} | |
| 187 | |
| 188 bool get mustBePrimary => true; | |
| 189 } | |
| 190 | |
| 191 /** | |
| 192 * This rule handles things that implement Map. It will recreate them as | |
| 193 * whatever the default implemenation of Map is on the target platform. If a | |
| 194 * map has string keys it will attempt to retain it as a map for JSON formats, | |
| 195 * otherwise it will store it as a list of references to keys and values. | |
| 196 */ | |
| 197 class MapRule extends SerializationRule { | |
| 198 | |
| 199 bool appliesTo(object, Writer w) => object is Map; | |
| 200 | |
| 201 bool get storesStateAsMaps => true; | |
| 202 | |
| 203 extractState(Map map, f, Writer w) { | |
| 204 // Note that we make a copy here because flattening may be destructive. | |
| 205 var newMap = new Map.from(map); | |
| 206 newMap.forEach((key, value) { | |
| 207 f(key); | |
| 208 f(value); | |
| 209 }); | |
| 210 return newMap; | |
| 211 } | |
| 212 | |
| 213 /** | |
| 214 * Change the keys and values of [state] into references in [writer]. | |
| 215 * If [state] is a map whose keys are all strings then we leave the keys | |
| 216 * as is so that JSON formats will be more readable. If the keys are | |
| 217 * arbitrary then we need to turn them into references and replace the | |
| 218 * state with a new Map whose keys are the references. | |
| 219 */ | |
| 220 flatten(Map state, Writer writer) { | |
| 221 bool keysAreAllStrings = state.keys.every((x) => x is String); | |
| 222 if (keysAreAllStrings && !writer.shouldUseReferencesForPrimitives) { | |
| 223 keysAndValues(state).forEach( | |
| 224 (key, value) => state[key] = writer._referenceFor(value)); | |
| 225 return state; | |
| 226 } else { | |
| 227 var newState = []; | |
| 228 keysAndValues(state).forEach((key, value) { | |
| 229 newState.add(writer._referenceFor(key)); | |
| 230 newState.add(writer._referenceFor(value)); | |
| 231 }); | |
| 232 return newState; | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 inflateEssential(state, Reader r) => new Map(); | |
| 237 | |
| 238 // For a map, we consider all of its state non-essential and add it | |
| 239 // after creation. | |
| 240 void inflateNonEssential(state, Map newMap, Reader r) { | |
| 241 if (state is List) { | |
| 242 inflateNonEssentialFromList(state, newMap, r); | |
| 243 } else { | |
| 244 inflateNonEssentialFromMap(state, newMap, r); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 void inflateNonEssentialFromList(List state, Map newMap, Reader r) { | |
| 249 var key; | |
| 250 for (var each in state) { | |
| 251 if (key == null) { | |
| 252 key = each; | |
| 253 } else { | |
| 254 newMap[r.inflateReference(key)] = r.inflateReference(each); | |
| 255 key = null; | |
| 256 } | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 void inflateNonEssentialFromMap(Map state, Map newMap, Reader r) { | |
| 261 state.forEach((key, value) { | |
| 262 newMap[r.inflateReference(key)] = r.inflateReference(value); | |
| 263 }); | |
| 264 } | |
| 265 | |
| 266 bool get hasVariableLengthEntries => true; | |
| 267 } | |
| 268 | |
| 269 /** | |
| 270 * This rule handles primitive types, defined as those that we can normally | |
| 271 * represent directly in the output format. We hard-code that to mean | |
| 272 * num, String, and bool. | |
| 273 */ | |
| 274 class PrimitiveRule extends SerializationRule { | |
| 275 bool appliesTo(object, Writer w) => isPrimitive(object); | |
| 276 extractState(object, Function f, Writer w) => object; | |
| 277 flatten(object, Writer writer) {} | |
| 278 inflateEssential(state, Reader r) => state; | |
| 279 void inflateNonEssential(object, _, Reader r) {} | |
| 280 | |
| 281 bool get storesStateAsPrimitives => true; | |
| 282 | |
| 283 /** | |
| 284 * Indicate whether we should save pointers to this object as references | |
| 285 * or store the object directly. For primitives this depends on the format, | |
| 286 * so we delegate to the writer. | |
| 287 */ | |
| 288 bool shouldUseReferenceFor(object, Writer w) => | |
| 289 w.shouldUseReferencesForPrimitives; | |
| 290 | |
| 291 bool get hasVariableLengthEntries => false; | |
| 292 } | |
| 293 | |
| 294 /** Typedef for the object construction closure used in ClosureRule. */ | |
| 295 typedef ConstructType(Map m); | |
| 296 | |
| 297 /** Typedef for the state-getting closure used in ClosureToMapRule. */ | |
| 298 typedef Map<String, dynamic> GetStateType(object); | |
| 299 | |
| 300 /** Typedef for the state-setting closure used in ClosureToMapRule. */ | |
| 301 typedef void NonEssentialStateType(object, Map m); | |
| 302 | |
| 303 /** | |
| 304 * This is a rule where the extraction and creation are hard-coded as | |
| 305 * closures. The result is expected to be a map indexed by field name. | |
| 306 */ | |
| 307 class ClosureRule extends CustomRule { | |
| 308 | |
| 309 /** The runtimeType of objects that this rule applies to. Used in appliesTo.*/ | |
| 310 final Type type; | |
| 311 | |
| 312 /** The function for constructing new objects when reading. */ | |
| 313 final ConstructType construct; | |
| 314 | |
| 315 /** The function for returning an object's state as a Map. */ | |
| 316 final GetStateType getStateFunction; | |
| 317 | |
| 318 /** The function for setting an object's state from a Map. */ | |
| 319 final NonEssentialStateType setNonEssentialState; | |
| 320 | |
| 321 /** | |
| 322 * Create a ClosureToMapRule for the given [type] which gets an object's | |
| 323 * state by calling [getState], creates a new object by calling [construct] | |
| 324 * and sets the new object's state by calling [setNonEssentialState]. | |
| 325 */ | |
| 326 ClosureRule(this.type, this.getStateFunction, this.construct, | |
| 327 this.setNonEssentialState); | |
| 328 | |
| 329 bool appliesTo(object, Writer w) => object.runtimeType == type; | |
| 330 | |
| 331 getState(object) => getStateFunction(object); | |
| 332 | |
| 333 create(state) => construct(state); | |
| 334 | |
| 335 void setState(object, state) { | |
| 336 if (setNonEssentialState == null) return; | |
| 337 setNonEssentialState(object, state); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 /** | |
| 342 * This rule handles things we can't pass directly, but only by reference. | |
| 343 * If objects are listed in the namedObjects in the writer or serialization, | |
| 344 * it will save the name rather than saving the state. | |
| 345 */ | |
| 346 class NamedObjectRule extends SerializationRule { | |
| 347 /** | |
| 348 * Return true if this rule applies to the object. Checked by looking up | |
| 349 * in the namedObjects collection. | |
| 350 */ | |
| 351 bool appliesTo(object, Writer writer) { | |
| 352 return writer.hasNameFor(object); | |
| 353 } | |
| 354 | |
| 355 /** Extract the state of the named objects as just the object itself. */ | |
| 356 extractState(object, Function f, Writer writer) { | |
| 357 var result = [nameFor(object, writer)]; | |
| 358 f(result.first); | |
| 359 return result; | |
| 360 } | |
| 361 | |
| 362 /** When we flatten the state we save it as the name. */ | |
| 363 // TODO(alanknight): This seems questionable. In a truly flat format we may | |
| 364 // want to have extracted the name as a string first and flatten it into a | |
| 365 // reference to that. But that requires adding the Writer as a parameter to | |
| 366 // extractState, and I'm reluctant to add yet another parameter until | |
| 367 // proven necessary. | |
| 368 flatten(state, Writer writer) { | |
| 369 state[0] = writer._referenceFor(state[0]); | |
| 370 } | |
| 371 | |
| 372 /** Look up the named object and return it. */ | |
| 373 inflateEssential(state, Reader r) => | |
| 374 r.objectNamed(r.resolveReference(state.first)); | |
| 375 | |
| 376 /** Set any non-essential state on the object. For this rule, a no-op. */ | |
| 377 void inflateNonEssential(state, object, Reader r) {} | |
| 378 | |
| 379 /** Return the name for this object in the Writer. */ | |
| 380 String nameFor(object, Writer writer) => writer.nameFor(object); | |
| 381 } | |
| 382 | |
| 383 /** | |
| 384 * This rule handles the special case of Mirrors. It stores the mirror by its | |
| 385 * qualifiedName and attempts to look it up in both the namedObjects | |
| 386 * collection, or if it's not found there, by looking it up in the mirror | |
| 387 * system. When reading, the user is responsible for supplying the appropriate | |
| 388 * values in [Serialization.namedObjects] or in the [externals] paramter to | |
| 389 * [Serialization.read]. | |
| 390 */ | |
| 391 class MirrorRule extends NamedObjectRule { | |
| 392 bool appliesTo(object, Writer writer) => object is DeclarationMirror; | |
| 393 | |
| 394 String nameFor(DeclarationMirror object, Writer writer) => | |
| 395 MirrorSystem.getName(object.qualifiedName); | |
| 396 | |
| 397 inflateEssential(state, Reader r) { | |
| 398 var qualifiedName = r.resolveReference(state.first); | |
| 399 var lookupFull = r.objectNamed(qualifiedName, (x) => null); | |
| 400 if (lookupFull != null) return lookupFull; | |
| 401 var separatorIndex = qualifiedName.lastIndexOf("."); | |
| 402 var type = qualifiedName.substring(separatorIndex + 1); | |
| 403 var lookup = r.objectNamed(type, (x) => null); | |
| 404 if (lookup != null) return lookup; | |
| 405 var name = qualifiedName.substring(0, separatorIndex); | |
| 406 // This is very ugly. The library name for an unnamed library is its URI. | |
| 407 // That can't be constructed as a Symbol, so we can't use findLibrary. | |
| 408 // So follow one or the other path depending if it has a colon, which we | |
| 409 // assume is in any URI and can't be in a Symbol. | |
| 410 if (name.contains(":")) { | |
| 411 var uri = Uri.parse(name); | |
| 412 var libMirror = currentMirrorSystem().libraries[uri]; | |
| 413 var candidate = libMirror.declarations[new Symbol(type)]; | |
| 414 return candidate is ClassMirror ? candidate : null; | |
| 415 } else { | |
| 416 var symbol = new Symbol(name); | |
| 417 var typeSymbol = new Symbol(type); | |
| 418 for (var libMirror in currentMirrorSystem().libraries.values) { | |
| 419 if (libMirror.simpleName != symbol) continue; | |
| 420 var candidate = libMirror.declarations[typeSymbol]; | |
| 421 if (candidate != null && candidate is ClassMirror) return candidate; | |
| 422 } | |
| 423 return null; | |
| 424 } | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 /** | |
| 429 * This provides an abstract superclass for writing your own rules specific to | |
| 430 * a class. It makes some assumptions about behaviour, and so can have a | |
| 431 * simpler set of methods that need to be implemented in order to subclass it. | |
| 432 * | |
| 433 */ | |
| 434 abstract class CustomRule extends SerializationRule { | |
| 435 // TODO(alanknight): It would be nice if we could provide an implementation | |
| 436 // of appliesTo() here. If we add a type parameter to these classes | |
| 437 // we can "is" test against it, but we need to be able to rule out subclasses. | |
| 438 // => instance.runtimeType == T | |
| 439 // should work. | |
| 440 /** | |
| 441 * Return true if this rule applies to this object, in the context | |
| 442 * where we're writing it, false otherwise. | |
| 443 */ | |
| 444 bool appliesTo(instance, Writer w); | |
| 445 | |
| 446 /** | |
| 447 * Subclasses should implement this to return a list of the important fields | |
| 448 * in the object. The order of the fields doesn't matter, except that the | |
| 449 * create and setState methods need to know how to use it. | |
| 450 */ | |
| 451 List getState(instance); | |
| 452 | |
| 453 /** | |
| 454 * Given a [List] of the object's [state], re-create the object. This should | |
| 455 * do the minimum needed to create the object, just calling the constructor. | |
| 456 * Setting the remaining state of the object should be done in the [setState] | |
| 457 * method, which will be called only once all the objects are created, so | |
| 458 * it won't cause problems with cycles. | |
| 459 */ | |
| 460 create(List state); | |
| 461 | |
| 462 /** | |
| 463 * Set any state in [object] which wasn't set in the constructor. Between | |
| 464 * this method and [create] all of the information in [state] should be set | |
| 465 * in the new object. | |
| 466 */ | |
| 467 void setState(object, List state); | |
| 468 | |
| 469 extractState(instance, Function f, Writer w) { | |
| 470 var state = getState(instance); | |
| 471 for (var each in values(state)) { | |
| 472 f(each); | |
| 473 } | |
| 474 return state; | |
| 475 } | |
| 476 | |
| 477 inflateEssential(state, Reader r) => create(_lazy(state, r)); | |
| 478 | |
| 479 void inflateNonEssential(state, object, Reader r) { | |
| 480 setState(object, _lazy(state, r)); | |
| 481 } | |
| 482 | |
| 483 // We don't want to have to make the end user tell us how long the list is | |
| 484 // separately, so write it out for each object, even though they're all | |
| 485 // expected to be the same length. | |
| 486 bool get hasVariableLengthEntries => true; | |
| 487 } | |
| 488 | |
| 489 /** A hard-coded rule for serializing Symbols. */ | |
| 490 class SymbolRule extends CustomRule { | |
| 491 bool appliesTo(instance, _) => instance is Symbol; | |
| 492 getState(instance) => [MirrorSystem.getName(instance)]; | |
| 493 create(state) => new Symbol(state[0]); | |
| 494 void setState(symbol, state) {} | |
| 495 int get dataLength => 1; | |
| 496 bool get hasVariableLengthEntries => false; | |
| 497 } | |
| 498 | |
| 499 /** A hard-coded rule for DateTime. */ | |
| 500 class DateTimeRule extends CustomRule { | |
| 501 bool appliesTo(instance, _) => instance is DateTime; | |
| 502 List getState(DateTime date) => [date.millisecondsSinceEpoch, date.isUtc]; | |
| 503 DateTime create(List state) => | |
| 504 new DateTime.fromMillisecondsSinceEpoch(state[0], isUtc: state[1]); | |
| 505 void setState(date, state) {} | |
| 506 // Let the system know we don't have to store a length for these. | |
| 507 int get dataLength => 2; | |
| 508 bool get hasVariableLengthEntries => false; | |
| 509 } | |
| 510 | |
| 511 /** Create a lazy list/map that will inflate its items on demand in [r]. */ | |
| 512 _lazy(l, Reader r) { | |
| 513 if (l is List) return new _LazyList(l, r); | |
| 514 if (l is Map) return new _LazyMap(l, r); | |
| 515 throw new SerializationException("Invalid type: must be Map or List - $l"); | |
| 516 } | |
| 517 | |
| 518 /** | |
| 519 * This provides an implementation of Map that wraps a list which may | |
| 520 * contain references to (potentially) non-inflated objects. If these | |
| 521 * are accessed it will inflate them. This allows us to pass something that | |
| 522 * looks like it's just a list of objects to a [CustomRule] without needing | |
| 523 * to inflate all the references in advance. | |
| 524 */ | |
| 525 class _LazyMap implements Map { | |
| 526 _LazyMap(this._raw, this._reader); | |
| 527 | |
| 528 final Map _raw; | |
| 529 final Reader _reader; | |
| 530 | |
| 531 // This is the only operation that really matters. | |
| 532 operator [](x) => _reader.inflateReference(_raw[x]); | |
| 533 | |
| 534 int get length => _raw.length; | |
| 535 bool get isEmpty => _raw.isEmpty; | |
| 536 bool get isNotEmpty => _raw.isNotEmpty; | |
| 537 Iterable get keys => _raw.keys; | |
| 538 bool containsKey(x) => _raw.containsKey(x); | |
| 539 | |
| 540 // These operations will work, but may be expensive, and are probably | |
| 541 // best avoided. | |
| 542 get _inflated => mapValues(_raw, _reader.inflateReference); | |
| 543 bool containsValue(x) => _inflated.containsValue(x); | |
| 544 Iterable get values => _inflated.values; | |
| 545 void forEach(f) => _inflated.forEach(f); | |
| 546 | |
| 547 // These operations are all invalid | |
| 548 _throw() { | |
| 549 throw new UnsupportedError("Not modifiable"); | |
| 550 } | |
| 551 operator []=(x, y) => _throw(); | |
| 552 putIfAbsent(x, y) => _throw(); | |
| 553 bool remove(x) => _throw(); | |
| 554 void clear() => _throw(); | |
| 555 void addAll(Map other) => _throw(); | |
| 556 } | |
| 557 | |
| 558 /** | |
| 559 * This provides an implementation of List that wraps a list which may | |
| 560 * contain references to (potentially) non-inflated objects. If these | |
| 561 * are accessed it will inflate them. This allows us to pass something that | |
| 562 * looks like it's just a list of objects to a [CustomRule] without needing | |
| 563 * to inflate all the references in advance. | |
| 564 */ | |
| 565 class _LazyList extends ListBase { | |
| 566 _LazyList(this._raw, this._reader); | |
| 567 | |
| 568 final List _raw; | |
| 569 final Reader _reader; | |
| 570 | |
| 571 operator [](int x) => _reader.inflateReference(_raw[x]); | |
| 572 int get length => _raw.length; | |
| 573 | |
| 574 void set length(int value) => _throw(); | |
| 575 | |
| 576 void operator []=(int index, value) => _throw(); | |
| 577 | |
| 578 void _throw() { | |
| 579 throw new UnsupportedError("Not modifiable"); | |
| 580 } | |
| 581 } | |
| OLD | NEW |