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

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

Issue 584473004: Revert "remove serialization. it's moved to github" (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 3 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of serialization;
6
7 /**
8 * This writes out the state of the objects to an external format. It holds
9 * all of the intermediate state needed. The primary API for it is the
10 * [write] method.
11 */
12 // TODO(alanknight): For simple serialization formats this does a lot of work
13 // that isn't necessary, e.g. detecting cycles and maintaining references.
14 // Consider having an abstract superclass with the basic functionality and
15 // simple serialization subclasses where we know there aren't cycles.
16 class Writer implements ReaderOrWriter {
17 /**
18 * The [serialization] holds onto the rules that define how objects
19 * are serialized.
20 */
21 final Serialization serialization;
22
23 /** The [trace] object keeps track of the objects to be visited while finding
24 * the full set of objects to be written.*/
25 Trace trace;
26
27 /**
28 * When we write out objects, should we also write out a description
29 * of the rules for the serialization. This defaults to the corresponding
30 * value on the Serialization.
31 */
32 bool selfDescribing;
33
34 final Format format;
35
36 /**
37 * Objects that cannot be represented in-place in the serialized form need
38 * to have references to them stored. The [Reference] objects are computed
39 * once and stored here for each object. This provides some space-saving,
40 * but also serves to record which objects we have already seen.
41 */
42 final Map<dynamic, Reference> references =
43 new HashMap<Object, Reference>.identity();
44
45 /**
46 * The state of objects that need to be serialized is stored here.
47 * Each rule has a number, and rules keep track of the objects that they
48 * serialize, in order. So the state of any object can be found by indexing
49 * from the rule number and the object number within the rule.
50 * The actual representation of the state is determined by the rule. Lists
51 * and Maps are common, but it is arbitrary.
52 */
53 final List<List> states = new List<List>();
54
55 /** Return the list of rules we use. */
56 List<SerializationRule> get rules => serialization.rules;
57
58 /**
59 * Creates a new [Writer] that uses the rules from its parent
60 * [Serialization]. Serializations do not keep any state
61 * related to a particular read/write, so the same one can be used
62 * for multiple different Readers/Writers.
63 */
64 Writer(this.serialization, [Format newFormat]) :
65 format = (newFormat == null) ? const SimpleMapFormat() : newFormat {
66 trace = new Trace(this);
67 selfDescribing = serialization.selfDescribing;
68 }
69
70 /**
71 * This is the main API for a [Writer]. It writes the objects and returns
72 * the serialized representation, as determined by [format].
73 */
74 write(anObject) {
75 trace.addRoot(anObject);
76 trace.traceAll();
77 _flatten();
78 return format.generateOutput(this);
79 }
80
81 /**
82 * Given that we have fully populated the list of [states], and more
83 * importantly, the list of [references], go through each state and turn
84 * anything that requires a [Reference] into one. Since only the rules
85 * know the representation they use for state, delegate to them.
86 */
87 void _flatten() {
88 for (var eachRule in rules) {
89 _growStates(eachRule);
90 var index = eachRule.number;
91 var statesForThisRule = states[index];
92 for (var i = 0; i < statesForThisRule.length; i++) {
93 var eachState = statesForThisRule[i];
94 var newState = eachRule.flatten(eachState, this);
95 if (newState != null) {
96 statesForThisRule[i] = newState;
97 }
98 }
99 }
100 }
101
102 /**
103 * As the [trace] processes each object, it will call this method on us.
104 * We find the rules for this object, and record the state of the object
105 * as determined by each rule.
106 */
107 void _process(object, Trace trace) {
108 var real = (object is DesignatedRuleForObject) ? object.target : object;
109 for (var eachRule in serialization.rulesFor(object, this)) {
110 _record(real, eachRule);
111 }
112 }
113
114 /**
115 * Record the state of [object] as determined by [rule] and keep
116 * track of it. Generate a [Reference] for this object if required.
117 * When it's required is up to the particular rule, but generally everything
118 * gets a reference except a primitive.
119 * Note that at this point the states are just the same as the fields of the
120 * object, and haven't been flattened.
121 */
122 void _record(object, SerializationRule rule) {
123 if (rule.shouldUseReferenceFor(object, this)) {
124 references.putIfAbsent(object, () =>
125 new Reference(this, rule.number, _nextObjectNumberFor(rule)));
126 var state = rule.extractState(object, trace.note, this);
127 _addStateForRule(rule, state);
128 }
129 }
130
131 /**
132 * Should we store primitive objects directly or create references for them.
133 * That depends on which format we're using, so a flat format will want
134 * references, but the Map format can store them directly.
135 */
136 bool get shouldUseReferencesForPrimitives
137 => format.shouldUseReferencesForPrimitives;
138
139 /**
140 * Returns a serialized version of the [SerializationRule]s used to write
141 * the data, if [selfDescribing] is true, otherwise returns null.
142 */
143 serializedRules() {
144 if (!selfDescribing) return null;
145 var meta = serialization.ruleSerialization();
146 var writer = new Writer(meta, format);
147 writer.selfDescribing = false;
148 return writer.write(serialization.rules);
149 }
150
151 /** Record a [state] entry for a particular rule. */
152 void _addStateForRule(eachRule, state) {
153 _growStates(eachRule);
154 states[eachRule.number].add(state);
155 }
156
157 /** Find what the object number for the thing we're about to add will be.*/
158 int _nextObjectNumberFor(SerializationRule rule) {
159 _growStates(rule);
160 return states[rule.number].length;
161 }
162
163 /**
164 * We store the states in a List, indexed by rule number. But rules can be
165 * dynamically added, so we may have to grow the list.
166 */
167 void _growStates(eachRule) {
168 while (states.length <= eachRule.number) states.add(new List());
169 }
170
171 /**
172 * Return true if we have an object number for this object. This is used to
173 * tell if we have processed the object or not. This relies on checking if we
174 * have a reference or not. That saves some space by not having to keep track
175 * of simple objects, but means that if someone refers to the identical string
176 * from several places, we will process it several times, and store it
177 * several times. That seems an acceptable tradeoff, and in cases where it
178 * isn't, it's possible to apply a rule for String, or even for Strings larger
179 * than x, which gives them references.
180 */
181 bool _hasIndexFor(object) {
182 return _objectNumberFor(object) != -1;
183 }
184
185 /**
186 * Given an object, find what number it has. The number is valid only in
187 * the context of a particular rule, and if the rule has more than one,
188 * this will return the one for the primary rule, defined as the one that
189 * is listed in its canonical reference.
190 */
191 int _objectNumberFor(object) {
192 var reference = references[object];
193 return (reference == null) ? -1 : reference.objectNumber;
194 }
195
196 /**
197 * Return a list of [Reference] objects pointing to our roots. This will be
198 * stored in the output under "roots" in the default format.
199 */
200 List _rootReferences() => trace.roots.map(_referenceFor).toList();
201
202 /**
203 * Given an object, return a reference for it if one exists. If there's
204 * no reference, return the object itself. Once we have finished the tracing
205 * step, all objects that should have a reference (roughly speaking,
206 * non-primitives) can be relied on to have a reference.
207 */
208 _referenceFor(object) {
209 var result = references[object];
210 return (result == null) ? object : result;
211 }
212
213 /**
214 * Return true if the [Serialization.namedObjects] collection has a
215 * reference to [object].
216 */
217 // TODO(alanknight): Should the writer also have its own namedObjects
218 // collection specific to the particular write, or is that just adding
219 // complexity for little value?
220 bool hasNameFor(object) => serialization._hasNameFor(object);
221
222 /**
223 * Return the name we have for this object in the [Serialization.namedObjects]
224 * collection.
225 */
226 String nameFor(object) => serialization._nameFor(object);
227
228 // For debugging/testing purposes. Find what state a reference points to.
229 stateForReference(Reference r) => states[r.ruleNumber][r.objectNumber];
230
231 /** Return the state pointed to by [reference]. */
232 resolveReference(reference) => stateForReference(reference);
233 }
234
235 /**
236 * An abstract class for Reader and Writer, which primarily exists so we can
237 * type things that will refer to one or the other, depending on which
238 * operation we're doing.
239 */
240 abstract class ReaderOrWriter {
241 /** Return the list of serialization rules we are using.*/
242 List<SerializationRule> get rules;
243
244 /** Return the internal collection of object state and [Reference] objects. */
245 List<List> get states;
246
247 /**
248 * Return the object, or state, that ref points to, depending on which
249 * we're generating.
250 */
251 resolveReference(Reference ref);
252 }
253
254 /**
255 * The main class responsible for reading. It holds
256 * onto the necessary state and to the objects that have been inflated.
257 */
258 class Reader implements ReaderOrWriter {
259
260 /**
261 * The serialization that specifies how we read. Note that in contrast
262 * to the Writer, this is not final. This is because we may be created
263 * with an empty [Serialization] and then read the rules from the data,
264 * if [selfDescribing] is true.
265 */
266 Serialization serialization;
267
268 /**
269 * When we read objects, should we read a description of the rules if
270 * present. This defaults to the corresponding value on the Serialization.
271 */
272 bool selfDescribing;
273
274 /**
275 * The state of objects that have been serialized is stored here.
276 * Each rule has a number, and rules keep track of the objects that they
277 * serialize, in order. So the state of any object can be found by indexing
278 * from the rule number and the object number within the rule.
279 * The actual representation of the state is determined by the rule. Lists
280 * and Maps are common, but it is arbitrary. See [Writer.states].
281 */
282 List<List> _data;
283
284 /** Return the internal collection of object state and [Reference] objects. */
285 get states => _data;
286
287 /**
288 * The resulting objects, indexed according to the same scheme as
289 * _data, where each rule has a number, and rules keep track of the objects
290 * that they serialize, in order.
291 */
292 List<List> objects;
293
294 final Format format;
295
296 /**
297 * Creates a new [Reader] that uses the rules from its parent
298 * [Serialization]. Serializations do not keep any state related to
299 * a particular read or write operation, so the same one can be used
300 * for multiple different Writers/Readers.
301 */
302 Reader(this.serialization, [Format newFormat]) :
303 format = (newFormat == null) ? const SimpleMapFormat() : newFormat {
304 selfDescribing = serialization.selfDescribing;
305 }
306
307 /**
308 * When we read, we may need to look up objects by name in order to link to
309 * them. This is particularly true if we have references to classes,
310 * functions, mirrors, or other non-portable entities. The map in which we
311 * look things up can be provided as an argument to read, but we can also
312 * provide a map here, and objects will be looked up in both places.
313 */
314 Map namedObjects;
315
316 /**
317 * Look up the reference to an external object. This can be held either in
318 * the reader-specific list of externals or in the serializer's
319 */
320 objectNamed(key, [Function ifAbsent]) {
321 var map = (namedObjects.containsKey(key))
322 ? namedObjects : serialization.namedObjects;
323 if (!map.containsKey(key)) {
324 (ifAbsent == null ? keyNotFound : ifAbsent)(key);
325 }
326 return map[key];
327 }
328
329 void keyNotFound(key) {
330 throw new SerializationException(
331 'Cannot find named object to link to: $key');
332 }
333
334 /**
335 * Return the list of rules to be used when writing. These come from the
336 * [serialization].
337 */
338 List<SerializationRule> get rules => serialization.rules;
339
340 /**
341 * Internal use only, for testing purposes. Set the data for this reader
342 * to a List of Lists whose size must match the number of rules.
343 */
344 // When we set the data, initialize the object storage to a matching size.
345 void set data(List<List> newData) {
346 _data = newData;
347 objects = _data.map((x) => new List(x.length)).toList();
348 }
349
350 /**
351 * This is the primary method for a [Reader]. It takes the input data,
352 * decodes it according to [format] and returns the root object.
353 */
354 read(rawInput, [Map externals = const {}]) {
355 namedObjects = externals;
356 var input = format.read(rawInput, this);
357 data = input["data"];
358 rules.forEach(inflateForRule);
359 return inflateReference(input["roots"].first);
360 }
361
362 /**
363 * If the data we are reading from has rules written to it, read them back
364 * and set them as the rules we will use.
365 */
366 void readRules(newRules) {
367 // TODO(alanknight): Replacing the serialization is kind of confusing.
368 if (newRules == null) return;
369 var reader = serialization.ruleSerialization().newReader(format);
370 List rulesWeRead = reader.read(newRules, namedObjects);
371 if (rulesWeRead != null && !rulesWeRead.isEmpty) {
372 serialization = new Serialization.blank();
373 rulesWeRead.forEach(serialization.addRule);
374 }
375 }
376
377 /**
378 * Inflate all of the objects for [rule]. Does the essential state for all
379 * objects first, then the non-essential state. This avoids cycles in
380 * non-essential state, because all the objects will have already been
381 * created.
382 */
383 void inflateForRule(rule) {
384 var dataForThisRule = _data[rule.number];
385 keysAndValues(dataForThisRule).forEach((position, state) {
386 inflateOne(rule, position, state);
387 });
388 keysAndValues(dataForThisRule).forEach((position, state) {
389 rule.inflateNonEssential(state, allObjectsForRule(rule)[position], this);
390 });
391 }
392
393 /**
394 * Create a new object, based on [rule] and [state], which will
395 * be stored in [position] in the storage for [rule]. This will
396 * follow references and recursively inflate them, leaving Sentinel objects
397 * to detect cycles.
398 */
399 inflateOne(SerializationRule rule, position, state) {
400 var existing = allObjectsForRule(rule)[position];
401 // We may already be in progress and hitting this in a cycle.
402 if (existing is _Sentinel) {
403 throw new SerializationException('Cycle in essential state');
404 }
405 // We may have already inflated this object, at least its essential state.
406 if (existing != null) return existing;
407
408 // Put a sentinel there to mark this in case of recursion.
409 allObjectsForRule(rule)[position] = const _Sentinel();
410 var newObject = rule.inflateEssential(state, this);
411 allObjectsForRule(rule)[position] = newObject;
412 return newObject;
413 }
414
415 /**
416 * The parameter [possibleReference] might be a reference. If it isn't, just
417 * return it. If it is, then inflate the target of the reference and return
418 * the resulting object.
419 */
420 inflateReference(possibleReference) {
421 // If this is a primitive, return it directly.
422 // TODO This seems too complicated.
423 return asReference(possibleReference,
424 ifReference: (reference) {
425 var rule = ruleFor(reference);
426 var state = _stateFor(reference);
427 inflateOne(rule, reference.objectNumber, state);
428 return _objectFor(reference);
429 });
430 }
431
432 /** Return the object pointed to by [reference]. */
433 resolveReference(reference) => inflateReference(reference);
434
435 /**
436 * Given [reference], return what we have stored as an object for it. Note
437 * that, depending on the current state, this might be null or a Sentinel.
438 */
439 _objectFor(Reference reference) =>
440 objects[reference.ruleNumber][reference.objectNumber];
441
442 /** Given [rule], return the storage for its objects. */
443 allObjectsForRule(SerializationRule rule) => objects[rule.number];
444
445 /** Given [reference], return the the state we have stored for it. */
446 _stateFor(Reference reference) =>
447 _data[reference.ruleNumber][reference.objectNumber];
448
449 /** Given a reference, return the rule it references. */
450 SerializationRule ruleFor(Reference reference) =>
451 serialization.rules[reference.ruleNumber];
452
453 /**
454 * Return the primitive rule we are using. This is an ugly mechanism to
455 * support the extra information to reconstruct objects in the
456 * [SimpleJsonFormat].
457 */
458 SerializationRule _primitiveRule() {
459 for (var each in rules) {
460 if (each.runtimeType == PrimitiveRule) {
461 return each;
462 }
463 }
464 throw new SerializationException("No PrimitiveRule found");
465 }
466
467 /**
468 * Given a possible reference [anObject], call either [ifReference] or
469 * [ifNotReference], depending if it's a reference or not. This is the
470 * primary place that knows about the serialized representation of a
471 * reference.
472 */
473 asReference(anObject, {Function ifReference: doNothing,
474 Function ifNotReference : doNothing}) {
475 if (anObject is Reference) return ifReference(anObject);
476 if (anObject is Map && anObject["__Ref"] != null) {
477 var ref =
478 new Reference(this, anObject["rule"], anObject["object"]);
479 return ifReference(ref);
480 } else {
481 return ifNotReference(anObject);
482 }
483 }
484 }
485
486 /**
487 * This serves as a marker to indicate a object that is in the process of
488 * being de-serialized. So if we look for an object slot and find one of these,
489 * we know we've hit a cycle.
490 */
491 class _Sentinel {
492 const _Sentinel();
493 }
494
495 /**
496 * This represents the transitive closure of the referenced objects to be
497 * used for serialization. It works closely in conjunction with the Writer,
498 * and is kept as a separate object primarily for the possibility of wanting
499 * to plug in different sorts of tracing rules.
500 */
501 class Trace {
502 // TODO(alanknight): It seems likely that the mechanism for cutting off
503 // tracings is by specifying rules. So is there any reason any more to have
504 // this as a separate class?
505 final Writer writer;
506
507 /**
508 * This class works by doing a breadth-first traversal of the objects,
509 * with the traversal order maintained in [queue].
510 */
511 final Queue queue = new Queue();
512
513 /** The root objects from which we will be tracing. */
514 final List roots = [];
515
516 Trace(this.writer);
517
518 void addRoot(object) {
519 roots.add(object);
520 }
521
522 /** A convenience method to add a single root and trace it in one step. */
523 void trace(object) {
524 addRoot(object);
525 traceAll();
526 }
527
528 /**
529 * Process all of the objects reachable from our roots via state that the
530 * serialization rules access.
531 */
532 void traceAll() {
533 queue.addAll(roots);
534 while (!queue.isEmpty) {
535 var next = queue.removeFirst();
536 if (!hasProcessed(next)) writer._process(next, this);
537 }
538 }
539
540 /**
541 * Has this object been seen yet? We test for this by checking if the
542 * writer has a reference for it. See comment for _hasIndexFor.
543 */
544 bool hasProcessed(object) {
545 return writer._hasIndexFor(object);
546 }
547
548 /** Note that we've seen [value], and add it to the queue to be processed. */
549 note(value) {
550 if (value != null) {
551 queue.add(value);
552 }
553 return value;
554 }
555 }
556
557 /**
558 * Any pointers to objects that can't be represented directly in the
559 * serialization format has to be stored as a reference. A reference encodes
560 * the rule number of the rule that saved it in the Serialization that was used
561 * for writing, and the object number within that rule.
562 */
563 class Reference {
564 /** The [Reader] or [Writer] that owns this reference. */
565 final ReaderOrWriter parent;
566 /** The position of the rule that controls this reference in [parent]. */
567 final int ruleNumber;
568 /** The index of the referred-to object in the storage of [parent] */
569 final int objectNumber;
570
571 Reference(this.parent, this.ruleNumber, this.objectNumber) {
572 if (ruleNumber == null || objectNumber == null) {
573 throw new SerializationException("Invalid Reference");
574 }
575 if (parent.rules.length < ruleNumber) {
576 throw new SerializationException("Invalid Reference");
577 }
578 }
579
580 /**
581 * Return the thing this reference points to. Assumes that we have a valid
582 * parent and that it is a Reader, as inflating is not meaningful when
583 * writing.
584 */
585 inflated() => parent.resolveReference(this);
586
587 /**
588 * Convert the reference to a map in JSON format. This is specific to the
589 * custom JSON format we define, and must be consistent with the
590 * [Reader.asReference] method.
591 */
592 // TODO(alanknight): This is a hack both in defining a toJson specific to a
593 // particular representation, and the use of a bogus sentinel "__Ref"
594 Map<String, int> toJson() => {
595 "__Ref" : 0,
596 "rule" : ruleNumber,
597 "object" : objectNumber
598 };
599
600 /** Write our information to [list]. Useful in writing to flat formats.*/
601 void writeToList(List list) {
602 list.add(ruleNumber);
603 list.add(objectNumber);
604 }
605
606 String toString() => "Reference($ruleNumber, $objectNumber)";
607 }
608
609 /**
610 * This is used during tracing to indicate that an object should be processed
611 * using a particular rule, rather than the one that might ordinarily be
612 * found for it. This normally only makes sense if the object is uniquely
613 * referenced, and is a more or less internal collection. See ListRuleEssential
614 * for an example. It knows how to return its object and how to filter.
615 */
616 class DesignatedRuleForObject {
617 final Function rulePredicate;
618 final target;
619
620 DesignatedRuleForObject(this.target, this.rulePredicate);
621
622 List possibleRules(List rules) => rules.where(rulePredicate).toList();
623 }
OLDNEW
« no previous file with comments | « pkg/serialization/lib/src/mirrors_helpers.dart ('k') | pkg/serialization/lib/src/serialization_helpers.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698