| 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 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 95 | 95 |
| 96 /** | 96 /** |
| 97 * 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 |
| 98 * variables from the representation in [state]. Where there are references | 98 * variables from the representation in [state]. Where there are references |
| 99 * to other objects they are resolved in the context of [reader]. | 99 * to other objects they are resolved in the context of [reader]. |
| 100 */ | 100 */ |
| 101 inflateNonEssential(state, object, Reader reader); | 101 inflateNonEssential(state, object, Reader reader); |
| 102 | 102 |
| 103 /** | 103 /** |
| 104 * If we have [object] as part of our state, should we represent that | 104 * If we have [object] as part of our state, should we represent that |
| 105 * 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, true. |
| 106 * reference for everything. | 106 * This may also delegate to [writer]. |
| 107 */ | 107 */ |
| 108 bool shouldUseReferenceFor(object, Writer w) => true; | 108 bool shouldUseReferenceFor(object, Writer writer) => true; |
| 109 | 109 |
| 110 /** | 110 /** |
| 111 * This writes the data from our internal representation into a List. | 111 * Return true if the data this rule returns is variable length, so a |
| 112 * It is used in order to write to a flat format, and is likely to be | 112 * length needs to be written for it if the format requires that. Return |
| 113 * folded into a more general mechanism for supporting different output | 113 * false if the results are always the same length. |
| 114 * formats. | |
| 115 */ | 114 */ |
| 116 // TODO(alanknight): This really shouldn't exist, but is a temporary measure | 115 bool get hasVariableLengthEntries => true; |
| 117 // for writing to a a flat format until that's more fleshed out. It takes | |
| 118 // the internal representation of the rule's state, which is particularly | |
| 119 // bad. The default implementation treats the ruleData as a List of Lists | |
| 120 // of references. | |
| 121 void dumpStateInto(List ruleData, List target) { | |
| 122 // Needing the intermediate is also bad for performance, but tricky | |
| 123 // to do otherwise without a mechanism to precalculate the size. | |
| 124 var intermediate = new List(); | |
| 125 var totalLength = 0; | |
| 126 for (var eachList in ruleData) { | |
| 127 if (writeLengthInFlatFormat) { | |
| 128 intermediate.add(eachList.length); | |
| 129 } | |
| 130 for (var eachRef in eachList) { | |
| 131 if (eachRef == null) { | |
| 132 intermediate..add(null)..add(null); | |
| 133 } else { | |
| 134 eachRef.writeToList(intermediate); | |
| 135 } | |
| 136 } | |
| 137 } | |
| 138 target.addAll(intermediate); | |
| 139 } | |
| 140 | 116 |
| 141 /** | 117 /** |
| 142 * Return true if this rule writes a length value before each entry in | 118 * If the data is fixed length, return it here. The format may or may not |
| 143 * the flat format. Return false if the results are fixed length. | 119 * make use of this, depending on whether it already has enough information |
| 144 */ | 120 * to determine the length on its own. If [hasVariableLengthEntries] is true |
| 145 // TODO(alanknight): This should probably go away with more general formats. | 121 * this is ignored. |
| 146 bool get writeLengthInFlatFormat => false; | |
| 147 | |
| 148 /** | |
| 149 * The inverse of dumpStateInto, this reads the rule's state from an | |
| 150 * iterator in a flat format. | |
| 151 */ | |
| 152 pullStateFrom(Iterator stream) { | |
| 153 stream.moveNext(); | |
| 154 var numberOfEntries = stream.current; | |
| 155 var ruleData = new List(); | |
| 156 for (var i = 0; i < numberOfEntries; i++) { | |
| 157 var subLength = dataLengthIn(stream); | |
| 158 var subList = []; | |
| 159 ruleData.add(subList); | |
| 160 for (var j = 0; j < subLength; j++) { | |
| 161 stream.moveNext(); | |
| 162 var a = stream.current; | |
| 163 stream.moveNext(); | |
| 164 var b = stream.current; | |
| 165 if (a is! int) { | |
| 166 // This wasn't a reference, just use the first object as a literal. | |
| 167 // particularly used for the case of null. | |
| 168 subList.add(a); | |
| 169 } else { | |
| 170 subList.add(new Reference(this, a, b)); | |
| 171 } | |
| 172 } | |
| 173 } | |
| 174 return ruleData; | |
| 175 } | |
| 176 | |
| 177 /** | |
| 178 * Return the length of the list of data we expect to see on a particular | |
| 179 * iterator in a flat format. This may have been encoded in the stream if we | |
| 180 * are variable length, or it may be constant. Returns null if the [Iterator] | |
| 181 * is empty. | |
| 182 */ | |
| 183 dataLengthIn(Iterator stream) => | |
| 184 writeLengthInFlatFormat ? (stream..moveNext()).current : dataLength; | |
| 185 | |
| 186 /** | |
| 187 * If the data is fixed length, return it here. Unused in the non-flat | |
| 188 * format, or if the data is variable length. | |
| 189 */ | 122 */ |
| 190 int get dataLength => 0; | 123 int get dataLength => 0; |
| 191 } | 124 } |
| 192 | 125 |
| 193 /** | 126 /** |
| 194 * This rule handles things that implement List. It will recreate them as | 127 * This rule handles things that implement List. It will recreate them as |
| 195 * whatever the default implemenation of List is on the target platform. | 128 * whatever the default implemenation of List is on the target platform. |
| 196 */ | 129 */ |
| 197 class ListRule extends SerializationRule { | 130 class ListRule extends SerializationRule { |
| 198 | 131 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 216 inflateNonEssential(List state, List newList, Reader r) { | 149 inflateNonEssential(List state, List newList, Reader r) { |
| 217 populateContents(state, newList, r); | 150 populateContents(state, newList, r); |
| 218 } | 151 } |
| 219 | 152 |
| 220 void populateContents(List state, List newList, Reader r) { | 153 void populateContents(List state, List newList, Reader r) { |
| 221 for(var each in state) { | 154 for(var each in state) { |
| 222 newList.add(r.inflateReference(each)); | 155 newList.add(r.inflateReference(each)); |
| 223 } | 156 } |
| 224 } | 157 } |
| 225 | 158 |
| 226 /** | 159 bool get hasVariableLengthEntries => true; |
| 227 * When reading from a flat format we are given [stream] and need to pull as | |
| 228 * much data from it as we need. Our format is that we have an integer N | |
| 229 * indicating the number of objects and then for each object a length M, | |
| 230 * and then M references, where a reference is stored in the stream as two | |
| 231 * integers. Or, in the special case of null, two nulls. | |
| 232 */ | |
| 233 pullStateFrom(Iterator stream) { | |
| 234 // TODO(alanknight): This is much too close to the basicRule implementation, | |
| 235 // and I'd refactor them if I didn't think this whole mechanism needed to | |
| 236 // change soon. | |
| 237 stream.moveNext(); | |
| 238 var length = stream.current; | |
| 239 var ruleData = new List(); | |
| 240 for (var i = 0; i < length; i++) { | |
| 241 stream.moveNext(); | |
| 242 var subLength = stream.current; | |
| 243 var subList = new List(); | |
| 244 ruleData.add(subList); | |
| 245 for (var j = 0; j < subLength; j++) { | |
| 246 stream.moveNext(); | |
| 247 var a = stream.current; | |
| 248 stream.moveNext(); | |
| 249 var b = stream.current; | |
| 250 if (!(a is int)) { | |
| 251 // This wasn't a reference, just use the first object as a literal. | |
| 252 // particularly used for the case of null. | |
| 253 subList.add(a); | |
| 254 } else { | |
| 255 subList.add(new Reference(this, a, b)); | |
| 256 } | |
| 257 } | |
| 258 } | |
| 259 return ruleData; | |
| 260 } | |
| 261 | |
| 262 /** | |
| 263 * Return true because we need to write the length of each list in the flat | |
| 264 * format. */ | |
| 265 bool get writeLengthInFlatFormat => true; | |
| 266 | |
| 267 /** Return the length of the next list when reading the flat format. */ | |
| 268 int dataLengthIn(Iterator stream) => stream.next(); | |
| 269 } | 160 } |
| 270 | 161 |
| 271 /** | 162 /** |
| 272 * This is a subclass of ListRule where all of the list's contents are | 163 * This is a subclass of ListRule where all of the list's contents are |
| 273 * considered essential state. This is needed if an object X contains a List L, | 164 * considered essential state. This is needed if an object X contains a List L, |
| 274 * but it expects L's contents to be fixed when X's constructor is called. | 165 * but it expects L's contents to be fixed when X's constructor is called. |
| 275 */ | 166 */ |
| 276 class ListRuleEssential extends ListRule { | 167 class ListRuleEssential extends ListRule { |
| 277 | 168 |
| 278 /** Create the new List and also inflate all of its contents. */ | 169 /** Create the new List and also inflate all of its contents. */ |
| (...skipping 24 matching lines...) Expand all Loading... |
| 303 inflateNonEssential(object, _, Reader r) {} | 194 inflateNonEssential(object, _, Reader r) {} |
| 304 | 195 |
| 305 /** | 196 /** |
| 306 * Indicate whether we should save pointers to this object as references | 197 * Indicate whether we should save pointers to this object as references |
| 307 * or store the object directly. For primitives this depends on the format, | 198 * or store the object directly. For primitives this depends on the format, |
| 308 * so we delegate to the writer. | 199 * so we delegate to the writer. |
| 309 */ | 200 */ |
| 310 bool shouldUseReferenceFor(object, Writer w) => | 201 bool shouldUseReferenceFor(object, Writer w) => |
| 311 w.shouldUseReferencesForPrimitives; | 202 w.shouldUseReferencesForPrimitives; |
| 312 | 203 |
| 313 /** | 204 bool get hasVariableLengthEntries => false; |
| 314 * This writes the data from our internal representation into a List. | |
| 315 * It is used in order to write to a flat format, and is likely to be | |
| 316 * folded into a more general mechanism for supporting different output | |
| 317 * formats. For primitives, the ruleData is our list of all the | |
| 318 * primitives and just add it into the target. | |
| 319 */ | |
| 320 void dumpStateInto(List ruleData, List target) { | |
| 321 target.addAll(ruleData); | |
| 322 } | |
| 323 | |
| 324 /** | |
| 325 * When reading from a flat format we are given [stream] and need to pull as | |
| 326 * much data from it as we need. Our format is that we have an integer N | |
| 327 * indicating the number of objects and then N simple objects. | |
| 328 */ | |
| 329 pullStateFrom(Iterator stream) { | |
| 330 stream.moveNext(); | |
| 331 var length = stream.current; | |
| 332 var ruleData = new List(); | |
| 333 for (var i = 0; i < length; i++) { | |
| 334 stream.moveNext(); | |
| 335 ruleData.add(stream.current); | |
| 336 } | |
| 337 return ruleData; | |
| 338 } | |
| 339 } | 205 } |
| 340 | 206 |
| 341 /** Typedef for the object construction closure used in ClosureRule. */ | 207 /** Typedef for the object construction closure used in ClosureRule. */ |
| 342 typedef ConstructType(Map m); | 208 typedef ConstructType(Map m); |
| 343 | 209 |
| 344 /** Typedef for the state-getting closure used in ClosureToMapRule. */ | 210 /** Typedef for the state-getting closure used in ClosureToMapRule. */ |
| 345 typedef Map<String, dynamic> GetStateType(object); | 211 typedef Map<String, dynamic> GetStateType(object); |
| 346 | 212 |
| 347 /** Typedef for the state-setting closure used in ClosureToMapRule. */ | 213 /** Typedef for the state-setting closure used in ClosureToMapRule. */ |
| 348 typedef void NonEssentialStateType(object, Map m); | 214 typedef void NonEssentialStateType(object, Map m); |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 484 } | 350 } |
| 485 | 351 |
| 486 inflateEssential(state, Reader r) => create(_lazy(state, r)); | 352 inflateEssential(state, Reader r) => create(_lazy(state, r)); |
| 487 | 353 |
| 488 void inflateNonEssential(state, object, Reader r) => | 354 void inflateNonEssential(state, object, Reader r) => |
| 489 setState(object, _lazy(state, r)); | 355 setState(object, _lazy(state, r)); |
| 490 | 356 |
| 491 // We don't want to have to make the end user tell us how long the list is | 357 // We don't want to have to make the end user tell us how long the list is |
| 492 // separately, so write it out for each object, even though they're all | 358 // separately, so write it out for each object, even though they're all |
| 493 // expected to be the same length. | 359 // expected to be the same length. |
| 494 get writeLengthInFlatFormat => true; | 360 get hasVariableLengthEntries => true; |
| 495 } | 361 } |
| 496 | 362 |
| 497 /** Create a lazy list/map that will inflate its items on demand in [r]. */ | 363 /** Create a lazy list/map that will inflate its items on demand in [r]. */ |
| 498 _lazy(l, Reader r) { | 364 _lazy(l, Reader r) { |
| 499 if (l is List) return l.mappedBy(r.inflateReference); | 365 if (l is List) return new _LazyList(l, r); |
| 500 if (l is Map) return new _LazyMap(l, r); | 366 if (l is Map) return new _LazyMap(l, r); |
| 501 throw new SerializationException("Invalid type: must be Map or List - $l"); | 367 throw new SerializationException("Invalid type: must be Map or List - $l"); |
| 502 } | 368 } |
| 503 | 369 |
| 504 /** | 370 /** |
| 505 * This provides an implementation of Map that wraps a list which may | 371 * This provides an implementation of Map that wraps a list which may |
| 506 * contain references to (potentially) non-inflated objects. If these | 372 * contain references to (potentially) non-inflated objects. If these |
| 507 * are accessed it will inflate them. This allows us to pass something that | 373 * are accessed it will inflate them. This allows us to pass something that |
| 508 * looks like it's just a list of objects to a [CustomRule] without needing | 374 * looks like it's just a list of objects to a [CustomRule] without needing |
| 509 * to inflate all the references in advance. | 375 * to inflate all the references in advance. |
| 510 */ | 376 */ |
| 511 class _LazyMap implements Map { | 377 class _LazyMap implements Map { |
| 512 _LazyMap(this._raw, this._reader); | 378 _LazyMap(this._raw, this._reader); |
| 513 | 379 |
| 514 Map _raw; | 380 final Map _raw; |
| 515 Reader _reader; | 381 final Reader _reader; |
| 516 | 382 |
| 517 // This is the only operation that really matters. | 383 // This is the only operation that really matters. |
| 518 operator [](x) => _reader.inflateReference(_raw[x]); | 384 operator [](x) => _reader.inflateReference(_raw[x]); |
| 519 | 385 |
| 520 int get length => _raw.length; | 386 int get length => _raw.length; |
| 521 bool get isEmpty => _raw.isEmpty; | 387 bool get isEmpty => _raw.isEmpty; |
| 522 Iterable get keys => _raw.keys; | 388 Iterable get keys => _raw.keys; |
| 523 bool containsKey(x) => _raw.containsKey(x); | 389 bool containsKey(x) => _raw.containsKey(x); |
| 524 | 390 |
| 525 // These operations will work, but may be expensive, and are probably | 391 // These operations will work, but may be expensive, and are probably |
| 526 // best avoided. | 392 // best avoided. |
| 527 get _inflated => keysAndValues(_raw).map(_reader.inflateReference); | 393 get _inflated => keysAndValues(_raw).mappedBy(_reader.inflateReference); |
| 528 bool containsValue(x) => _inflated.containsValue(x); | 394 bool containsValue(x) => _inflated.containsValue(x); |
| 529 Iterable get values => _inflated.values; | 395 Iterable get values => _inflated.values; |
| 530 void forEach(f) => _inflated.forEach(f); | 396 void forEach(f) => _inflated.forEach(f); |
| 531 | 397 |
| 532 // These operations are all invalid | 398 // These operations are all invalid |
| 533 _throw() => throw new UnsupportedError("Not modifiable"); | 399 _throw() { |
| 400 throw new UnsupportedError("Not modifiable"); |
| 401 } |
| 534 operator []=(x, y) => _throw(); | 402 operator []=(x, y) => _throw(); |
| 535 putIfAbsent(x, y) => _throw(); | 403 putIfAbsent(x, y) => _throw(); |
| 536 remove(x) => _throw(); | 404 remove(x) => _throw(); |
| 537 clear() => _throw(); | 405 clear() => _throw(); |
| 538 } | 406 } |
| 407 |
| 408 /** |
| 409 * This provides an implementation of List that wraps a list which may |
| 410 * contain references to (potentially) non-inflated objects. If these |
| 411 * are accessed it will inflate them. This allows us to pass something that |
| 412 * looks like it's just a list of objects to a [CustomRule] without needing |
| 413 * to inflate all the references in advance. |
| 414 */ |
| 415 class _LazyList extends Iterable implements List { |
| 416 _LazyList(this._raw, this._reader); |
| 417 |
| 418 final List _raw; |
| 419 final Reader _reader; |
| 420 |
| 421 // This is the only operation that really matters. |
| 422 operator [](x) => _reader.inflateReference(_raw[x]); |
| 423 |
| 424 int get length => _raw.length; |
| 425 bool get isEmpty => _raw.isEmpty; |
| 426 get first => _reader.inflateReference(_raw.first); |
| 427 get last => _reader.inflateReference(_raw.last); |
| 428 |
| 429 // These operations, and other inherited methods that iterate over the whole |
| 430 // list will work, but may be expensive, and are probably |
| 431 // best avoided. |
| 432 List get _inflated => _raw.mappedBy(_reader.inflateReference); |
| 433 Iterator get iterator => _inflated.iterator; |
| 434 indexOf(x, [pos = 0]) => _inflated.toList().indexOf(x); |
| 435 lastIndexOf(x, [pos]) => _inflated.toList().lastIndexOf(x); |
| 436 |
| 437 // These operations are all invalid |
| 438 _throw() { |
| 439 throw new UnsupportedError("Not modifiable"); |
| 440 } |
| 441 operator []=(x, y) => _throw(); |
| 442 add(x) => _throw(); |
| 443 addLast(x) => _throw(); |
| 444 addAll(x) => _throw(); |
| 445 sort([f]) => _throw(); |
| 446 clear() => _throw(); |
| 447 removeAt(x) => _throw(); |
| 448 removeLast() => _throw(); |
| 449 getRange(x, y) => _throw(); |
| 450 setRange(x, y, z, [a]) => _throw(); |
| 451 removeRange(x, y) => _throw(); |
| 452 insertRange(x, y, [z]) => _throw(); |
| 453 void set length(x) => _throw(); |
| 454 } |
| OLD | NEW |