| Index: pkg/serialization/lib/src/serialization_rule.dart
|
| diff --git a/pkg/serialization/lib/src/serialization_rule.dart b/pkg/serialization/lib/src/serialization_rule.dart
|
| index b040796668570f0e04db697e77eca4f28682fd89..311563c17d21fe712b811679765673f5de946ba2 100644
|
| --- a/pkg/serialization/lib/src/serialization_rule.dart
|
| +++ b/pkg/serialization/lib/src/serialization_rule.dart
|
| @@ -28,7 +28,7 @@ abstract class SerializationRule {
|
| * be identified within it by number.
|
| */
|
| void set number(x) {
|
| - if (_number != null) throw
|
| + if (_number != null && _number != x) throw
|
| new SerializationException("Rule numbers cannot be changed, once set");
|
| _number = x;
|
| }
|
| @@ -48,23 +48,32 @@ abstract class SerializationRule {
|
| extractState(object, void f(value));
|
|
|
| /**
|
| + * Allows rules to tell us how they expect to store their state. If this
|
| + * isn't specified we can also just look at the data to tell.
|
| + */
|
| + bool get storesStateAsLists => false;
|
| + bool get storesStateAsMaps => false;
|
| + bool get storesStateAsPrimitives => false;
|
| +
|
| + /**
|
| * Given the variables representing the state of an object, flatten it
|
| * by turning object pointers into Reference objects where needed. This
|
| * destructively modifies the state object.
|
| *
|
| * This has a default implementation which assumes that object is indexable,
|
| * so either conforms to Map or List. Subclasses may override to do something
|
| - * different.
|
| + * different, including returning a new state object to be used in place
|
| + * of the original.
|
| */
|
| // This has to be a separate operation from extracting, because we extract
|
| // as we are traversing the objects, so we don't yet have the objects to
|
| // generate references for them. It might be possible to avoid that by
|
| // doing a depth-first rather than breadth-first traversal, but I'm not
|
| // sure it's worth it.
|
| - void flatten(state, Writer writer) {
|
| + flatten(state, Writer writer) {
|
| keysAndValues(state).forEach((key, value) {
|
| var reference = writer._referenceFor(value);
|
| - state[key] = (reference == null) ? value : reference;
|
| + state[key] = reference;
|
| });
|
| }
|
|
|
| @@ -131,7 +140,7 @@ class ListRule extends SerializationRule {
|
|
|
| appliesTo(object, Writer w) => object is List;
|
|
|
| - state(List list) => new List.from(list);
|
| + bool get storesStateAsLists => true;
|
|
|
| List extractState(List list, f) {
|
| var result = new List();
|
| @@ -180,6 +189,84 @@ class ListRuleEssential extends ListRule {
|
| }
|
|
|
| /**
|
| + * This rule handles things that implement Map. It will recreate them as
|
| + * whatever the default implemenation of Map is on the target platform. If a
|
| + * map has string keys it will attempt to retain it as a map for JSON formats,
|
| + * otherwise it will store it as a list of references to keys and values.
|
| + */
|
| +class MapRule extends SerializationRule {
|
| +
|
| + appliesTo(object, Writer w) => object is Map;
|
| +
|
| + bool get storesStateAsMaps => true;
|
| +
|
| + extractState(Map map, f) {
|
| + // Note that we make a copy here because flattening may be destructive.
|
| + var newMap = new Map.from(map);
|
| + newMap.forEach((key, value) {
|
| + f(key);
|
| + f(value);
|
| + });
|
| + return newMap;
|
| + }
|
| +
|
| + /**
|
| + * Change the keys and values of [state] into references in [writer].
|
| + * If [state] is a map whose keys are all strings then we leave the keys
|
| + * as is so that JSON formats will be more readable. If the keys are
|
| + * arbitrary then we need to turn them into references and replace the
|
| + * state with a new Map whose keys are the references.
|
| + */
|
| + flatten(Map state, Writer writer) {
|
| + bool keysAreAllStrings = state.keys.every((x) => x is String);
|
| + if (keysAreAllStrings) {
|
| + keysAndValues(state).forEach(
|
| + (key, value) => state[key] = writer._referenceFor(value));
|
| + return state;
|
| + } else {
|
| + var newState = [];
|
| + keysAndValues(state).forEach((key, value) {
|
| + newState.add(writer._referenceFor(key));
|
| + newState.add(writer._referenceFor(value));
|
| + });
|
| + return newState;
|
| + }
|
| + }
|
| +
|
| + inflateEssential(state, Reader r) => new Map();
|
| +
|
| + // For a map, we consider all of its state non-essential and add it
|
| + // after creation.
|
| + inflateNonEssential(state, Map newMap, Reader r) {
|
| + if (state is List) {
|
| + inflateNonEssentialFromList(state, newMap, r);
|
| + } else {
|
| + inflateNonEssentialFromMap(state, newMap, r);
|
| + }
|
| + }
|
| +
|
| + void inflateNonEssentialFromList(List state, Map newMap, Reader r) {
|
| + var key;
|
| + for (var each in state) {
|
| + if (key == null) {
|
| + key = each;
|
| + } else {
|
| + newMap[r.inflateReference(key)] = r.inflateReference(each);
|
| + key = null;
|
| + }
|
| + }
|
| + }
|
| +
|
| + void inflateNonEssentialFromMap(Map state, Map newMap, Reader r) {
|
| + state.forEach((key, value) {
|
| + newMap[r.inflateReference(key)] = r.inflateReference(value);
|
| + });
|
| + }
|
| +
|
| + bool get hasVariableLengthEntries => true;
|
| +}
|
| +
|
| +/**
|
| * This rule handles primitive types, defined as those that we can normally
|
| * represent directly in the output format. We hard-code that to mean
|
| * num, String, and bool.
|
| @@ -189,10 +276,12 @@ class PrimitiveRule extends SerializationRule {
|
| return isPrimitive(object);
|
| }
|
| extractState(object, Function f) => object;
|
| - void flatten(object, Writer writer) {}
|
| + flatten(object, Writer writer) {}
|
| inflateEssential(state, Reader r) => state;
|
| inflateNonEssential(object, _, Reader r) {}
|
|
|
| + bool get storesStateAsPrimitives => true;
|
| +
|
| /**
|
| * Indicate whether we should save pointers to this object as references
|
| * or store the object directly. For primitives this depends on the format,
|
| @@ -274,7 +363,7 @@ class NamedObjectRule extends SerializationRule {
|
| // reference to that. But that requires adding the Writer as a parameter to
|
| // extractState, and I'm reluctant to add yet another parameter until
|
| // proven necessary.
|
| - void flatten(state, Writer writer) {
|
| + flatten(state, Writer writer) {
|
| state[0] = nameFor(state.first, writer);
|
| }
|
|
|
| @@ -445,8 +534,8 @@ class _LazyList extends Iterable implements List {
|
| sort([f]) => _throw();
|
| clear() => _throw();
|
| removeAt(x) => _throw();
|
| - removeLast() => _throw();
|
| remove(x) => _throw();
|
| + removeLast() => _throw();
|
| removeAll(x) => _throw();
|
| retainAll(x) => _throw();
|
| removeMatching(x) => _throw();
|
| @@ -455,6 +544,6 @@ class _LazyList extends Iterable implements List {
|
| setRange(x, y, z, [a]) => _throw();
|
| removeRange(x, y) => _throw();
|
| insertRange(x, y, [z]) => _throw();
|
| + get reversed => _throw();
|
| void set length(x) => _throw();
|
| - List get reversed => _throw();
|
| }
|
|
|