| OLD | NEW |
| 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 const Format(); | 9 const Format(); |
| 10 | 10 |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 reader._data = topLevel["data"]; | 73 reader._data = topLevel["data"]; |
| 74 topLevel["roots"] = topLevel["roots"]; | 74 topLevel["roots"] = topLevel["roots"]; |
| 75 return topLevel; | 75 return topLevel; |
| 76 } | 76 } |
| 77 } | 77 } |
| 78 | 78 |
| 79 /** | 79 /** |
| 80 * A format that stores the data in maps which can be converted into a JSON | 80 * A format that stores the data in maps which can be converted into a JSON |
| 81 * string or passed through an isolate. Note that this consists of maps, but | 81 * string or passed through an isolate. Note that this consists of maps, but |
| 82 * that they don't follow the original object structure or look like the nested | 82 * that they don't follow the original object structure or look like the nested |
| 83 * maps of a [json] representation. They are flat, and [Reference] objects | 83 * maps of a JSON representation. They are flat, and [Reference] objects |
| 84 * are converted into a map form that will not make sense to | 84 * are converted into a map form that will not make sense to |
| 85 * anything but this format. For simple acyclic JSON that other programs | 85 * anything but this format. For simple acyclic JSON that other programs |
| 86 * can read, use [SimpleJsonFormat]. This is the default format, and is | 86 * can read, use [SimpleJsonFormat]. This is the default format, and is |
| 87 * easier to read than the more efficient [SimpleFlatFormat]. | 87 * easier to read than the more efficient [SimpleFlatFormat]. |
| 88 */ | 88 */ |
| 89 class SimpleMapFormat extends InternalMapFormat { | 89 class SimpleMapFormat extends InternalMapFormat { |
| 90 | 90 |
| 91 const SimpleMapFormat(); | 91 const SimpleMapFormat(); |
| 92 | 92 |
| 93 /** | 93 /** |
| 94 * Generate output for this format from [w] and return it as a String which | 94 * Generate output for this format from [w] and return it as a String which |
| 95 * is the [json] representation of a nested Map structure. The top level has | 95 * is the JSON representation of a nested Map structure. The top level has |
| 96 * 3 fields, "rules" which may hold a definition of the rules used, | 96 * 3 fields, "rules" which may hold a definition of the rules used, |
| 97 * "data" which holds the serialized data, and "roots", which holds | 97 * "data" which holds the serialized data, and "roots", which holds |
| 98 * [Reference] objects indicating the root objects. Note that roots are | 98 * [Reference] objects indicating the root objects. Note that roots are |
| 99 * necessary because the data is not organized in the same way as the object | 99 * necessary because the data is not organized in the same way as the object |
| 100 * structure, it's a list of lists holding self-contained maps which only | 100 * structure, it's a list of lists holding self-contained maps which only |
| 101 * refer to other parts via [Reference] objects. | 101 * refer to other parts via [Reference] objects. |
| 102 * This effectively defines a custom JSON serialization format, although | 102 * This effectively defines a custom JSON serialization format, although |
| 103 * the details of the format vary depending which rules were used. | 103 * the details of the format vary depending which rules were used. |
| 104 */ | 104 */ |
| 105 Map<String, dynamic> generateOutput(Writer w) { | 105 Map<String, dynamic> generateOutput(Writer w) { |
| 106 forAllStates(w, (x) => x is Reference, referenceToMap); | 106 forAllStates(w, (x) => x is Reference, referenceToMap); |
| 107 var result = super.generateOutput(w); | 107 var result = super.generateOutput(w); |
| 108 result["roots"] = result["roots"].map( | 108 result["roots"] = result["roots"].map( |
| 109 (x) => x is Reference ? referenceToMap(x) : x).toList(); | 109 (x) => x is Reference ? referenceToMap(x) : x).toList(); |
| 110 return result; | 110 return result; |
| 111 } | 111 } |
| 112 | 112 |
| 113 /** | 113 /** |
| 114 * Convert the data generated by the rules to have maps with the fields | 114 * Convert the data generated by the rules to have maps with the fields |
| 115 * of [Reference] objects instead of the [Reference] so that the structure | 115 * of [Reference] objects instead of the [Reference] so that the structure |
| 116 * can be serialized between isolates and json easily. | 116 * can be serialized between isolates and json easily. |
| 117 */ | 117 */ |
| 118 forAllStates(ReaderOrWriter w, bool predicate(value), | 118 void forAllStates(ReaderOrWriter w, bool predicate(value), |
| 119 void transform(value)) { | 119 void transform(value)) { |
| 120 for (var eachRule in w.rules) { | 120 for (var eachRule in w.rules) { |
| 121 var ruleData = w.states[eachRule.number]; | 121 var ruleData = w.states[eachRule.number]; |
| 122 for (var data in ruleData) { | 122 for (var data in ruleData) { |
| 123 keysAndValues(data).forEach((key, value) { | 123 keysAndValues(data).forEach((key, value) { |
| 124 if (predicate(value)) { | 124 if (predicate(value)) { |
| 125 data[key] = transform(value); | 125 data[key] = transform(value); |
| 126 } | 126 } |
| 127 }); | 127 }); |
| 128 } | 128 } |
| 129 } | 129 } |
| 130 } | 130 } |
| 131 | 131 |
| 132 /** Convert the reference to a [json] serializable form. */ | 132 /** Convert the reference to a JSON serializable form. */ |
| 133 Map<String, int> referenceToMap(Reference ref) => ref == null ? null : | 133 Map<String, int> referenceToMap(Reference ref) => ref == null ? null : |
| 134 { | 134 { |
| 135 "__Ref" : 0, | 135 "__Ref" : 0, |
| 136 "rule" : ref.ruleNumber, | 136 "rule" : ref.ruleNumber, |
| 137 "object" : ref.objectNumber | 137 "object" : ref.objectNumber |
| 138 }; | 138 }; |
| 139 | 139 |
| 140 /** | 140 /** |
| 141 * Convert the [referenceToMap] form for a reference back to a [Reference] | 141 * Convert the [referenceToMap] form for a reference back to a [Reference] |
| 142 * object. | 142 * object. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 156 (ref) => ref is Map && ref["__Ref"] != null, | 156 (ref) => ref is Map && ref["__Ref"] != null, |
| 157 (ref) => mapToReference(reader, ref)); | 157 (ref) => mapToReference(reader, ref)); |
| 158 topLevel["roots"] = topLevel["roots"] | 158 topLevel["roots"] = topLevel["roots"] |
| 159 .map((x) => x is Map<String, int> ? mapToReference(reader, x) : x) | 159 .map((x) => x is Map<String, int> ? mapToReference(reader, x) : x) |
| 160 .toList(); | 160 .toList(); |
| 161 return topLevel; | 161 return topLevel; |
| 162 } | 162 } |
| 163 } | 163 } |
| 164 | 164 |
| 165 /** | 165 /** |
| 166 * A format for "normal" [json] representation of objects. It stores | 166 * A format for "normal" JSON representation of objects. It stores |
| 167 * the fields of the objects as nested maps, and doesn't allow cycles. This can | 167 * the fields of the objects as nested maps, and doesn't allow cycles. This can |
| 168 * be useful in talking to existing APIs that expect [json] format data. The | 168 * be useful in talking to existing APIs that expect JSON format data. The |
| 169 * output will be either a simple object (string, num, bool), a List, or a Map, | 169 * output will be either a simple object (string, num, bool), a List, or a Map, |
| 170 * with nesting of those. | 170 * with nesting of those. |
| 171 * Note that since the classes of objects aren't normally stored, this isn't | 171 * Note that since the classes of objects aren't normally stored, this isn't |
| 172 * enough information to read back the objects. However, if the | 172 * enough information to read back the objects. However, if the |
| 173 * If the [storeRoundTripInfo] field of the format is set to true, then this | 173 * If the [storeRoundTripInfo] field of the format is set to true, then this |
| 174 * will store the rule number along with the data, allowing reconstruction. | 174 * will store the rule number along with the data, allowing reconstruction. |
| 175 */ | 175 */ |
| 176 class SimpleJsonFormat extends SimpleMapFormat { | 176 class SimpleJsonFormat extends SimpleMapFormat { |
| 177 | 177 |
| 178 /** | 178 /** |
| 179 * Indicate if we should store rule numbers with map/list data so that we | 179 * Indicate if we should store rule numbers with map/list data so that we |
| 180 * will know how to reconstruct it with a read operation. If we don't, this | 180 * will know how to reconstruct it with a read operation. If we don't, this |
| 181 * will be more compliant with things that expect known format JSON as input, | 181 * will be more compliant with things that expect known format JSON as input, |
| 182 * but we won't be able to read back the objects. | 182 * but we won't be able to read back the objects. |
| 183 */ | 183 */ |
| 184 final bool storeRoundTripInfo; | 184 final bool storeRoundTripInfo; |
| 185 | 185 |
| 186 /** | 186 /** |
| 187 * If we store the rule numbers, what key should we use to store them. | 187 * If we store the rule numbers, what key should we use to store them. |
| 188 */ | 188 */ |
| 189 static const String RULE = "_rule"; | 189 static const String RULE = "_rule"; |
| 190 static const String RULES = "_rules"; | 190 static const String RULES = "_rules"; |
| 191 static const String DATA = "_data"; | 191 static const String DATA = "_data"; |
| 192 static const String ROOTS = "_root"; | 192 static const String ROOTS = "_root"; |
| 193 | 193 |
| 194 const SimpleJsonFormat({this.storeRoundTripInfo : false}); | 194 const SimpleJsonFormat({this.storeRoundTripInfo : false}); |
| 195 | 195 |
| 196 /** | 196 /** |
| 197 * Generate output for this format from [w] and return it as | 197 * Generate output for this format from [w] and return it as |
| 198 * the [json] representation of a nested Map structure. | 198 * the JSON representation of a nested Map structure. |
| 199 */ | 199 */ |
| 200 generateOutput(Writer w) { | 200 generateOutput(Writer w) { |
| 201 jsonify(w); | 201 jsonify(w); |
| 202 var root = w._rootReferences().first; | 202 var root = w._rootReferences().first; |
| 203 if (root is Reference) root = w.stateForReference(root); | 203 if (root is Reference) root = w.stateForReference(root); |
| 204 if (w.selfDescribing && storeRoundTripInfo) { | 204 if (w.selfDescribing && storeRoundTripInfo) { |
| 205 root = new Map() | 205 root = new Map() |
| 206 ..[RULES] = w.serializedRules() | 206 ..[RULES] = w.serializedRules() |
| 207 ..[DATA] = root; | 207 ..[DATA] = root; |
| 208 } | 208 } |
| 209 return root; | 209 return root; |
| 210 } | 210 } |
| 211 | 211 |
| 212 /** | 212 /** |
| 213 * Convert the data generated by the rules to have nested maps instead | 213 * Convert the data generated by the rules to have nested maps instead |
| 214 * of Reference objects and to add rule numbers if [storeRoundTripInfo] | 214 * of Reference objects and to add rule numbers if [storeRoundTripInfo] |
| 215 * is true. | 215 * is true. |
| 216 */ | 216 */ |
| 217 jsonify(Writer w) { | 217 void jsonify(Writer w) { |
| 218 for (var eachRule in w.rules) { | 218 for (var eachRule in w.rules) { |
| 219 var ruleData = w.states[eachRule.number]; | 219 var ruleData = w.states[eachRule.number]; |
| 220 jsonifyForRule(ruleData, w, eachRule); | 220 jsonifyForRule(ruleData, w, eachRule); |
| 221 } | 221 } |
| 222 } | 222 } |
| 223 | 223 |
| 224 /** | 224 /** |
| 225 * For a particular [rule] modify the [ruleData] to conform to this format. | 225 * For a particular [rule] modify the [ruleData] to conform to this format. |
| 226 */ | 226 */ |
| 227 jsonifyForRule(List ruleData, Writer w, SerializationRule rule) { | 227 void jsonifyForRule(List ruleData, Writer w, SerializationRule rule) { |
| 228 for (var i = 0; i < ruleData.length; i++) { | 228 for (var i = 0; i < ruleData.length; i++) { |
| 229 var each = ruleData[i]; | 229 var each = ruleData[i]; |
| 230 if (each is List) { | 230 if (each is List) { |
| 231 jsonifyEntry(each, w); | 231 jsonifyEntry(each, w); |
| 232 if (storeRoundTripInfo) ruleData[i].add(rule.number); | 232 if (storeRoundTripInfo) ruleData[i].add(rule.number); |
| 233 } else if (each is Map) { | 233 } else if (each is Map) { |
| 234 jsonifyEntry(each, w); | 234 jsonifyEntry(each, w); |
| 235 if (storeRoundTripInfo) each[RULE] = rule.number; | 235 if (storeRoundTripInfo) each[RULE] = rule.number; |
| 236 } | 236 } |
| 237 } | 237 } |
| 238 } | 238 } |
| 239 | 239 |
| 240 /** | 240 /** |
| 241 * For one particular entry, which is either a Map or a List, update it | 241 * For one particular entry, which is either a Map or a List, update it |
| 242 * to turn References into a nested List/Map. | 242 * to turn References into a nested List/Map. |
| 243 */ | 243 */ |
| 244 jsonifyEntry(map, Writer w) { | 244 void jsonifyEntry(map, Writer w) { |
| 245 // Note, if this is a Map, and the key might be a reference, we need to | 245 // Note, if this is a Map, and the key might be a reference, we need to |
| 246 // bend over backwards to avoid concurrent modifications. Non-string keys | 246 // bend over backwards to avoid concurrent modifications. Non-string keys |
| 247 // won't actually work if we try to write this to json, but might happen | 247 // won't actually work if we try to write this to json, but might happen |
| 248 // if e.g. sending between isolates. | 248 // if e.g. sending between isolates. |
| 249 var updates = new Map(); | 249 var updates = new Map(); |
| 250 keysAndValues(map).forEach((key, value) { | 250 keysAndValues(map).forEach((key, value) { |
| 251 if (value is Reference) updates[key] = w.stateForReference(value); | 251 if (value is Reference) updates[key] = w.stateForReference(value); |
| 252 }); | 252 }); |
| 253 updates.forEach((k, v) => map[k] = v); | 253 updates.forEach((k, v) => map[k] = v); |
| 254 } | 254 } |
| (...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 489 return []; | 489 return []; |
| 490 } else { | 490 } else { |
| 491 throw new SerializationException("Invalid data in serialization"); | 491 throw new SerializationException("Invalid data in serialization"); |
| 492 } | 492 } |
| 493 } | 493 } |
| 494 | 494 |
| 495 /** | 495 /** |
| 496 * Read data for [rule] from [input] with [length] number of entries, | 496 * Read data for [rule] from [input] with [length] number of entries, |
| 497 * creating lists from the results. | 497 * creating lists from the results. |
| 498 */ | 498 */ |
| 499 readLists(Iterator input, SerializationRule rule, int length, Reader r) { | 499 List readLists(Iterator input, SerializationRule rule, int length, Reader r) { |
| 500 var ruleData = []; | 500 var ruleData = []; |
| 501 for (var i = 0; i < length; i++) { | 501 for (var i = 0; i < length; i++) { |
| 502 var subLength = | 502 var subLength = |
| 503 rule.hasVariableLengthEntries ? _next(input) : rule.dataLength; | 503 rule.hasVariableLengthEntries ? _next(input) : rule.dataLength; |
| 504 var subList = []; | 504 var subList = []; |
| 505 ruleData.add(subList); | 505 ruleData.add(subList); |
| 506 for (var j = 0; j < subLength; j++) { | 506 for (var j = 0; j < subLength; j++) { |
| 507 subList.add(nextReferenceFrom(input, r)); | 507 subList.add(nextReferenceFrom(input, r)); |
| 508 } | 508 } |
| 509 } | 509 } |
| 510 return ruleData; | 510 return ruleData; |
| 511 } | 511 } |
| 512 | 512 |
| 513 /** | 513 /** |
| 514 * Read data for [rule] from [input] with [length] number of entries, | 514 * Read data for [rule] from [input] with [length] number of entries, |
| 515 * creating maps from the results. | 515 * creating maps from the results. |
| 516 */ | 516 */ |
| 517 readMaps(Iterator input, SerializationRule rule, int length, Reader r) { | 517 List readMaps(Iterator input, SerializationRule rule, int length, Reader r) { |
| 518 var ruleData = []; | 518 var ruleData = []; |
| 519 for (var i = 0; i < length; i++) { | 519 for (var i = 0; i < length; i++) { |
| 520 var subLength = | 520 var subLength = |
| 521 rule.hasVariableLengthEntries ? _next(input) : rule.dataLength; | 521 rule.hasVariableLengthEntries ? _next(input) : rule.dataLength; |
| 522 var map = new Map(); | 522 var map = new Map(); |
| 523 ruleData.add(map); | 523 ruleData.add(map); |
| 524 for (var j = 0; j < subLength; j++) { | 524 for (var j = 0; j < subLength; j++) { |
| 525 var key = nextReferenceFrom(input, r); | 525 var key = nextReferenceFrom(input, r); |
| 526 var value = nextReferenceFrom(input, r); | 526 var value = nextReferenceFrom(input, r); |
| 527 map[key] = value; | 527 map[key] = value; |
| 528 } | 528 } |
| 529 } | 529 } |
| 530 return ruleData; | 530 return ruleData; |
| 531 } | 531 } |
| 532 | 532 |
| 533 /** | 533 /** |
| 534 * Read data for [rule] from [input] with [length] number of entries, | 534 * Read data for [rule] from [input] with [length] number of entries, |
| 535 * treating the data as primitives that can be returned directly. | 535 * treating the data as primitives that can be returned directly. |
| 536 */ | 536 */ |
| 537 readPrimitives(Iterator input, SerializationRule rule, int length) { | 537 List readPrimitives(Iterator input, SerializationRule rule, int length) { |
| 538 var ruleData = []; | 538 var ruleData = []; |
| 539 for (var i = 0; i < length; i++) { | 539 for (var i = 0; i < length; i++) { |
| 540 ruleData.add(_next(input)); | 540 ruleData.add(_next(input)); |
| 541 } | 541 } |
| 542 return ruleData; | 542 return ruleData; |
| 543 } | 543 } |
| 544 | 544 |
| 545 /** Read the next Reference from the input. */ | 545 /** Read the next Reference from the input. */ |
| 546 Reference nextReferenceFrom(Iterator input, Reader r) { | 546 Reference nextReferenceFrom(Iterator input, Reader r) { |
| 547 var a = _next(input); | 547 var a = _next(input); |
| 548 var b = _next(input); | 548 var b = _next(input); |
| 549 if (a == null) { | 549 if (a == null) { |
| 550 return null; | 550 return null; |
| 551 } else { | 551 } else { |
| 552 return new Reference(r, a, b); | 552 return new Reference(r, a, b); |
| 553 } | 553 } |
| 554 } | 554 } |
| 555 | 555 |
| 556 /** Return the next element from the input. */ | 556 /** Return the next element from the input. */ |
| 557 _next(Iterator input) { | 557 _next(Iterator input) { |
| 558 input.moveNext(); | 558 input.moveNext(); |
| 559 return input.current; | 559 return input.current; |
| 560 } | 560 } |
| 561 } | 561 } |
| OLD | NEW |