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

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

Issue 11931030: Add a MapRule (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Changes from review comments Created 7 years, 10 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
« no previous file with comments | « pkg/serialization/lib/src/basic_rule.dart ('k') | pkg/serialization/lib/src/reader_writer.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 part of serialization; 1 part of serialization;
2 2
3 /** 3 /**
4 * An abstract class for serialization formats. Subclasses define how data 4 * An abstract class for serialization formats. Subclasses define how data
5 * is read or written to a particular output mechanism. 5 * is read or written to a particular output mechanism.
6 */ 6 */
7 abstract class Format { 7 abstract class Format {
8 /** 8 /**
9 * Return true if this format stores primitives in their own area and uses 9 * Return true if this format stores primitives in their own area and uses
10 * references to them (e.g. [SimpleFlatFormat]) and false if primitives 10 * references to them (e.g. [SimpleFlatFormat]) and false if primitives
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 class SimpleJsonFormat extends Format { 88 class SimpleJsonFormat extends Format {
89 89
90 /** 90 /**
91 * Indicate if we should store rule numbers with map/list data so that we 91 * Indicate if we should store rule numbers with map/list data so that we
92 * will know how to reconstruct it with a read operation. If we don't, this 92 * will know how to reconstruct it with a read operation. If we don't, this
93 * will be more compliant with things that expect known format JSON as input, 93 * will be more compliant with things that expect known format JSON as input,
94 * but we won't be able to read back the objects. 94 * but we won't be able to read back the objects.
95 */ 95 */
96 final bool storeRoundTripInfo; 96 final bool storeRoundTripInfo;
97 97
98 /**
99 * If we store the rule numbers, what key should we use to store them.
100 */
101 String ruleIdentifier = "__rule";
102
98 SimpleJsonFormat({this.storeRoundTripInfo : false}); 103 SimpleJsonFormat({this.storeRoundTripInfo : false});
99 104
100 /** 105 /**
101 * Generate output for this format from [w] and return it as a String which 106 * Generate output for this format from [w] and return it as a String which
102 * is the [json] representation of a nested Map structure. 107 * is the [json] representation of a nested Map structure.
103 */ 108 */
104 String generateOutput(Writer w) { 109 String generateOutput(Writer w) {
105 jsonify(w); 110 jsonify(w);
106 return json.stringify(w.stateForReference(w._rootReferences().first)); 111 return json.stringify(w.stateForReference(w._rootReferences().first));
107 } 112 }
(...skipping 14 matching lines...) Expand all
122 * For a particular [rule] modify the [ruleData] to conform to this format. 127 * For a particular [rule] modify the [ruleData] to conform to this format.
123 */ 128 */
124 jsonifyForRule(List ruleData, Writer w, SerializationRule rule) { 129 jsonifyForRule(List ruleData, Writer w, SerializationRule rule) {
125 for (var i = 0; i < ruleData.length; i++) { 130 for (var i = 0; i < ruleData.length; i++) {
126 var each = ruleData[i]; 131 var each = ruleData[i];
127 if (each is List) { 132 if (each is List) {
128 jsonifyEntry(each, w); 133 jsonifyEntry(each, w);
129 if (storeRoundTripInfo) ruleData[i].add(rule.number); 134 if (storeRoundTripInfo) ruleData[i].add(rule.number);
130 } else if (each is Map) { 135 } else if (each is Map) {
131 jsonifyEntry(each, w); 136 jsonifyEntry(each, w);
132 if (storeRoundTripInfo) each["__rule"] = rule.number; 137 if (storeRoundTripInfo) each[ruleIdentifier] = rule.number;
133 } 138 }
134 } 139 }
135 } 140 }
136 141
137 /** 142 /**
138 * For one particular entry, which is either a Map or a List, update it 143 * For one particular entry, which is either a Map or a List, update it
139 * to turn References into a nested List/Map. 144 * to turn References into a nested List/Map.
140 */ 145 */
141 jsonifyEntry(map, Writer w) { 146 jsonifyEntry(map, Writer w) {
142 keysAndValues(map).forEach((key, value) { 147 keysAndValues(map).forEach((key, value) {
143 if (value is Reference) map[key] = w.stateForReference(value); 148 if (value is Reference) map[key] = w.stateForReference(value);
144 }); 149 });
145 } 150 }
146 151
147 /** 152 /**
148 * Read a [json] encoded string representing serialized data in this format 153 * Read a [json] encoded string representing serialized data in this format
149 * and return the Map representation that the reader expects, with top-level 154 * and return the Map representation that the reader expects, with top-level
150 * entries for "rules", "data", and "roots". Nested lists/maps will be 155 * entries for "rules", "data", and "roots". Nested lists/maps will be
151 * converted into Reference objects. Note that if the data was not written 156 * converted into Reference objects. Note that if the data was not written
152 * with [storeRoundTripInfo] true this will fail. 157 * with [storeRoundTripInfo] true this will fail.
153 */ 158 */
154 Map<String, dynamic> read(String input, Reader r) { 159 Map<String, dynamic> read(String input, Reader r) {
155 var data = json.parse(input); 160 var data = json.parse(input);
156 var result = {}; 161 var result = {};
157 result["rules"] = null; 162 result["rules"] = null;
158 var ruleData = 163 var ruleData =
159 new List(r.serialization.rules.length).mappedBy((x) => []).toList(); 164 new List(r.serialization.rules.length).mappedBy((x) => []).toList();
160 var rootRule = data["__rule"];
161 var top = recursivelyFixUp(data, r, ruleData); 165 var top = recursivelyFixUp(data, r, ruleData);
162 result["data"] = ruleData; 166 result["data"] = ruleData;
163 result["roots"] = [top]; 167 result["roots"] = [top];
164 return result; 168 return result;
165 } 169 }
166 170
167 /** 171 /**
168 * Convert nested references in [data] into [Reference] objects. 172 * Convert nested references in [data] into [Reference] objects.
169 */ 173 */
170 recursivelyFixUp(data, Reader r, List result) { 174 recursivelyFixUp(data, Reader r, List result) {
171 if (isPrimitive(data)) { 175 if (isPrimitive(data)) {
172 result[r._primitiveRule().number].add(data); 176 result[r._primitiveRule().number].add(data);
173 return data; 177 return data;
174 } 178 }
175 var ruleNumber = 179 var ruleNumber;
176 (data is List) ? data.removeLast() : data.remove("__rule"); 180 if (data is List) {
181 ruleNumber = data.removeLast();
182 } else if (data is Map) {
183 ruleNumber = data.remove(ruleIdentifier);
184 } else {
185 throw new SerializationException("Invalid data format");
186 }
177 var newData = values(data).mappedBy( 187 var newData = values(data).mappedBy(
178 (x) => recursivelyFixUp(x, r, result)); 188 (x) => recursivelyFixUp(x, r, result));
189 // TODO(alanknight): Ugh. Get rid of this if we can resolve bug 7982/7940.
190 if (newData is MappedList) {
191 newData = newData.toList();
192 }
179 result[ruleNumber].add(newData); 193 result[ruleNumber].add(newData);
180 return new Reference(this, ruleNumber, result[ruleNumber].length - 1); 194 return new Reference(r, ruleNumber, result[ruleNumber].length - 1);
181 } 195 }
182 } 196 }
183 197
184 /** 198 /**
185 * Writes to a simple mostly-flat format. Details are subject to change. 199 * Writes to a simple mostly-flat format. Details are subject to change.
186 * Right now this produces a List containing null, num, and String. This is 200 * Right now this produces a List containing null, num, and String. This is
187 * more space-efficient than the map formats, but much less human-readable. 201 * more space-efficient than the map formats, but much less human-readable.
188 * Simple usage is to turn this into JSON for transmission. 202 * Simple usage is to turn this into JSON for transmission.
189 */ 203 */
190 class SimpleFlatFormat extends Format { 204 class SimpleFlatFormat extends Format {
(...skipping 27 matching lines...) Expand all
218 w._rootReferences().forEach((x) => x.writeToList(result[2])); 232 w._rootReferences().forEach((x) => x.writeToList(result[2]));
219 return result; 233 return result;
220 } 234 }
221 235
222 /** 236 /**
223 * Writes the data from [rule] into the [target] list. 237 * Writes the data from [rule] into the [target] list.
224 */ 238 */
225 void writeStateInto(SerializationRule rule, List ruleData, List target) { 239 void writeStateInto(SerializationRule rule, List ruleData, List target) {
226 if (!ruleData.isEmpty) { 240 if (!ruleData.isEmpty) {
227 var sample = ruleData.first; 241 var sample = ruleData.first;
228 if (sample is List) { 242 if (rule.storesStateAsLists || sample is List) {
229 writeLists(rule, ruleData, target); 243 writeLists(rule, ruleData, target);
230 } else if (sample is Map) { 244 } else if (rule.storesStateAsMaps || sample is Map) {
231 writeMaps(rule, ruleData, target); 245 writeMaps(rule, ruleData, target);
246 } else if (rule.storesStateAsPrimitives || isPrimitive(sample)) {
247 writeObjects(ruleData, target);
232 } else { 248 } else {
233 writeObjects(ruleData, target); 249 throw new SerializationException("Invalid data format");
234 } 250 }
235 } else { 251 } else {
236 // If there is no data, write a zero for the length. 252 // If there is no data, write a zero for the length.
237 target.add(0); 253 target.add(0);
238 } 254 }
239 } 255 }
240 256
241 /** 257 /**
242 * Write [entries], which contains Lists. Either the lists are variable 258 * Write [entries], which contains Lists. Either the lists are variable
243 * length, in which case we add a length field, or they are fixed length, in 259 * length, in which case we add a length field, or they are fixed length, in
(...skipping 20 matching lines...) Expand all
264 * right length when we read it back. Then we write alternating keys and 280 * right length when we read it back. Then we write alternating keys and
265 * values. We expect the values to be references, which we store as 281 * values. We expect the values to be references, which we store as
266 * two numbers. 282 * two numbers.
267 */ 283 */
268 writeMaps(SerializationRule rule, List<Map> entries, List target) { 284 writeMaps(SerializationRule rule, List<Map> entries, List target) {
269 target.add(STORED_AS_MAP); 285 target.add(STORED_AS_MAP);
270 for (var eachEntry in entries) { 286 for (var eachEntry in entries) {
271 if (rule.hasVariableLengthEntries) { 287 if (rule.hasVariableLengthEntries) {
272 target.add(eachEntry.length); 288 target.add(eachEntry.length);
273 } 289 }
274 // We take advantage of this being only a semi-flat format, and expecting
275 // that the keys here are field names, i.e. strings. So we write
276 // the keys as literals and the values as references. This duplicates the
277 // keys, so is quite inefficient. But generating maps rather than lists is
278 // not very efficient in the first place.
279 eachEntry.forEach((key, value) { 290 eachEntry.forEach((key, value) {
280 target.add(key); 291 writeReference(key, target);
281 writeReference(value, target); 292 writeReference(value, target);
282 }); 293 });
283 } 294 }
284 } 295 }
285 296
286 /** 297 /**
287 * Write [entries], which contains simple objects which we can put directly 298 * Write [entries], which contains simple objects which we can put directly
288 * into [target]. 299 * into [target].
289 */ 300 */
290 writeObjects(List entries, List target) { 301 writeObjects(List entries, List target) {
291 target.add(STORED_AS_PRIMITIVE); 302 target.add(STORED_AS_PRIMITIVE);
303 for (var each in entries) {
304 if (!isPrimitive(each)) throw new SerializationException("Invalid data");
305 }
292 target.addAll(entries); 306 target.addAll(entries);
293 } 307 }
294 308
295 /** 309 /**
296 * Write [eachRef] to [target]. It will be written as two ints. If [eachRef] 310 * Write [eachRef] to [target]. It will be written as two ints. If [eachRef]
297 * is null it will be written as two nulls. 311 * is null it will be written as two nulls.
298 */ 312 */
299 void writeReference(Reference eachRef, List target) { 313 void writeReference(Reference eachRef, List target) {
300 // TODO(alanknight): Writing nulls is problematic in a real flat format. 314 // TODO(alanknight): Writing nulls is problematic in a real flat format.
301 if (eachRef == null) { 315 if (eachRef == null) {
302 target..add(null)..add(null); 316 target..add(null)..add(null);
303 } else { 317 } else {
304 eachRef.writeToList(target); 318 eachRef.writeToList(target);
305 } 319 }
306 } 320 }
307 321
308 /** 322 /**
309 * Read the data from [rawInput] in the context of [r] and return it as a 323 * Read the data from [rawInput] in the context of [r] and return it as a
310 * Map with entries for "roots", "data" and "rules", which the reader knows 324 * Map with entries for "roots", "data" and "rules", which the reader knows
311 * how to interpret. We expect [rawInput] to have been generated from this 325 * how to interpret. We expect [rawInput] to have been generated from this
312 * format. 326 * format.
313 */ 327 */
314 Map<String, dynamic> read(List rawInput, Reader r) { 328 Map<String, dynamic> read(List rawInput, Reader r) {
329 // TODO(alanknight): It's annoying to have to pass the reader around so
330 // much, consider having the format be specific to a particular
331 // serialization operation along with the reader and having it as a field.
315 var input = {}; 332 var input = {};
316 input["rules"] = rawInput[0]; 333 input["rules"] = rawInput[0];
317 r.readRules(input["rules"]); 334 r.readRules(input["rules"]);
318 335
319 var flatData = rawInput[1]; 336 var flatData = rawInput[1];
320 var stream = flatData.iterator; 337 var stream = flatData.iterator;
321 var tempData = new List(r.rules.length); 338 var tempData = new List(r.rules.length);
322 for (var eachRule in r.rules) { 339 for (var eachRule in r.rules) {
323 tempData[eachRule.number] = readRuleDataFrom(stream, eachRule); 340 tempData[eachRule.number] = readRuleDataFrom(stream, eachRule, r);
324 } 341 }
325 input["data"] = tempData; 342 input["data"] = tempData;
326 343
327 var roots = []; 344 var roots = [];
328 var rootsAsInts = rawInput[2].iterator; 345 var rootsAsInts = rawInput[2].iterator;
329 do { 346 do {
330 roots.add(nextReferenceFrom(rootsAsInts)); 347 roots.add(nextReferenceFrom(rootsAsInts, r));
331 } while (rootsAsInts.current != null); 348 } while (rootsAsInts.current != null);
332 349
333 input["roots"] = roots; 350 input["roots"] = roots;
334 return input; 351 return input;
335 } 352 }
336 353
337 /** 354 /**
338 * Read the data for [rule] from [input] and return it. 355 * Read the data for [rule] from [input] and return it.
339 */ 356 */
340 readRuleDataFrom(Iterator input, SerializationRule rule) { 357 readRuleDataFrom(Iterator input, SerializationRule rule, Reader r) {
341 var numberOfEntries = _next(input); 358 var numberOfEntries = _next(input);
342 var entryType = _next(input); 359 var entryType = _next(input);
343 if (entryType == STORED_AS_LIST) { 360 if (entryType == STORED_AS_LIST) {
344 return readLists(input, rule, numberOfEntries); 361 return readLists(input, rule, numberOfEntries, r);
345 } 362 }
346 if (entryType == STORED_AS_MAP) { 363 if (entryType == STORED_AS_MAP) {
347 return readMaps(input, rule, numberOfEntries); 364 return readMaps(input, rule, numberOfEntries, r);
348 } 365 }
349 if (entryType == STORED_AS_PRIMITIVE) { 366 if (entryType == STORED_AS_PRIMITIVE) {
350 return readPrimitives(input, rule, numberOfEntries); 367 return readPrimitives(input, rule, numberOfEntries);
351 } 368 }
352 if (numberOfEntries == 0) { 369 if (numberOfEntries == 0) {
353 return []; 370 return [];
354 } else { 371 } else {
355 throw new SerializationException("Invalid data in serialization"); 372 throw new SerializationException("Invalid data in serialization");
356 } 373 }
357 } 374 }
358 375
359 /** 376 /**
360 * Read data for [rule] from [input] with [length] number of entries, 377 * Read data for [rule] from [input] with [length] number of entries,
361 * creating lists from the results. 378 * creating lists from the results.
362 */ 379 */
363 readLists(Iterator input, SerializationRule rule, int length) { 380 readLists(Iterator input, SerializationRule rule, int length, Reader r) {
364 var ruleData = []; 381 var ruleData = [];
365 for (var i = 0; i < length; i++) { 382 for (var i = 0; i < length; i++) {
366 var subLength = 383 var subLength =
367 rule.hasVariableLengthEntries ? _next(input) : rule.dataLength; 384 rule.hasVariableLengthEntries ? _next(input) : rule.dataLength;
368 var subList = []; 385 var subList = [];
369 ruleData.add(subList); 386 ruleData.add(subList);
370 for (var j = 0; j < subLength; j++) { 387 for (var j = 0; j < subLength; j++) {
371 subList.add(nextReferenceFrom(input)); 388 subList.add(nextReferenceFrom(input, r));
372 } 389 }
373 } 390 }
374 return ruleData; 391 return ruleData;
375 } 392 }
376 393
377 /** 394 /**
378 * Read data for [rule] from [input] with [length] number of entries, 395 * Read data for [rule] from [input] with [length] number of entries,
379 * creating maps from the results. 396 * creating maps from the results.
380 */ 397 */
381 readMaps(Iterator input, SerializationRule rule, int length) { 398 readMaps(Iterator input, SerializationRule rule, int length, Reader r) {
382 var ruleData = []; 399 var ruleData = [];
383 for (var i = 0; i < length; i++) { 400 for (var i = 0; i < length; i++) {
384 var subLength = 401 var subLength =
385 rule.hasVariableLengthEntries ? _next(input) : rule.dataLength; 402 rule.hasVariableLengthEntries ? _next(input) : rule.dataLength;
386 var map = {}; 403 var map = new Map();
387 ruleData.add(map); 404 ruleData.add(map);
388 for (var j = 0; j < subLength; j++) { 405 for (var j = 0; j < subLength; j++) {
389 map[_next(input)] = nextReferenceFrom(input); 406 var key = nextReferenceFrom(input, r);
407 var value = nextReferenceFrom(input, r);
408 map[key] = value;
390 } 409 }
391 } 410 }
392 return ruleData; 411 return ruleData;
393 } 412 }
394 413
395 /** 414 /**
396 * Read data for [rule] from [input] with [length] number of entries, 415 * Read data for [rule] from [input] with [length] number of entries,
397 * treating the data as primitives that can be returned directly. 416 * treating the data as primitives that can be returned directly.
398 */ 417 */
399 readPrimitives(Iterator input, SerializationRule rule, int length) { 418 readPrimitives(Iterator input, SerializationRule rule, int length) {
400 var ruleData = []; 419 var ruleData = [];
401 for (var i = 0; i < length; i++) { 420 for (var i = 0; i < length; i++) {
402 ruleData.add(_next(input)); 421 ruleData.add(_next(input));
403 } 422 }
404 return ruleData; 423 return ruleData;
405 } 424 }
406 425
407 /** Read the next Reference from the input. */ 426 /** Read the next Reference from the input. */
408 nextReferenceFrom(Iterator input) { 427 nextReferenceFrom(Iterator input, Reader r) {
409 var a = _next(input); 428 var a = _next(input);
410 var b = _next(input); 429 var b = _next(input);
411 if (a == null) { 430 if (a == null) {
412 return null; 431 return null;
413 } else { 432 } else {
414 return new Reference(this, a, b); 433 return new Reference(r, a, b);
415 } 434 }
416 } 435 }
417 436
418 /** Return the next element from the input. */ 437 /** Return the next element from the input. */
419 _next(Iterator input) { 438 _next(Iterator input) {
420 input.moveNext(); 439 input.moveNext();
421 return input.current; 440 return input.current;
422 } 441 }
423 } 442 }
OLDNEW
« no previous file with comments | « pkg/serialization/lib/src/basic_rule.dart ('k') | pkg/serialization/lib/src/reader_writer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698