Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |