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

Side by Side Diff: pkg/serialization/lib/src/reader_writer.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 /** 7 /**
8 * This writes out the state of the objects to an external format. It holds 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 9 * all of the intermediate state needed. The primary API for it is the
10 * [write] method. 10 * [write] method.
(...skipping 13 matching lines...) Expand all
24 * the full set of objects to be written.*/ 24 * the full set of objects to be written.*/
25 Trace trace; 25 Trace trace;
26 26
27 /** 27 /**
28 * When we write out objects, should we also write out a description 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 29 * of the rules for the serialization. This defaults to the corresponding
30 * value on the Serialization. 30 * value on the Serialization.
31 */ 31 */
32 bool selfDescribing; 32 bool selfDescribing;
33 33
34 Format format = new SimpleMapFormat();
35
34 /** 36 /**
35 * Objects that cannot be represented in-place in the serialized form need 37 * Objects that cannot be represented in-place in the serialized form need
36 * to have references to them stored. The [Reference] objects are computed 38 * to have references to them stored. The [Reference] objects are computed
37 * once and stored here for each object. This provides some space-saving, 39 * once and stored here for each object. This provides some space-saving,
38 * but also serves to record which objects we have already seen. 40 * but also serves to record which objects we have already seen.
39 */ 41 */
40 final Map<dynamic, Reference> references = 42 final Map<dynamic, Reference> references =
41 new IdentityMap<Object, Reference>(); 43 new IdentityMap<Object, Reference>();
42 44
43 /** 45 /**
44 * The state of objects that need to be serialized is stored here. 46 * The state of objects that need to be serialized is stored here.
45 * Each rule has a number, and rules keep track of the objects that they 47 * Each rule has a number, and rules keep track of the objects that they
46 * serialize, in order. So the state of any object can be found by indexing 48 * serialize, in order. So the state of any object can be found by indexing
47 * from the rule number and the object number within the rule. 49 * from the rule number and the object number within the rule.
48 * The actual representation of the state is determined by the rule. Lists 50 * The actual representation of the state is determined by the rule. Lists
49 * and Maps are common, but it is arbitrary. 51 * and Maps are common, but it is arbitrary.
50 */ 52 */
51 final List<List> states = new List<List>(); 53 final List<List> states = new List<List>();
52 54
53 /** Return the list of rules we use. */ 55 /** Return the list of rules we use. */
54 List<SerializationRule> get rules => serialization._rules; 56 List<SerializationRule> get rules => serialization._rules;
55 57
56 /** 58 /**
57 * Creates a new [Writer] that uses the rules from its parent 59 * Creates a new [Writer] that uses the rules from its parent
58 * [Serialization]. Serializations do not keep any state 60 * [Serialization]. Serializations do not keep any state
59 * related to a particular read/write, so the same one can be used 61 * related to a particular read/write, so the same one can be used
60 * for multiple different Readers/Writers. 62 * for multiple different Readers/Writers.
61 */ 63 */
62 Writer(this.serialization) { 64 Writer(this.serialization, [Format newFormat]) {
63 trace = new Trace(this); 65 trace = new Trace(this);
64 selfDescribing = serialization.selfDescribing; 66 selfDescribing = serialization.selfDescribing;
67 if (newFormat != null) format = newFormat;
65 } 68 }
66 69
67 /** 70 /**
68 * This is the main API for a [Writer]. It writes the objects and returns 71 * This is the main API for a [Writer]. It writes the objects and returns
69 * the serialized representation, currently a JSON format of a map 72 * the serialized representation, as determined by [format].
70 * whose data is either lists indexed by field position or maps indexed
71 * by field name, and holding either primitives or references. See [toMaps]
72 */ 73 */
73 // TODO(alanknight): Generalize the output representation. Probably requires 74 write(anObject) {
74 // introducing some sort of OutputFormat object.
75 String write(anObject) {
76 trace.addRoot(anObject); 75 trace.addRoot(anObject);
77 trace.traceAll(); 76 trace.traceAll();
78 _flatten(); 77 _flatten();
79 return toStringFormat(); 78 return format.generateOutput(this);
80 } 79 }
81 80
82 /** 81 /**
83 * This is an alternate writing API that writes the objects and returns
84 * the serialized representation as a List of simple objects.
85 * See [toFlatFormat].
86 */
87 List writeFlat(anObject) {
88 shouldUseReferencesForPrimitives = true;
89 trace.addRoot(anObject);
90 trace.traceAll();
91 _flatten();
92 return toFlatFormat();
93 }
94
95 /**
96 * Write to a simple flat format. This format is at the proof of concept
97 * stage, so details are not finalized and are likely to change in the future.
98 * Right now this produces a List containing null, int, and String. This is
99 * more space-efficient than the map format created by [toStringFormat] or
100 * [toMaps], but is much less human-readable.
101 */
102 List toFlatFormat() {
103 var result = new List(3);
104 // TODO(alanknight): Don't make it call toMaps in order to make non-maps.
105 // As part of that, if writing flat, the rule serialization should be flat.
106 var stuff = toMaps();
107 result[0] = stuff["rules"];
108 var roots = new List();
109 stuff["roots"].forEach((x) => x.writeToList(roots));
110 result[2] = roots;
111
112 // TODO(alanknight): This needs serious generalization. Do we introduce
113 // an output format object that the rules talk to? Do we make use of the
114 // fact that rules talk to something that looks to them like a List. Do
115 // we then mandate that instead of saying they have complete charge of
116 // their own storage?
117 var flatData = [];
118 for (var eachRule in rules) {
119 var ruleData = stuff["data"][eachRule.number];
120 flatData.add(ruleData.length);
121 eachRule.dumpStateInto(ruleData, flatData);
122 }
123 result[1] = flatData;
124 return result;
125 }
126
127 /**
128 * Given that we have fully populated the list of [states], and more 82 * Given that we have fully populated the list of [states], and more
129 * importantly, the list of [references], go through each state and turn 83 * importantly, the list of [references], go through each state and turn
130 * anything that requires a [Reference] into one. Since only the rules 84 * anything that requires a [Reference] into one. Since only the rules
131 * know the representation they use for state, delegate to them. 85 * know the representation they use for state, delegate to them.
132 */ 86 */
133 void _flatten() { 87 void _flatten() {
134 for (var eachRule in rules) { 88 for (var eachRule in rules) {
135 _growStates(eachRule); 89 _growStates(eachRule);
136 var index = eachRule.number; 90 var index = eachRule.number;
137 for (var eachState in states[index]) { 91 for (var eachState in states[index]) {
(...skipping 29 matching lines...) Expand all
167 var state = rule.extractState(object, trace.note); 121 var state = rule.extractState(object, trace.note);
168 _addStateForRule(rule, state); 122 _addStateForRule(rule, state);
169 } 123 }
170 } 124 }
171 125
172 /** 126 /**
173 * Should we store primitive objects directly or create references for them. 127 * Should we store primitive objects directly or create references for them.
174 * That depends on which format we're using, so a flat format will want 128 * That depends on which format we're using, so a flat format will want
175 * references, but the Map format can store them directly. 129 * references, but the Map format can store them directly.
176 */ 130 */
177 bool shouldUseReferencesForPrimitives = false; 131 bool get shouldUseReferencesForPrimitives
132 => format.shouldUseReferencesForPrimitives;
133
134 /**
135 * Returns a serialized version of the [SerializationRule]s used to write
136 * the data, if [selfDescribing] is true, otherwise returns null.
137 */
138 serializedRules() {
139 if (!selfDescribing) return null;
140 var meta = serialization.ruleSerialization();
141 var writer = new Writer(meta);
142 writer.selfDescribing = false;
143 return writer.write(serialization._rules);
144 }
178 145
179 /** Record a [state] entry for a particular rule. */ 146 /** Record a [state] entry for a particular rule. */
180 void _addStateForRule(eachRule, state) { 147 void _addStateForRule(eachRule, state) {
181 _growStates(eachRule); 148 _growStates(eachRule);
182 states[eachRule.number].add(state); 149 states[eachRule.number].add(state);
183 } 150 }
184 151
185 /** Find what the object number for the thing we're about to add will be.*/ 152 /** Find what the object number for the thing we're about to add will be.*/
186 int _nextObjectNumberFor(SerializationRule rule) { 153 int _nextObjectNumberFor(SerializationRule rule) {
187 _growStates(rule); 154 _growStates(rule);
(...skipping 27 matching lines...) Expand all
215 * the context of a particular rule, and if the rule has more than one, 182 * the context of a particular rule, and if the rule has more than one,
216 * this will return the one for the primary rule, defined as the one that 183 * this will return the one for the primary rule, defined as the one that
217 * is listed in its canonical reference. 184 * is listed in its canonical reference.
218 */ 185 */
219 int _objectNumberFor(object) { 186 int _objectNumberFor(object) {
220 var reference = references[object]; 187 var reference = references[object];
221 return (reference == null) ? -1 : reference.objectNumber; 188 return (reference == null) ? -1 : reference.objectNumber;
222 } 189 }
223 190
224 /** 191 /**
225 * Return the serialized data in string format. Currently hard-coded to
226 * our custom JSON format.
227 */
228 String toStringFormat() {
229 return json.stringify(toMaps());
230 }
231
232 /**
233 * Returns the full serialized structure as nested maps. The top-level
234 * has 3 fields, "rules" which may hold a definition of the rules used,
235 * "data" which holds the serialized data, and "roots", which holds
236 * [Reference] objects indicating the root objects. Note that roots are
237 * necessary because the data is organized in the same way as the object
238 * structure, it's a list of lists holding self-contained maps which only
239 * refer to other parts via [Reference] objects.
240 * This effectively defines a custom JSON serialization format, although
241 * the details of the format vary depending which rules were used.
242 */
243 Map toMaps() {
244 var result = new Map();
245 var savedRules;
246 if (selfDescribing) {
247 var meta = serialization._ruleSerialization();
248 var writer = new Writer(meta);
249 writer.selfDescribing = false;
250 savedRules = writer.write(serialization._rules);
251 }
252 result["rules"] = savedRules;
253 result["data"] = states;
254 result["roots"] = _rootReferences(trace.roots);
255 return result;
256 }
257
258 /**
259 * Return a list of [Reference] objects pointing to our roots. This will be 192 * Return a list of [Reference] objects pointing to our roots. This will be
260 * stored in the output under "roots" in the default format. 193 * stored in the output under "roots" in the default format.
261 */ 194 */
262 _rootReferences(roots) => 195 _rootReferences() => trace.roots.mappedBy(_referenceFor).toList();
263 roots.mappedBy(_referenceFor).toList();
264 196
265 /** 197 /**
266 * Given an object, return a reference for it if one exists. If there's 198 * Given an object, return a reference for it if one exists. If there's
267 * no reference, return null. Once we have finished the tracing step, all 199 * no reference, return null. Once we have finished the tracing step, all
268 * objects that should have a reference (roughly speaking, non-primitives) 200 * objects that should have a reference (roughly speaking, non-primitives)
269 * can be relied on to have a reference. 201 * can be relied on to have a reference.
270 */ 202 */
271 _referenceFor(object) => references[object]; 203 _referenceFor(object) => references[object];
272 204
273 /** 205 /**
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
317 */ 249 */
318 List<List> _data; 250 List<List> _data;
319 251
320 /** 252 /**
321 * The resulting objects, indexed according to the same scheme as 253 * The resulting objects, indexed according to the same scheme as
322 * [data], where each rule has a number, and rules keep track of the objects 254 * [data], where each rule has a number, and rules keep track of the objects
323 * that they serialize, in order. 255 * that they serialize, in order.
324 */ 256 */
325 List<List> objects; 257 List<List> objects;
326 258
259 Format format = new SimpleMapFormat();
260
327 /** 261 /**
328 * Creates a new [Reader] that uses the rules from its parent 262 * Creates a new [Reader] that uses the rules from its parent
329 * [Serialization]. Serializations do not keep any state related to 263 * [Serialization]. Serializations do not keep any state related to
330 * a particular read or write operation, so the same one can be used 264 * a particular read or write operation, so the same one can be used
331 * for multiple different Writers/Readers. 265 * for multiple different Writers/Readers.
332 */ 266 */
333 Reader(this.serialization) { 267 Reader(this.serialization, [Format newFormat]) {
334 selfDescribing = serialization.selfDescribing; 268 selfDescribing = serialization.selfDescribing;
269 if (newFormat != null) format = newFormat;
335 } 270 }
336 271
337 /** 272 /**
338 * When we read, we may need to look up objects by name in order to link to 273 * When we read, we may need to look up objects by name in order to link to
339 * them. This is particularly true if we have references to classes, 274 * them. This is particularly true if we have references to classes,
340 * functions, mirrors, or other non-portable entities. The map in which we 275 * functions, mirrors, or other non-portable entities. The map in which we
341 * look things up can be provided as an argument to read, but we can also 276 * look things up can be provided as an argument to read, but we can also
342 * provide a map here, and objects will be looked up in both places. 277 * provide a map here, and objects will be looked up in both places.
343 */ 278 */
344 Map namedObjects; 279 Map namedObjects;
(...skipping 22 matching lines...) Expand all
367 * to a List of Lists whose size must match the number of rules. 302 * to a List of Lists whose size must match the number of rules.
368 */ 303 */
369 // When we set the data, initialize the object storage to a matching size. 304 // When we set the data, initialize the object storage to a matching size.
370 void set data(List<List> newData) { 305 void set data(List<List> newData) {
371 _data = newData; 306 _data = newData;
372 objects = _data.mappedBy((x) => new List(x.length)).toList(); 307 objects = _data.mappedBy((x) => new List(x.length)).toList();
373 } 308 }
374 309
375 /** 310 /**
376 * This is the primary method for a [Reader]. It takes the input data, 311 * This is the primary method for a [Reader]. It takes the input data,
377 * currently hard-coded to expect our custom JSON format, and returns 312 * decodes it according to [format] and returns the root object.
378 * the root object.
379 */ 313 */
380 read(String input, [Map externals = const {}]) { 314 read(rawInput, [Map externals = const {}]) {
381 namedObjects = externals; 315 namedObjects = externals;
382 var topLevel = json.parse(input); 316 var input = format.read(rawInput, this);
383 var ruleString = topLevel["rules"]; 317 data = input["data"];
384 readRules(ruleString, externals);
385 data = topLevel["data"];
386 rules.forEach(inflateForRule); 318 rules.forEach(inflateForRule);
387 var roots = topLevel["roots"]; 319 return inflateReference(input["roots"].first);
388 return inflateReference(roots.first);
389 } 320 }
390 321
391 /** 322 /**
392 * If the data we are reading from has rules written to it, read them back 323 * If the data we are reading from has rules written to it, read them back
393 * and set them as the rules we will use. 324 * and set them as the rules we will use.
394 */ 325 */
395 void readRules(String newRules, Map externals) { 326 void readRules(String newRules) {
396 // TODO(alanknight): Replacing the serialization is kind of confusing. 327 // TODO(alanknight): Replacing the serialization is kind of confusing.
397 List rulesWeRead = (newRules == null) ? 328 List rulesWeRead = (newRules == null) ?
398 null : serialization._ruleSerialization().read(newRules, externals); 329 null : serialization.ruleSerialization().read(newRules, namedObjects);
399 if (rulesWeRead != null && !rulesWeRead.isEmpty) { 330 if (rulesWeRead != null && !rulesWeRead.isEmpty) {
400 serialization = new Serialization.blank(); 331 serialization = new Serialization.blank();
401 rulesWeRead.forEach(serialization.addRule); 332 rulesWeRead.forEach(serialization.addRule);
402 } 333 }
403 } 334 }
404 335
405 /** 336 /**
406 * This is a hard-coded read method for a vaguely flat format. It's just a
407 * proof of concept of handling more flat formats right now, and needs a lot
408 * of fixing and generalization.
409 */
410 readFlat(List input, [Map externals = const {}]) {
411 // TODO(alanknight): Way too much code duplication with read. Numerous
412 // code smells.
413 namedObjects = externals;
414 var topLevel = input;
415 var ruleString = topLevel[0];
416 readRules(ruleString, externals);
417 var flatData = topLevel[1];
418 var stream = flatData.iterator;
419 var tempData = new List(rules.length);
420 for (var eachRule in rules) {
421 tempData[eachRule.number] = eachRule.pullStateFrom(stream);
422 }
423 data = tempData;
424 for (var eachRule in rules) {
425 inflateForRule(eachRule);
426 }
427 var rootsAsInts = topLevel[2];
428 var rootStream = rootsAsInts.iterator;
429 var roots = new List();
430 while (rootStream.moveNext()) {
431 var first = rootStream.current;
432 rootStream.moveNext();
433 var second = rootStream.current;
434 roots.add(new Reference(this, first, second));
435 }
436 var x = inflateReference(roots[0]);
437 return inflateReference(roots.first);
438 }
439
440 /**
441 * Inflate all of the objects for [rule]. Does the essential state for all 337 * Inflate all of the objects for [rule]. Does the essential state for all
442 * objects first, then the non-essential state. This avoids cycles in 338 * objects first, then the non-essential state. This avoids cycles in
443 * non-essential state, because all the objects will have already been 339 * non-essential state, because all the objects will have already been
444 * created. 340 * created.
445 */ 341 */
446 inflateForRule(rule) { 342 inflateForRule(rule) {
447 var dataForThisRule = _data[rule.number]; 343 var dataForThisRule = _data[rule.number];
448 keysAndValues(dataForThisRule).forEach((position, state) { 344 keysAndValues(dataForThisRule).forEach((position, state) {
449 inflateOne(rule, position, state); 345 inflateOne(rule, position, state);
450 }); 346 });
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
504 400
505 /** Given [reference], return the the state we have stored for it. */ 401 /** Given [reference], return the the state we have stored for it. */
506 _stateFor(Reference reference) => 402 _stateFor(Reference reference) =>
507 _data[reference.ruleNumber][reference.objectNumber]; 403 _data[reference.ruleNumber][reference.objectNumber];
508 404
509 /** Given a reference, return the rule it references. */ 405 /** Given a reference, return the rule it references. */
510 SerializationRule ruleFor(Reference reference) => 406 SerializationRule ruleFor(Reference reference) =>
511 serialization._rules[reference.ruleNumber]; 407 serialization._rules[reference.ruleNumber];
512 408
513 /** 409 /**
410 * Return the primitive rule we are using. This is an ugly mechanism to
411 * support the extra information to reconstruct objects in the
412 * [SimpleJsonFormat].
413 */
414 SerializationRule _primitiveRule() {
415 for (var each in rules) {
416 if (each.runtimeType == PrimitiveRule) {
417 return each;
418 }
419 }
420 throw new SerializationException("No PrimitiveRule found");
421 }
422
423 /**
514 * Given a possible reference [anObject], call either [ifReference] or 424 * Given a possible reference [anObject], call either [ifReference] or
515 * [ifNotReference], depending if it's a reference or not. This is the 425 * [ifNotReference], depending if it's a reference or not. This is the
516 * primary place that knows about the serialized representation of a 426 * primary place that knows about the serialized representation of a
517 * reference. 427 * reference.
518 */ 428 */
519 asReference(anObject, {Function ifReference: doNothing, 429 asReference(anObject, {Function ifReference: doNothing,
520 Function ifNotReference : doNothing}) { 430 Function ifNotReference : doNothing}) {
521 if (anObject is Reference) return ifReference(anObject); 431 if (anObject is Reference) return ifReference(anObject);
522 if (anObject is Map && anObject["__Ref"] == true) { 432 if (anObject is Map && anObject["__Ref"] == true) {
523 var ref = 433 var ref =
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
627 "__Ref" : true, 537 "__Ref" : true,
628 "rule" : ruleNumber, 538 "rule" : ruleNumber,
629 "object" : objectNumber 539 "object" : objectNumber
630 }; 540 };
631 541
632 /** Write our information to [list]. Useful in writing to flat formats.*/ 542 /** Write our information to [list]. Useful in writing to flat formats.*/
633 writeToList(List list) { 543 writeToList(List list) {
634 list.add(ruleNumber); 544 list.add(ruleNumber);
635 list.add(objectNumber); 545 list.add(objectNumber);
636 } 546 }
547
548 toString() => "Reference $ruleNumber, $objectNumber";
637 } 549 }
638 550
639 /** 551 /**
640 * This is used during tracing to indicate that an object should be processed 552 * This is used during tracing to indicate that an object should be processed
641 * using a particular rule, rather than the one that might ordinarily be 553 * using a particular rule, rather than the one that might ordinarily be
642 * found for it. This normally only makes sense if the object is uniquely 554 * found for it. This normally only makes sense if the object is uniquely
643 * referenced, and is a more or less internal collection. See ListRuleEssential 555 * referenced, and is a more or less internal collection. See ListRuleEssential
644 * for an example. It knows how to return its object and how to filter. 556 * for an example. It knows how to return its object and how to filter.
645 */ 557 */
646 class DesignatedRuleForObject { 558 class DesignatedRuleForObject {
647 Function rulePredicate; 559 Function rulePredicate;
648 final target; 560 final target;
649 561
650 DesignatedRuleForObject(this.target, this.rulePredicate); 562 DesignatedRuleForObject(this.target, this.rulePredicate);
651 563
652 possibleRules(List rules) => rules.where(rulePredicate).toList(); 564 possibleRules(List rules) => rules.where(rulePredicate).toList();
653 } 565 }
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