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 |