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

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

Issue 11820032: Make input/output formats pluggable, adapt to new libraries (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Changes from review comments Created 7 years, 11 months 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 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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 }
OLDNEW
« no previous file with comments | « pkg/serialization/lib/src/serialization_helpers.dart ('k') | pkg/serialization/test/serialization_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698