Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(25)

Side by Side Diff: pkg/serialization/lib/src/serialization_rule.dart

Issue 11553012: Better ability to have hand-written custom rules and various cleanups. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 // TODO(alanknight): We should have an example and tests for subclassing 7 // TODO(alanknight): We should have an example and tests for subclassing
8 // serialization rule rather than using the hard-coded ClosureToMap rule. And 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. 9 // possibly an abstract superclass that's designed to be subclassed that way.
10 /** 10 /**
(...skipping 15 matching lines...) Expand all
26 /** 26 /**
27 * Rules belong uniquely to a particular Serialization instance, and can 27 * Rules belong uniquely to a particular Serialization instance, and can
28 * be identified within it by number. 28 * be identified within it by number.
29 */ 29 */
30 void set number(x) { 30 void set number(x) {
31 if (_number != null) throw 31 if (_number != null) throw
32 new SerializationException("Rule numbers cannot be changed, once set"); 32 new SerializationException("Rule numbers cannot be changed, once set");
33 _number = x; 33 _number = x;
34 } 34 }
35 35
36 /** Return true if this rule applies to this object, false otherwise. */ 36 /**
37 bool appliesTo(object); 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);
38 41
39 /** 42 /**
40 * This extracts the state from the object, calling [f] for each value 43 * This extracts the state from the object, calling [f] for each value
41 * as it is extracted, and returning an object representing the whole 44 * as it is extracted, and returning an object representing the whole
42 * state at the end. The state that results will still have direct 45 * state at the end. The state that results will still have direct
43 * pointers to objects, rather than references. 46 * pointers to objects, rather than references.
44 */ 47 */
45 Object extractState(object, void f(value)); 48 extractState(object, void f(value));
46 49
47 /** 50 /**
48 * Given the variables representing the state of an object, flatten it 51 * Given the variables representing the state of an object, flatten it
49 * by turning object pointers into Reference objects where needed. This 52 * by turning object pointers into Reference objects where needed. This
50 * destructively modifies the state object. 53 * destructively modifies the state object.
51 * 54 *
52 * This has a default implementation which assumes that object is indexable, 55 * This has a default implementation which assumes that object is indexable,
53 * so either conforms to Map or List. Subclasses may override to do something 56 * so either conforms to Map or List. Subclasses may override to do something
54 * different. 57 * different.
55 */ 58 */
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 inflateEssential(state, Reader reader); 94 inflateEssential(state, Reader reader);
92 95
93 /** 96 /**
94 * The [object] has already been created. Set any of its non-essential 97 * The [object] has already been created. Set any of its non-essential
95 * variables from the representation in [state]. Where there are references 98 * variables from the representation in [state]. Where there are references
96 * to other objects they are resolved in the context of [reader]. 99 * to other objects they are resolved in the context of [reader].
97 */ 100 */
98 inflateNonEssential(state, object, Reader reader); 101 inflateNonEssential(state, object, Reader reader);
99 102
100 /** 103 /**
101 * If we have an object [o] as part of our state, should we represent that 104 * If we have [object] as part of our state, should we represent that
102 * directly, or should we make a reference for it. By default we use a 105 * directly, or should we make a reference for it. By default we use a
103 * reference for everything. 106 * reference for everything.
104 */ 107 */
105 bool shouldUseReferenceFor(Object o, Writer w) => true; 108 bool shouldUseReferenceFor(object, Writer w) => true;
106 109
107 /** 110 /**
108 * This writes the data from our internal representation into a List. 111 * This writes the data from our internal representation into a List.
109 * It is used in order to write to a flat format, and is likely to be 112 * It is used in order to write to a flat format, and is likely to be
110 * folded into a more general mechanism for supporting different output 113 * folded into a more general mechanism for supporting different output
111 * formats. 114 * formats.
112 */ 115 */
113 // TODO(alanknight): This really shouldn't exist, but is a temporary measure 116 // TODO(alanknight): This really shouldn't exist, but is a temporary measure
114 // for writing to a a flat format until that's more fleshed out. It takes 117 // for writing to a a flat format until that's more fleshed out. It takes
115 // the internal representation of the rule's state, which is particularly 118 // the internal representation of the rule's state, which is particularly
116 // bad. The default implementation treats the ruleData as a List of Lists 119 // bad. The default implementation treats the ruleData as a List of Lists
117 // of references. 120 // of references.
118 void dumpStateInto(List ruleData, List target) { 121 void dumpStateInto(List ruleData, List target) {
119 // Needing the intermediate is also bad for performance, but tricky 122 // Needing the intermediate is also bad for performance, but tricky
120 // to do otherwise without a mechanism to precalculate the size. 123 // to do otherwise without a mechanism to precalculate the size.
121 var intermediate = new List(); 124 var intermediate = new List();
122 var totalLength = 0; 125 var totalLength = 0;
123 for (var eachList in ruleData) { 126 for (var eachList in ruleData) {
124 // TODO(alanknight): Abstract this out better, this really won't scale. 127 if (writeLengthInFlatFormat())
Jennifer Messerly 2012/12/12 20:38:28 fwiw, I think our style is usually either to have
Alan Knight 2012/12/12 21:19:33 Done.
125 if (this is ListRule)
126 intermediate.add(eachList.length); 128 intermediate.add(eachList.length);
127 for (var eachRef in eachList) { 129 for (var eachRef in eachList) {
128 if (eachRef == null) { 130 if (eachRef == null) {
129 intermediate..add(null)..add(null); 131 intermediate..add(null)..add(null);
130 } else { 132 } else {
131 eachRef.writeToList(intermediate); 133 eachRef.writeToList(intermediate);
132 } 134 }
133 } 135 }
134 } 136 }
135 target.addAll(intermediate); 137 target.addAll(intermediate);
136 } 138 }
137 139
138 /** 140 /**
141 * Return true if this rule writes a length value before each entry in
142 * the flat format. Return false if the results are fixed length.
143 */
144 // TODO(alanknight): This should probably go away with more general formats.
145 bool writeLengthInFlatFormat() => false;
Jennifer Messerly 2012/12/12 20:38:28 make a getter?
Alan Knight 2012/12/12 21:19:33 Done.
146
147 /**
139 * The inverse of dumpStateInto, this reads the rule's state from an 148 * The inverse of dumpStateInto, this reads the rule's state from an
140 * iterator in a flat format. 149 * iterator in a flat format.
141 */ 150 */
142 pullStateFrom(Iterator stream); 151 pullStateFrom(Iterator stream) {
152 var numberOfEntries = stream.next();
153 var ruleData = new List();
154 for (var i = 0; i < numberOfEntries; i++) {
155 var subLength = dataLengthIn(stream);
156 var subList = new List();
Jennifer Messerly 2012/12/12 20:38:28 personally I prefer [] form
Alan Knight 2012/12/12 21:19:33 Done.
157 ruleData.add(subList);
158 for (var j = 0; j < subLength; j++) {
159 var a = stream.next();
160 var b = stream.next();
161 if (!(a is int)) {
162 // This wasn't a reference, just use the first object as a literal.
163 // particularly used for the case of null.
164 subList.add(a);
165 } else {
166 subList.add(new Reference(this, a, b));
167 }
168 }
169 }
170 return ruleData;
171 }
172
173 /**
174 * Return the length of the list of data we expect to see on a particular
175 * iterator in a flat format. This may have been encoded in the stream if we
176 * are variable length, or it may be constant. Note that this is expressed in
177 *
178 */
179 dataLengthIn(Iterator stream) =>
180 writeLengthInFlatFormat() ? stream.next() : dataLength();
181
182 /**
183 * If the data is fixed length, return it here. Unused in the non-flat
184 * format, or if the data is variable length.
185 */
186 int dataLength() => 0;
Jennifer Messerly 2012/12/12 20:38:28 getter?
Alan Knight 2012/12/12 21:19:33 Done. Also renamed locals that were shadowing this
143 } 187 }
144 188
145 /** 189 /**
146 * This rule handles things that implement List. It will recreate them as 190 * This rule handles things that implement List. It will recreate them as
147 * whatever the default implemenation of List is on the target platform. 191 * whatever the default implemenation of List is on the target platform.
148 */ 192 */
149 class ListRule extends SerializationRule { 193 class ListRule extends SerializationRule {
150 194
151 appliesTo(object) => object is List; 195 appliesTo(object, Writer w) => object is List;
152 196
153 state(List list) => new List.from(list); 197 state(List list) => new List.from(list);
154 198
155 List extractState(List list, f) { 199 List extractState(List list, f) {
156 var result = new List(); 200 var result = new List();
157 for (var each in list) { 201 for (var each in list) {
158 result.add(each); 202 result.add(each);
159 f(each); 203 f(each);
160 } 204 }
161 return result; 205 return result;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 // This wasn't a reference, just use the first object as a literal. 243 // This wasn't a reference, just use the first object as a literal.
200 // particularly used for the case of null. 244 // particularly used for the case of null.
201 subList.add(a); 245 subList.add(a);
202 } else { 246 } else {
203 subList.add(new Reference(this, a, b)); 247 subList.add(new Reference(this, a, b));
204 } 248 }
205 } 249 }
206 } 250 }
207 return ruleData; 251 return ruleData;
208 } 252 }
253
254 /**
255 * Return true because we need to write the length of each list in the flat
256 * format. */
257 bool writeLengthInFlatFormat() => true;
258
259 /** Return the length of the next list when reading the flat format. */
260 int dataLengthIn(Iterator stream) => stream.next();
209 } 261 }
210 262
211 /** 263 /**
212 * This is a subclass of ListRule where all of the list's contents are 264 * This is a subclass of ListRule where all of the list's contents are
213 * considered essential state. This is needed if an object X contains a List L, 265 * considered essential state. This is needed if an object X contains a List L,
214 * but it expects L's contents to be fixed when X's constructor is called. 266 * but it expects L's contents to be fixed when X's constructor is called.
215 */ 267 */
216 class ListRuleEssential extends ListRule { 268 class ListRuleEssential extends ListRule {
217 269
218 /** Create the new List and also inflate all of its contents. */ 270 /** Create the new List and also inflate all of its contents. */
219 inflateEssential(List state, Reader r) { 271 inflateEssential(List state, Reader r) {
220 var object = super.inflateEssential(state, r); 272 var object = super.inflateEssential(state, r);
221 populateContents(state, object, r); 273 populateContents(state, object, r);
222 return object; 274 return object;
223 } 275 }
224 276
225 /** Does nothing, because all the work has been done in inflateEssential. */ 277 /** Does nothing, because all the work has been done in inflateEssential. */
226 inflateNonEssential(state, newList, reader) {} 278 inflateNonEssential(state, newList, reader) {}
227 279
228 bool get mustBePrimary => true; 280 bool get mustBePrimary => true;
229 } 281 }
230 282
231 /** 283 /**
232 * This rule handles primitive types, defined as those that we can normally 284 * This rule handles primitive types, defined as those that we can normally
233 * represent directly in the output format. We hard-code that to mean 285 * represent directly in the output format. We hard-code that to mean
234 * num, String, and bool. 286 * num, String, and bool.
235 */ 287 */
236 class PrimitiveRule extends SerializationRule { 288 class PrimitiveRule extends SerializationRule {
237 appliesTo(object) { 289 appliesTo(object, Writer w) {
238 return isPrimitive(object); 290 return isPrimitive(object);
239 } 291 }
240 extractState(object, Function f) => object; 292 extractState(object, Function f) => object;
241 void flatten(object, Writer writer) {} 293 void flatten(object, Writer writer) {}
242 inflateEssential(state, Reader r) => state; 294 inflateEssential(state, Reader r) => state;
243 inflateNonEssential(object, _, Reader r) {} 295 inflateNonEssential(object, _, Reader r) {}
244 296
245 /** Indicate whether we should save pointers to this object as references 297 /**
298 * Indicate whether we should save pointers to this object as references
246 * or store the object directly. For primitives this depends on the format, 299 * or store the object directly. For primitives this depends on the format,
247 * so we delegate to the writer. 300 * so we delegate to the writer.
248 */ 301 */
249 bool shouldUseReferenceFor(Object o, Writer w) => 302 bool shouldUseReferenceFor(object, Writer w) =>
250 w.shouldUseReferencesForPrimitives; 303 w.shouldUseReferencesForPrimitives;
251 304
252 /** 305 /**
253 * This writes the data from our internal representation into a List. 306 * This writes the data from our internal representation into a List.
254 * It is used in order to write to a flat format, and is likely to be 307 * It is used in order to write to a flat format, and is likely to be
255 * folded into a more general mechanism for supporting different output 308 * folded into a more general mechanism for supporting different output
256 * formats. For primitives, the ruleData is our list of all the 309 * formats. For primitives, the ruleData is our list of all the
257 * primitives and just add it into the target. 310 * primitives and just add it into the target.
258 */ 311 */
259 void dumpStateInto(List ruleData, List target) { 312 void dumpStateInto(List ruleData, List target) {
260 target.addAll(ruleData); 313 target.addAll(ruleData);
261 } 314 }
262 315
263 /** 316 /**
264 * When reading from a flat format we are given [stream] and need to pull as 317 * When reading from a flat format we are given [stream] and need to pull as
265 * much data from it as we need. Our format is that we have an integer N 318 * much data from it as we need. Our format is that we have an integer N
266 * indicating the number of objects and then N simple objects. 319 * indicating the number of objects and then N simple objects.
267 */ 320 */
268 pullStateFrom(Iterator stream) { 321 pullStateFrom(Iterator stream) {
269 var dataLength = stream.next(); 322 var dataLength = stream.next();
270 var ruleData = new List(); 323 var ruleData = new List();
271 for (var i = 0; i < dataLength; i++) { 324 for (var i = 0; i < dataLength; i++) {
272 ruleData.add(stream.next()); 325 ruleData.add(stream.next());
273 } 326 }
274 return ruleData; 327 return ruleData;
275 } 328 }
276 } 329 }
277 330
278 /** Helper function for PrimitiveRule to tell which objects it applies to. */ 331 /** Helper function for PrimitiveRule to tell which objects it applies to. */
279 bool isPrimitive(Object object) { 332 bool isPrimitive(object) {
280 return object is num || object is String || object is bool; 333 return object is num || object is String || object is bool;
281 } 334 }
282 335
283 /** Typedef for the object construction closure used in ClosureToMapRule. */ 336 /** Typedef for the object construction closure used in ClosureRule. */
284 typedef Object ConstructType(Map m); 337 typedef ConstructType(Map m);
285 338
286 /** Typedef for the state-getting closure used in ClosureToMapRule. */ 339 /** Typedef for the state-getting closure used in ClosureToMapRule. */
287 typedef Map<String, Object> GetStateType(Object o); 340 typedef Map<String, dynamic> GetStateType(object);
288 341
289 /** Typedef for the state-setting closure used in ClosureToMapRule. */ 342 /** Typedef for the state-setting closure used in ClosureToMapRule. */
290 typedef void NonEssentialStateType(Object o, Map m); 343 typedef void NonEssentialStateType(object, Map m);
291 344
292 /** 345 /**
293 * This is a rule where the extraction and creation are hard-coded as 346 * This is a rule where the extraction and creation are hard-coded as
294 * closures. The result is expected to be a map indexed by field name. 347 * closures. The result is expected to be a map indexed by field name.
295 */ 348 */
296 class ClosureToMapRule extends SerializationRule { 349 class ClosureRule extends CustomRule {
297 350
298 /** The runtimeType of objects that this rule applies to. Used in appliesTo.*/ 351 /** The runtimeType of objects that this rule applies to. Used in appliesTo.*/
299 final Type type; 352 final Type type;
300 353
301 /** The function for constructing new objects when reading. */ 354 /** The function for constructing new objects when reading. */
302 ConstructType construct; 355 ConstructType construct;
303 356
304 /** The function for returning an object's state as a Map. */ 357 /** The function for returning an object's state as a Map. */
305 GetStateType getState; 358 GetStateType getStateFunction;
306 359
307 /** The function for setting an object's state from a Map. */ 360 /** The function for setting an object's state from a Map. */
308 NonEssentialStateType setNonEssentialState; 361 NonEssentialStateType setNonEssentialState;
309 362
310 /** 363 /**
311 * Create a ClosureToMapRule for the given [type] which gets an object's 364 * Create a ClosureToMapRule for the given [type] which gets an object's
312 * state by calling [getState], creates a new object by calling [construct] 365 * state by calling [getState], creates a new object by calling [construct]
313 * and sets the new object's state by calling [setNonEssentialState]. 366 * and sets the new object's state by calling [setNonEssentialState].
314 */ 367 */
315 ClosureToMapRule(this.type, this.getState, this.construct, 368 ClosureRule(this.type, this.getStateFunction, this.construct,
316 this.setNonEssentialState); 369 this.setNonEssentialState);
317 370
318 /** 371 bool appliesTo(object, Writer w) => object.runtimeType == type;
319 * If we deserialize a ClosureToMapRule we can't actually use it, because 372
320 * we don't have the closures, so generate a stub that just returns the 373 getState(object) => getStateFunction(object);
321 * raw state object. 374
322 */ 375 create(state) => construct(state);
323 ClosureToMapRule.stub(this.type) { 376
324 getState = (x) { throw new SerializationException( 377 setState(object, state) {
325 'Closures cannot be serialized'); }; 378 if (setNonEssentialState == null) return;
326 construct = (state) => state; 379 setNonEssentialState(object, state);
327 setNonEssentialState = (object, state) {}; 380 }
328 } 381 }
329 382
330 bool appliesTo(object) => object.runtimeType == type; 383 /**
331 384 * This rule handles things we can't pass directly, but only by reference.
332 extractState(object, Function f) { 385 * If objects are listed in the namedObjects in the writer or serialization,
333 Map state = getState(object); 386 * it will save the name rather than saving the state.
334 values(state).forEach(f); 387 */
388 class NamedObjectRule extends SerializationRule {
389 /**
390 * Return true if this rule applies to the object. Checked by looking up
391 * in the namedObjects collection.
392 */
393 bool appliesTo(object, Writer writer) {
394 return writer.hasNameFor(object);
395 }
396
397 /** Extract the state of the named objects as just the object itself. */
398 extractState(object, Function f) => [object];
399
400 /** When we flatten the state we save it as the name. */
401 // TODO(alanknight): This seems questionable. In a truly flat format we may
402 // want to have extracted the name as a string first and flatten it into a
403 // reference to that. But that requires adding the Writer as a parameter to
404 // extractState, and I'm reluctant to add yet another parameter until
405 // proven necessary.
406 void flatten(state, Writer writer) {
407 state[0] = nameFor(state.first, writer);
408 }
409
410 /** Look up the named object and return it. */
411 inflateEssential(state, Reader r) => r.objectNamed(state.first);
412
413 /** Set any non-essential state on the object. For this rule, a no-op. */
414 inflateNonEssential(state, object, Reader r) {}
415
416 /** Return the name for this object in the Writer. */
417 nameFor(object, Writer writer) => writer.nameFor(object);
418 }
419
420 /**
421 * This rule handles the special case of Mirrors, restricted to those that
422 * have a simpleName. It knows that it applies to any such mirror and
423 * automatically uses its simpleName as the key into the namedObjects.
424 * When reading, the user is still responsible for adding the appropriate
425 * mirrors to namedObject.
426 */
427 class MirrorRule extends NamedObjectRule {
428 bool appliesTo(object, Writer writer) => object is DeclarationMirror;
429 nameFor(DeclarationMirror object, Writer writer) => object.simpleName;
430 }
431
432 /**
433 * This provides an abstract superclass for writing your own rules specific to
434 * a class. It makes some assumptions about behaviour, and so can have a
435 * simpler set of methods that need to be implemented in order to subclass it.
436 *
437 */
438 abstract class CustomRule extends SerializationRule {
439 // TODO(alanknight): It would be nice if we could provide an implementation
440 // of appliesTo() here. If we add a type parameter to these classes
441 // we can "is" test against it, but we need to be able to rule out subclasses.
442 // => instance.runtimeType == T
443 // should work.
444 /**
445 * Return true if this rule applies to this object, in the context
446 * where we're writing it, false otherwise.
447 */
448 bool appliesTo(instance, Writer w);
449
450 /**
451 * Subclasses should implement this to return a list of the important fields
452 * in the object. The order of the fields doesn't matter, except that the
453 * create and setState methods need to know how to use it.
454 */
455 List getState(instance);
456
457 /**
458 * Given a [List] of the object's [state], re-create the object. This should
459 * do the minimum needed to create the object, just calling the constructor.
460 * Setting the remaining state of the object should be done in the [setState]
461 * method, which will be called only once all the objects are created, so
462 * it won't cause problems with cycles.
463 */
464 create(List state);
465
466 /**
467 * Set any state in [object] which wasn't set in the constructor. Between
468 * this method and [create] all of the information in [state] should be set
469 * in the new object.
470 */
471 void setState(object, List state);
472
473 extractState(instance, Function f) {
474 var state = getState(instance);
475 for (var each in values(state)) {
476 f(each);
477 }
335 return state; 478 return state;
336 } 479 }
337 480
338 // TODO(alanknight): We're inflating twice here. How to avoid doing 481 inflateEssential(state, Reader r) => create(_lazy(state, r));
339 // that without giving the user even more stuff to specify. 482
340 // Worse than that, by inflating everything in advance, we are are 483 void inflateNonEssential(state, object, Reader r) =>
341 // forcing all the state to be essential. 484 setState(object, _lazy(state, r));
342 Object inflateEssential(Map<String, Object> state, Reader r) { 485
343 var inflated = values(state).map((x) => r.inflateReference(x)); 486 // We don't want to have to make the end user tell us how long the list is
344 return construct(inflated); 487 // separately, so write it out for each object, even though they're all
345 } 488 // expected to be the same length.
346 489 writeLengthInFlatFormat() => true;
347 void inflateNonEssential(state, object, Reader r) { 490 }
348 if (setNonEssentialState == null) return; 491
349 var inflated = values(state).map((x) => r.inflateReference(x)); 492 /** Create a lazy list that will inflate its items on demand in [r]. */
350 setNonEssentialState(inflated, object); 493 _lazy(l, Reader r)
351 } 494 => (l is List) ? new _LazyList(l, r) : new _LazyMap(l, r);
Jennifer Messerly 2012/12/12 20:38:28 should this check for primitive types too?
Alan Knight 2012/12/12 21:19:33 It really expects this to be either a List or a Ma
352 } 495
353 496 /**
354 /** 497 * This provides an implementation of Map that wraps a list which may
355 * This rule handles things we can't pass directly, but only by reference. 498 * contain references to (potentially) non-inflated objects. If these
356 * It extracts an identifier we can use to pass them. 499 * are accessed it will inflate them. This allows us to pass something that
357 */ 500 * looks like it's just a list of objects to a [CustomRule] without needing
358 class ClassMirrorRule extends SerializationRule { 501 * to inflate all the references in advance.
359 // TODO(alanknight): This probably generalizes to any named object. 502 */
360 bool appliesTo(object) { 503 class _LazyMap implements Map {
361 return object is ClassMirror; 504 _LazyMap(this.raw, this.reader);
362 } 505
363 extractState(object, Function f) => f(object.simpleName); 506 Map raw;
Jennifer Messerly 2012/12/12 20:38:28 make these private?
Alan Knight 2012/12/12 21:19:33 Done.
364 void flatten(object, Writer writer) {} 507 Reader reader;
365 inflateEssential(state, Reader r) => r.externalObjectNamed(state); 508
366 inflateNonEssential(state, object, Reader r) {} 509 // This is the only operation that really matters.
510 operator [](x) => reader.inflateReference(raw[x]);
511
512 int get length => raw.length;
513 bool get isEmpty => raw.isEmpty;
514 List get keys => raw.keys;
515 bool containsKey(x) => raw.containsKey(x);
516
517 // These operations will work, but may be expensive, and are probably
518 // best avoided.
519 get _inflated => keysAndValues(raw).map(reader.inflateReference);
520 bool containsValue(x) => _inflated.containsValue(x);
521 List get values => _inflated.values;
522 void forEach(f) => _inflated.forEach(f);
523
524 // These operations are all invalid
525 _throw() => throw new UnsupportedError("Not modifiable");
526 operator []=(x, y) => _throw();
527 putIfAbsent(x, y) => _throw();
528 remove(x) => _throw();
529 clear() => _throw();
530 }
531
532 /**
533 * This provides an implementation of List that wraps a list which may
534 * contain references to (potentially) non-inflated objects. If these
535 * are accessed it will inflate them. This allows us to pass something that
536 * looks like it's just a list of objects to a [CustomRule] without needing
537 * to inflate all the references in advance.
538 */
539 class _LazyList implements List {
540 _LazyList(this.raw, this.reader);
541
542 List raw;
543 Reader reader;
544
545 // This is the only operation that really matters.
546 operator [](x) => reader.inflateReference(raw[x]);
547
548 int get length => raw.length;
549 bool get isEmpty => raw.isEmpty;
550 get first => reader.inflateReference(raw.first);
551 get last => reader.inflateReference(raw.last);
552
553 // These operations will work, but may be expensive, and are probably
554 // best avoided.
555 get _inflated => raw.map(reader.inflateReference);
556 map(f) => _inflated.map(f);
557 filter(f) => _inflated.filter(f);
558 bool contains(element) => _inflated.filter(element);
559 forEach(f) => _inflated.forEach(f);
560 reduce(x, f) => _inflated.reduce(x, f);
561 every(f) => _inflated(f);
562 some(f) => _inflated(f);
563 iterator() => _inflated.iterator();
564 indexOf(x, [pos = 0]) => _inflated.indexOf(x);
565 lastIndexOf(x, [pos]) => _inflated.lastIndexOf(x);
566
567 // These operations are all invalid
568 _throw() => throw new UnsupportedError("Not modifiable");
569 operator []=(x, y) => _throw();
570 add(x) => _throw();
571 addLast(x) => _throw();
572 addAll(x) => _throw();
573 sort([f]) => _throw();
574 clear() => _throw();
575 removeAt(x) => _throw();
576 removeLast() => _throw();
577 getRange(x, y) => _throw();
578 setRange(x, y, z, [a]) => _throw();
579 removeRange(x, y) => _throw();
580 insertRange(x, y, [z]) => _throw();
581 void set length(x) => _throw();
367 } 582 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698