blob: 1f86a9777a60947afcec8382ed5095fbb7485936 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* This contains extra functions and classes useful for implementing
* serialiation. Some or all of these will be removed once the functionality is
* available in the core library.
*/
library serialization_helpers;
/**
* A named function of one argument that just returns it. Useful for using
* as a default value for a function parameter or other places where you want
* to concisely provide a function that just returns its argument.
*/
doNothing(x) => x;
/** Concatenate two lists. Handle the case where one or both might be null. */
// TODO(alanknight): Remove once issue 5342 is resolved.
Iterable append(Iterable a, Iterable b) {
if (a == null) {
return (b == null) ? [] : new List.from(b);
}
if (b == null) return new List.from(a);
var result = new List.from(a);
result.addAll(b);
return result;
}
/**
* Return a sorted version of [anIterable], using the default sort criterion.
* Always returns a List, regardless of the type of [anIterable].
*/
List sorted(anIterable) {
var result = new List.from(anIterable);
result.sort();
return result;
}
/** Helper function for PrimitiveRule to tell which objects it applies to. */
bool isPrimitive(object) {
return identical(object, null) || object is num || object is String ||
identical(object, true) || identical(object, false);
}
/**
* Be able to iterate polymorphically between List-like and Map-like things.
* For example, keysAndValues(["a", "b", "c"]).forEach((key, value) => ...);
* will loop over the key/value pairs 1/"a", 2/"b", 3/"c", as if the argument
* was a Map from integer keys to string values.
* Only supports forEach() and map() operations because that was all the code
* needed for the moment.
*/
MapLikeIterable keysAndValues(x) {
if (x is Map) return new MapLikeIterableForMap(x);
if (x is Iterable) return new MapLikeIterableForList(x);
throw new ArgumentError("Invalid argument");
}
/**
* A class for iterating over things as if they were Maps, which primarily
* means that forEach() and map() pass two arguments, and map() returns a new
* Map with the same keys as [collection] and values which have been transformed
* by the argument to map().
*/
abstract class MapLikeIterable {
MapLikeIterable(this.collection);
final collection;
/** Iterate over the collection, passing both key and value parameters. */
void forEach(Function f);
/**
* Return a new collection whose keys are the same as [collection], but whose
* values are the result of applying [f] to the key/value pairs. So, if
* [collection] is a List, it will be the same as the map() method if the
* [key] parameter wasn't passed.
*/
map(Function f) {
var result = copyEmpty();
forEach((key, value) {
result[key] = f(key, value);
});
return result;
}
/**
* Return an empty copy of our collection. Very limited, only knows enough
* to return a Map or List as appropriate.
*/
copyEmpty();
}
class MapLikeIterableForMap extends MapLikeIterable {
MapLikeIterableForMap(collection) : super(collection);
void forEach(Function f) { collection.forEach(f);}
Map copyEmpty() => new Map();
}
class MapLikeIterableForList extends MapLikeIterable {
MapLikeIterableForList(collection) : super(collection);
void forEach(f) {
Iterator iterator = collection.iterator;
for (var i = 0; i < collection.length; i++) {
iterator.moveNext();
f(i, iterator.current);
}
}
List copyEmpty() => new List(collection.length);
}
/**
* An inverse of MapLikeIterable. Lets you iterate polymorphically between
* List-like and Map-like things, but making them behave like Lists, instead
* of behaving like Maps.
* So values(["a", "b", "c"]).forEach((value) => ...);
* will loop over the values "a", "b", "c", as if it were a List of values.
* Only supports forEach() and map() operations because that was all I needed
* for the moment.
*/
values(x) {
if (x is Iterable) return x;
if (x is Map) return new ListLikeIterable(x);
throw new ArgumentError("Invalid argument");
}
mapValues(x, f) {
if (x is Set) return x.map(f).toSet();
if (x is Iterable) return x.map(f).toList();
if (x is Map) return new ListLikeIterable(x).map(f);
throw new ArgumentError("Invalid argument");
}
/**
* A class for iterating over things as if they were Lists, which primarily
* means that forEach passes one argument, and map() returns a new Map
* with the same keys as [collection] and values which haev been transformed
* by the argument to map().
*/
class ListLikeIterable {
ListLikeIterable(this.collection);
final Map collection;
/** Iterate over the collection, passing just the value parameters. */
forEach(f) {
collection.forEach((key, value) => f(value));
}
/**
* Return a new collection whose keys are the same as [collection], but whose
* values are the result of applying [f] to the key/value pairs. So, if
* [collection] is a List, it will be the same as if map() had been called
* directly on [collection].
*/
map(Function f) {
var result = new Map();
collection.forEach((key, value) => result[key] = f(value));
return result;
}
/**
* Return an iterator that behaves like a List iterator, taking one parameter.
*/
Iterator get iterator => collection.values.iterator;
}
/**
* This acts as a stand-in for some value that cannot be hashed. We can't
* just use const Object() because the compiler will fold them together.
*/
class _Sentinel {
final _wrappedObject;
const _Sentinel(this._wrappedObject);
}
/**
* This provides an identity map which also allows true, false, and null
* as valid keys. In the interests of avoiding duplicating map code, and
* because hashCode for arbitrary objects is currently very slow on the VM,
* just do a linear lookup.
*/
class IdentityMap<K, V> implements Map<K, V> {
final List<K> keys = <K>[];
final List<V> values = <V>[];
V operator [](K key) {
var index = _indexOf(key);
return (index == -1) ? null : values[index];
}
void operator []=(K key, V value) {
var index = _indexOf(key);
if (index == -1) {
keys.add(key);
values.add(value);
} else {
values[index] = value;
}
}
putIfAbsent(K key, Function ifAbsent) {
var index = _indexOf(key);
if (index == -1) {
keys.add(key);
values.add(ifAbsent());
return values.last;
} else {
return values[index];
}
}
_indexOf(K key) {
// Go backwards on the guess that we are most likely to access the most
// recently added.
// Make strings and primitives unique
var compareEquality = isPrimitive(key);
for (var i = keys.length - 1; i >= 0; i--) {
var equal = compareEquality ? key == keys[i] : identical(key, keys[i]);
if (equal) return i;
}
return -1;
}
bool containsKey(K key) => _indexOf(key) != -1;
void forEach(f(K key, V value)) {
for (var i = 0; i < keys.length; i++) {
f(keys[i], values[i]);
}
}
V remove(K key) {
var index = _indexOf(key);
if (index == -1) return null;
keys.removeAt(index);
return values.removeAt(index);
}
int get length => keys.length;
void clear() {
keys.clear();
values.clear();
}
bool get isEmpty => keys.isEmpty;
bool get isNotEmpty => !isEmpty;
// Note that this is doing an equality comparison.
bool containsValue(x) => values.contains(x);
}