| OLD | NEW |
| 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 10 matching lines...) Expand all Loading... |
| 21 * Rules belong uniquely to a particular Serialization instance, and can | 21 * Rules belong uniquely to a particular Serialization instance, and can |
| 22 * be identified within it by number. | 22 * be identified within it by number. |
| 23 */ | 23 */ |
| 24 int get number => _number; | 24 int get number => _number; |
| 25 | 25 |
| 26 /** | 26 /** |
| 27 * Rules belong uniquely to a particular Serialization instance, and can | 27 * Rules belong uniquely to a particular Serialization instance, and can |
| 28 * be identified within it by number. | 28 * be identified within it by number. |
| 29 */ | 29 */ |
| 30 void set number(x) { | 30 void set number(x) { |
| 31 if (_number != null) throw | 31 if (_number != null && _number != x) throw |
| 32 new SerializationException("Rule numbers cannot be changed, once set"); | 32 new SerializationException("Rule numbers cannot be changed, once set"); |
| 33 _number = x; | 33 _number = x; |
| 34 } | 34 } |
| 35 | 35 |
| 36 /** | 36 /** |
| 37 * Return true if this rule applies to this object, in the context | 37 * Return true if this rule applies to this object, in the context |
| 38 * where we're writing it, false otherwise. | 38 * where we're writing it, false otherwise. |
| 39 */ | 39 */ |
| 40 bool appliesTo(object, Writer writer); | 40 bool appliesTo(object, Writer writer); |
| 41 | 41 |
| 42 /** | 42 /** |
| 43 * This extracts the state from the object, calling [f] for each value | 43 * This extracts the state from the object, calling [f] for each value |
| 44 * as it is extracted, and returning an object representing the whole | 44 * as it is extracted, and returning an object representing the whole |
| 45 * state at the end. The state that results will still have direct | 45 * state at the end. The state that results will still have direct |
| 46 * pointers to objects, rather than references. | 46 * pointers to objects, rather than references. |
| 47 */ | 47 */ |
| 48 extractState(object, void f(value)); | 48 extractState(object, void f(value)); |
| 49 | 49 |
| 50 /** | 50 /** |
| 51 * Allows rules to tell us how they expect to store their state. If this |
| 52 * isn't specified we can also just look at the data to tell. |
| 53 */ |
| 54 bool get storesStateAsLists => false; |
| 55 bool get storesStateAsMaps => false; |
| 56 bool get storesStateAsPrimitives => false; |
| 57 |
| 58 /** |
| 51 * Given the variables representing the state of an object, flatten it | 59 * Given the variables representing the state of an object, flatten it |
| 52 * by turning object pointers into Reference objects where needed. This | 60 * by turning object pointers into Reference objects where needed. This |
| 53 * destructively modifies the state object. | 61 * destructively modifies the state object. |
| 54 * | 62 * |
| 55 * This has a default implementation which assumes that object is indexable, | 63 * This has a default implementation which assumes that object is indexable, |
| 56 * so either conforms to Map or List. Subclasses may override to do something | 64 * so either conforms to Map or List. Subclasses may override to do something |
| 57 * different. | 65 * different, including returning a new state object to be used in place |
| 66 * of the original. |
| 58 */ | 67 */ |
| 59 // This has to be a separate operation from extracting, because we extract | 68 // This has to be a separate operation from extracting, because we extract |
| 60 // as we are traversing the objects, so we don't yet have the objects to | 69 // as we are traversing the objects, so we don't yet have the objects to |
| 61 // generate references for them. It might be possible to avoid that by | 70 // generate references for them. It might be possible to avoid that by |
| 62 // doing a depth-first rather than breadth-first traversal, but I'm not | 71 // doing a depth-first rather than breadth-first traversal, but I'm not |
| 63 // sure it's worth it. | 72 // sure it's worth it. |
| 64 void flatten(state, Writer writer) { | 73 flatten(state, Writer writer) { |
| 65 keysAndValues(state).forEach((key, value) { | 74 keysAndValues(state).forEach((key, value) { |
| 66 var reference = writer._referenceFor(value); | 75 var reference = writer._referenceFor(value); |
| 67 state[key] = (reference == null) ? value : reference; | 76 state[key] = reference; |
| 68 }); | 77 }); |
| 69 } | 78 } |
| 70 | 79 |
| 71 /** Return true if this rule should only be applied when we are the first | 80 /** Return true if this rule should only be applied when we are the first |
| 72 * rule found that applies to this object. This may or may not be a hack | 81 * rule found that applies to this object. This may or may not be a hack |
| 73 * that will disappear once we have better support for multiple rules. | 82 * that will disappear once we have better support for multiple rules. |
| 74 * We want to have multiple different rules that apply to the same object. We | 83 * We want to have multiple different rules that apply to the same object. We |
| 75 * also want to have multiple different rules that might exclusively apply | 84 * also want to have multiple different rules that might exclusively apply |
| 76 * to the same object. So, we want either ListRule or ListRuleEssential, and | 85 * to the same object. So, we want either ListRule or ListRuleEssential, and |
| 77 * only one of them can be there. But on the other hand, we may want both | 86 * only one of them can be there. But on the other hand, we may want both |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 124 } | 133 } |
| 125 | 134 |
| 126 /** | 135 /** |
| 127 * This rule handles things that implement List. It will recreate them as | 136 * This rule handles things that implement List. It will recreate them as |
| 128 * whatever the default implemenation of List is on the target platform. | 137 * whatever the default implemenation of List is on the target platform. |
| 129 */ | 138 */ |
| 130 class ListRule extends SerializationRule { | 139 class ListRule extends SerializationRule { |
| 131 | 140 |
| 132 appliesTo(object, Writer w) => object is List; | 141 appliesTo(object, Writer w) => object is List; |
| 133 | 142 |
| 134 state(List list) => new List.from(list); | 143 bool get storesStateAsLists => true; |
| 135 | 144 |
| 136 List extractState(List list, f) { | 145 List extractState(List list, f) { |
| 137 var result = new List(); | 146 var result = new List(); |
| 138 for (var each in list) { | 147 for (var each in list) { |
| 139 result.add(each); | 148 result.add(each); |
| 140 f(each); | 149 f(each); |
| 141 } | 150 } |
| 142 return result; | 151 return result; |
| 143 } | 152 } |
| 144 | 153 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 173 return object; | 182 return object; |
| 174 } | 183 } |
| 175 | 184 |
| 176 /** Does nothing, because all the work has been done in inflateEssential. */ | 185 /** Does nothing, because all the work has been done in inflateEssential. */ |
| 177 inflateNonEssential(state, newList, reader) {} | 186 inflateNonEssential(state, newList, reader) {} |
| 178 | 187 |
| 179 bool get mustBePrimary => true; | 188 bool get mustBePrimary => true; |
| 180 } | 189 } |
| 181 | 190 |
| 182 /** | 191 /** |
| 192 * This rule handles things that implement Map. It will recreate them as |
| 193 * whatever the default implemenation of Map is on the target platform. If a |
| 194 * map has string keys it will attempt to retain it as a map for JSON formats, |
| 195 * otherwise it will store it as a list of references to keys and values. |
| 196 */ |
| 197 class MapRule extends SerializationRule { |
| 198 |
| 199 appliesTo(object, Writer w) => object is Map; |
| 200 |
| 201 bool get storesStateAsMaps => true; |
| 202 |
| 203 extractState(Map map, f) { |
| 204 // Note that we make a copy here because flattening may be destructive. |
| 205 var newMap = new Map.from(map); |
| 206 newMap.forEach((key, value) { |
| 207 f(key); |
| 208 f(value); |
| 209 }); |
| 210 return newMap; |
| 211 } |
| 212 |
| 213 /** |
| 214 * Change the keys and values of [state] into references in [writer]. |
| 215 * If [state] is a map whose keys are all strings then we leave the keys |
| 216 * as is so that JSON formats will be more readable. If the keys are |
| 217 * arbitrary then we need to turn them into references and replace the |
| 218 * state with a new Map whose keys are the references. |
| 219 */ |
| 220 flatten(Map state, Writer writer) { |
| 221 bool keysAreAllStrings = state.keys.every((x) => x is String); |
| 222 if (keysAreAllStrings) { |
| 223 keysAndValues(state).forEach( |
| 224 (key, value) => state[key] = writer._referenceFor(value)); |
| 225 return state; |
| 226 } else { |
| 227 var newState = []; |
| 228 keysAndValues(state).forEach((key, value) { |
| 229 newState.add(writer._referenceFor(key)); |
| 230 newState.add(writer._referenceFor(value)); |
| 231 }); |
| 232 return newState; |
| 233 } |
| 234 } |
| 235 |
| 236 inflateEssential(state, Reader r) => new Map(); |
| 237 |
| 238 // For a map, we consider all of its state non-essential and add it |
| 239 // after creation. |
| 240 inflateNonEssential(state, Map newMap, Reader r) { |
| 241 if (state is List) { |
| 242 inflateNonEssentialFromList(state, newMap, r); |
| 243 } else { |
| 244 inflateNonEssentialFromMap(state, newMap, r); |
| 245 } |
| 246 } |
| 247 |
| 248 void inflateNonEssentialFromList(List state, Map newMap, Reader r) { |
| 249 var key; |
| 250 for (var each in state) { |
| 251 if (key == null) { |
| 252 key = each; |
| 253 } else { |
| 254 newMap[r.inflateReference(key)] = r.inflateReference(each); |
| 255 key = null; |
| 256 } |
| 257 } |
| 258 } |
| 259 |
| 260 void inflateNonEssentialFromMap(Map state, Map newMap, Reader r) { |
| 261 state.forEach((key, value) { |
| 262 newMap[r.inflateReference(key)] = r.inflateReference(value); |
| 263 }); |
| 264 } |
| 265 |
| 266 bool get hasVariableLengthEntries => true; |
| 267 } |
| 268 |
| 269 /** |
| 183 * This rule handles primitive types, defined as those that we can normally | 270 * This rule handles primitive types, defined as those that we can normally |
| 184 * represent directly in the output format. We hard-code that to mean | 271 * represent directly in the output format. We hard-code that to mean |
| 185 * num, String, and bool. | 272 * num, String, and bool. |
| 186 */ | 273 */ |
| 187 class PrimitiveRule extends SerializationRule { | 274 class PrimitiveRule extends SerializationRule { |
| 188 appliesTo(object, Writer w) { | 275 appliesTo(object, Writer w) { |
| 189 return isPrimitive(object); | 276 return isPrimitive(object); |
| 190 } | 277 } |
| 191 extractState(object, Function f) => object; | 278 extractState(object, Function f) => object; |
| 192 void flatten(object, Writer writer) {} | 279 flatten(object, Writer writer) {} |
| 193 inflateEssential(state, Reader r) => state; | 280 inflateEssential(state, Reader r) => state; |
| 194 inflateNonEssential(object, _, Reader r) {} | 281 inflateNonEssential(object, _, Reader r) {} |
| 195 | 282 |
| 283 bool get storesStateAsPrimitives => true; |
| 284 |
| 196 /** | 285 /** |
| 197 * Indicate whether we should save pointers to this object as references | 286 * Indicate whether we should save pointers to this object as references |
| 198 * or store the object directly. For primitives this depends on the format, | 287 * or store the object directly. For primitives this depends on the format, |
| 199 * so we delegate to the writer. | 288 * so we delegate to the writer. |
| 200 */ | 289 */ |
| 201 bool shouldUseReferenceFor(object, Writer w) => | 290 bool shouldUseReferenceFor(object, Writer w) => |
| 202 w.shouldUseReferencesForPrimitives; | 291 w.shouldUseReferencesForPrimitives; |
| 203 | 292 |
| 204 bool get hasVariableLengthEntries => false; | 293 bool get hasVariableLengthEntries => false; |
| 205 } | 294 } |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 | 356 |
| 268 /** Extract the state of the named objects as just the object itself. */ | 357 /** Extract the state of the named objects as just the object itself. */ |
| 269 extractState(object, Function f) => [object]; | 358 extractState(object, Function f) => [object]; |
| 270 | 359 |
| 271 /** When we flatten the state we save it as the name. */ | 360 /** When we flatten the state we save it as the name. */ |
| 272 // TODO(alanknight): This seems questionable. In a truly flat format we may | 361 // TODO(alanknight): This seems questionable. In a truly flat format we may |
| 273 // want to have extracted the name as a string first and flatten it into a | 362 // want to have extracted the name as a string first and flatten it into a |
| 274 // reference to that. But that requires adding the Writer as a parameter to | 363 // reference to that. But that requires adding the Writer as a parameter to |
| 275 // extractState, and I'm reluctant to add yet another parameter until | 364 // extractState, and I'm reluctant to add yet another parameter until |
| 276 // proven necessary. | 365 // proven necessary. |
| 277 void flatten(state, Writer writer) { | 366 flatten(state, Writer writer) { |
| 278 state[0] = nameFor(state.first, writer); | 367 state[0] = nameFor(state.first, writer); |
| 279 } | 368 } |
| 280 | 369 |
| 281 /** Look up the named object and return it. */ | 370 /** Look up the named object and return it. */ |
| 282 inflateEssential(state, Reader r) => r.objectNamed(state.first); | 371 inflateEssential(state, Reader r) => r.objectNamed(state.first); |
| 283 | 372 |
| 284 /** Set any non-essential state on the object. For this rule, a no-op. */ | 373 /** Set any non-essential state on the object. For this rule, a no-op. */ |
| 285 inflateNonEssential(state, object, Reader r) {} | 374 inflateNonEssential(state, object, Reader r) {} |
| 286 | 375 |
| 287 /** Return the name for this object in the Writer. */ | 376 /** Return the name for this object in the Writer. */ |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 438 _throw() { | 527 _throw() { |
| 439 throw new UnsupportedError("Not modifiable"); | 528 throw new UnsupportedError("Not modifiable"); |
| 440 } | 529 } |
| 441 operator []=(x, y) => _throw(); | 530 operator []=(x, y) => _throw(); |
| 442 add(x) => _throw(); | 531 add(x) => _throw(); |
| 443 addLast(x) => _throw(); | 532 addLast(x) => _throw(); |
| 444 addAll(x) => _throw(); | 533 addAll(x) => _throw(); |
| 445 sort([f]) => _throw(); | 534 sort([f]) => _throw(); |
| 446 clear() => _throw(); | 535 clear() => _throw(); |
| 447 removeAt(x) => _throw(); | 536 removeAt(x) => _throw(); |
| 537 remove(x) => _throw(); |
| 448 removeLast() => _throw(); | 538 removeLast() => _throw(); |
| 449 remove(x) => _throw(); | |
| 450 removeAll(x) => _throw(); | 539 removeAll(x) => _throw(); |
| 451 retainAll(x) => _throw(); | 540 retainAll(x) => _throw(); |
| 452 removeMatching(x) => _throw(); | 541 removeMatching(x) => _throw(); |
| 453 retainMatching(x) => _throw(); | 542 retainMatching(x) => _throw(); |
| 454 getRange(x, y) => _throw(); | 543 getRange(x, y) => _throw(); |
| 455 setRange(x, y, z, [a]) => _throw(); | 544 setRange(x, y, z, [a]) => _throw(); |
| 456 removeRange(x, y) => _throw(); | 545 removeRange(x, y) => _throw(); |
| 457 insertRange(x, y, [z]) => _throw(); | 546 insertRange(x, y, [z]) => _throw(); |
| 547 get reversed => _throw(); |
| 458 void set length(x) => _throw(); | 548 void set length(x) => _throw(); |
| 459 List get reversed => _throw(); | |
| 460 } | 549 } |
| OLD | NEW |