| 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 d0228465d646a8177974e84b0ace1c27d043179c..d9aed0f9b036ae49e1b853f7cc522df9acb3ba33 100644
|
| --- a/pkg/serialization/lib/src/serialization_rule.dart
|
| +++ b/pkg/serialization/lib/src/serialization_rule.dart
|
| @@ -102,90 +102,23 @@ abstract class SerializationRule {
|
|
|
| /**
|
| * If we have [object] as part of our state, should we represent that
|
| - * directly, or should we make a reference for it. By default we use a
|
| - * reference for everything.
|
| + * directly, or should we make a reference for it. By default, true.
|
| + * This may also delegate to [writer].
|
| */
|
| - bool shouldUseReferenceFor(object, Writer w) => true;
|
| + bool shouldUseReferenceFor(object, Writer writer) => true;
|
|
|
| /**
|
| - * This writes the data from our internal representation into a List.
|
| - * It is used in order to write to a flat format, and is likely to be
|
| - * folded into a more general mechanism for supporting different output
|
| - * formats.
|
| + * Return true if the data this rule returns is variable length, so a
|
| + * length needs to be written for it if the format requires that. Return
|
| + * false if the results are always the same length.
|
| */
|
| - // TODO(alanknight): This really shouldn't exist, but is a temporary measure
|
| - // for writing to a a flat format until that's more fleshed out. It takes
|
| - // the internal representation of the rule's state, which is particularly
|
| - // bad. The default implementation treats the ruleData as a List of Lists
|
| - // of references.
|
| - void dumpStateInto(List ruleData, List target) {
|
| - // Needing the intermediate is also bad for performance, but tricky
|
| - // to do otherwise without a mechanism to precalculate the size.
|
| - var intermediate = new List();
|
| - var totalLength = 0;
|
| - for (var eachList in ruleData) {
|
| - if (writeLengthInFlatFormat) {
|
| - intermediate.add(eachList.length);
|
| - }
|
| - for (var eachRef in eachList) {
|
| - if (eachRef == null) {
|
| - intermediate..add(null)..add(null);
|
| - } else {
|
| - eachRef.writeToList(intermediate);
|
| - }
|
| - }
|
| - }
|
| - target.addAll(intermediate);
|
| - }
|
| -
|
| - /**
|
| - * Return true if this rule writes a length value before each entry in
|
| - * the flat format. Return false if the results are fixed length.
|
| - */
|
| - // TODO(alanknight): This should probably go away with more general formats.
|
| - bool get writeLengthInFlatFormat => false;
|
| -
|
| - /**
|
| - * The inverse of dumpStateInto, this reads the rule's state from an
|
| - * iterator in a flat format.
|
| - */
|
| - pullStateFrom(Iterator stream) {
|
| - stream.moveNext();
|
| - var numberOfEntries = stream.current;
|
| - var ruleData = new List();
|
| - for (var i = 0; i < numberOfEntries; i++) {
|
| - var subLength = dataLengthIn(stream);
|
| - var subList = [];
|
| - ruleData.add(subList);
|
| - for (var j = 0; j < subLength; j++) {
|
| - stream.moveNext();
|
| - var a = stream.current;
|
| - stream.moveNext();
|
| - var b = stream.current;
|
| - if (a is! int) {
|
| - // This wasn't a reference, just use the first object as a literal.
|
| - // particularly used for the case of null.
|
| - subList.add(a);
|
| - } else {
|
| - subList.add(new Reference(this, a, b));
|
| - }
|
| - }
|
| - }
|
| - return ruleData;
|
| - }
|
| -
|
| - /**
|
| - * Return the length of the list of data we expect to see on a particular
|
| - * iterator in a flat format. This may have been encoded in the stream if we
|
| - * are variable length, or it may be constant. Returns null if the [Iterator]
|
| - * is empty.
|
| - */
|
| - dataLengthIn(Iterator stream) =>
|
| - writeLengthInFlatFormat ? (stream..moveNext()).current : dataLength;
|
| + bool get hasVariableLengthEntries => true;
|
|
|
| /**
|
| - * If the data is fixed length, return it here. Unused in the non-flat
|
| - * format, or if the data is variable length.
|
| + * If the data is fixed length, return it here. The format may or may not
|
| + * make use of this, depending on whether it already has enough information
|
| + * to determine the length on its own. If [hasVariableLengthEntries] is true
|
| + * this is ignored.
|
| */
|
| int get dataLength => 0;
|
| }
|
| @@ -223,49 +156,7 @@ class ListRule extends SerializationRule {
|
| }
|
| }
|
|
|
| - /**
|
| - * When reading from a flat format we are given [stream] and need to pull as
|
| - * much data from it as we need. Our format is that we have an integer N
|
| - * indicating the number of objects and then for each object a length M,
|
| - * and then M references, where a reference is stored in the stream as two
|
| - * integers. Or, in the special case of null, two nulls.
|
| - */
|
| - pullStateFrom(Iterator stream) {
|
| - // TODO(alanknight): This is much too close to the basicRule implementation,
|
| - // and I'd refactor them if I didn't think this whole mechanism needed to
|
| - // change soon.
|
| - stream.moveNext();
|
| - var length = stream.current;
|
| - var ruleData = new List();
|
| - for (var i = 0; i < length; i++) {
|
| - stream.moveNext();
|
| - var subLength = stream.current;
|
| - var subList = new List();
|
| - ruleData.add(subList);
|
| - for (var j = 0; j < subLength; j++) {
|
| - stream.moveNext();
|
| - var a = stream.current;
|
| - stream.moveNext();
|
| - var b = stream.current;
|
| - if (!(a is int)) {
|
| - // This wasn't a reference, just use the first object as a literal.
|
| - // particularly used for the case of null.
|
| - subList.add(a);
|
| - } else {
|
| - subList.add(new Reference(this, a, b));
|
| - }
|
| - }
|
| - }
|
| - return ruleData;
|
| - }
|
| -
|
| - /**
|
| - * Return true because we need to write the length of each list in the flat
|
| - * format. */
|
| - bool get writeLengthInFlatFormat => true;
|
| -
|
| - /** Return the length of the next list when reading the flat format. */
|
| - int dataLengthIn(Iterator stream) => stream.next();
|
| + bool get hasVariableLengthEntries => true;
|
| }
|
|
|
| /**
|
| @@ -310,32 +201,7 @@ class PrimitiveRule extends SerializationRule {
|
| bool shouldUseReferenceFor(object, Writer w) =>
|
| w.shouldUseReferencesForPrimitives;
|
|
|
| - /**
|
| - * This writes the data from our internal representation into a List.
|
| - * It is used in order to write to a flat format, and is likely to be
|
| - * folded into a more general mechanism for supporting different output
|
| - * formats. For primitives, the ruleData is our list of all the
|
| - * primitives and just add it into the target.
|
| - */
|
| - void dumpStateInto(List ruleData, List target) {
|
| - target.addAll(ruleData);
|
| - }
|
| -
|
| - /**
|
| - * When reading from a flat format we are given [stream] and need to pull as
|
| - * much data from it as we need. Our format is that we have an integer N
|
| - * indicating the number of objects and then N simple objects.
|
| - */
|
| - pullStateFrom(Iterator stream) {
|
| - stream.moveNext();
|
| - var length = stream.current;
|
| - var ruleData = new List();
|
| - for (var i = 0; i < length; i++) {
|
| - stream.moveNext();
|
| - ruleData.add(stream.current);
|
| - }
|
| - return ruleData;
|
| - }
|
| + bool get hasVariableLengthEntries => false;
|
| }
|
|
|
| /** Typedef for the object construction closure used in ClosureRule. */
|
| @@ -491,12 +357,12 @@ abstract class CustomRule extends SerializationRule {
|
| // We don't want to have to make the end user tell us how long the list is
|
| // separately, so write it out for each object, even though they're all
|
| // expected to be the same length.
|
| - get writeLengthInFlatFormat => true;
|
| + get hasVariableLengthEntries => true;
|
| }
|
|
|
| /** Create a lazy list/map that will inflate its items on demand in [r]. */
|
| _lazy(l, Reader r) {
|
| - if (l is List) return l.mappedBy(r.inflateReference);
|
| + if (l is List) return new _LazyList(l, r);
|
| if (l is Map) return new _LazyMap(l, r);
|
| throw new SerializationException("Invalid type: must be Map or List - $l");
|
| }
|
| @@ -511,8 +377,8 @@ _lazy(l, Reader r) {
|
| class _LazyMap implements Map {
|
| _LazyMap(this._raw, this._reader);
|
|
|
| - Map _raw;
|
| - Reader _reader;
|
| + final Map _raw;
|
| + final Reader _reader;
|
|
|
| // This is the only operation that really matters.
|
| operator [](x) => _reader.inflateReference(_raw[x]);
|
| @@ -524,15 +390,65 @@ class _LazyMap implements Map {
|
|
|
| // These operations will work, but may be expensive, and are probably
|
| // best avoided.
|
| - get _inflated => keysAndValues(_raw).map(_reader.inflateReference);
|
| + get _inflated => keysAndValues(_raw).mappedBy(_reader.inflateReference);
|
| bool containsValue(x) => _inflated.containsValue(x);
|
| Iterable get values => _inflated.values;
|
| void forEach(f) => _inflated.forEach(f);
|
|
|
| // These operations are all invalid
|
| - _throw() => throw new UnsupportedError("Not modifiable");
|
| + _throw() {
|
| + throw new UnsupportedError("Not modifiable");
|
| + }
|
| operator []=(x, y) => _throw();
|
| putIfAbsent(x, y) => _throw();
|
| remove(x) => _throw();
|
| clear() => _throw();
|
| }
|
| +
|
| +/**
|
| + * This provides an implementation of List that wraps a list which may
|
| + * contain references to (potentially) non-inflated objects. If these
|
| + * are accessed it will inflate them. This allows us to pass something that
|
| + * looks like it's just a list of objects to a [CustomRule] without needing
|
| + * to inflate all the references in advance.
|
| + */
|
| +class _LazyList extends Iterable implements List {
|
| + _LazyList(this._raw, this._reader);
|
| +
|
| + final List _raw;
|
| + final Reader _reader;
|
| +
|
| + // This is the only operation that really matters.
|
| + operator [](x) => _reader.inflateReference(_raw[x]);
|
| +
|
| + int get length => _raw.length;
|
| + bool get isEmpty => _raw.isEmpty;
|
| + get first => _reader.inflateReference(_raw.first);
|
| + get last => _reader.inflateReference(_raw.last);
|
| +
|
| + // These operations, and other inherited methods that iterate over the whole
|
| + // list will work, but may be expensive, and are probably
|
| + // best avoided.
|
| + List get _inflated => _raw.mappedBy(_reader.inflateReference);
|
| + Iterator get iterator => _inflated.iterator;
|
| + indexOf(x, [pos = 0]) => _inflated.toList().indexOf(x);
|
| + lastIndexOf(x, [pos]) => _inflated.toList().lastIndexOf(x);
|
| +
|
| + // These operations are all invalid
|
| + _throw() {
|
| + throw new UnsupportedError("Not modifiable");
|
| + }
|
| + operator []=(x, y) => _throw();
|
| + add(x) => _throw();
|
| + addLast(x) => _throw();
|
| + addAll(x) => _throw();
|
| + sort([f]) => _throw();
|
| + clear() => _throw();
|
| + removeAt(x) => _throw();
|
| + removeLast() => _throw();
|
| + getRange(x, y) => _throw();
|
| + setRange(x, y, z, [a]) => _throw();
|
| + removeRange(x, y) => _throw();
|
| + insertRange(x, y, [z]) => _throw();
|
| + void set length(x) => _throw();
|
| +}
|
|
|