Chromium Code Reviews| Index: lib/collections/helpers.dart |
| diff --git a/lib/collections/helpers.dart b/lib/collections/helpers.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..747cd4aabe96f19f90366210ea0c94253c61c97e |
| --- /dev/null |
| +++ b/lib/collections/helpers.dart |
| @@ -0,0 +1,363 @@ |
| +// 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. |
| + |
| +/** |
| + * The [Collections] class implements static methods useful when |
| + * writing a class that implements [Collection] and the [iterator] |
| + * method. |
| + */ |
| +class Collections { |
| + static bool contains(Iterable iterable, var element) { |
| + for (final e in iterable) { |
| + if (element == e) return true; |
| + } |
| + return false; |
| + } |
| + |
| + static void forEach(Iterable iterable, void f(o)) { |
| + for (final e in iterable) { |
| + f(e); |
| + } |
| + } |
| + |
| + static bool some(Iterable iterable, bool f(o)) { |
| + for (final e in iterable) { |
| + if (f(e)) return true; |
| + } |
| + return false; |
| + } |
| + |
| + static bool every(Iterable iterable, bool f(o)) { |
| + for (final e in iterable) { |
| + if (!f(e)) return false; |
| + } |
| + return true; |
| + } |
| + |
| + static List map(Iterable source, List destination, f(o)) { |
| + for (final e in source) { |
| + destination.add(f(e)); |
| + } |
| + return destination; |
| + } |
| + |
| + static Dynamic reduce(Iterable iterable, |
| + Dynamic initialValue, |
| + Dynamic combine(Dynamic previousValue, element)) { |
| + for (final element in iterable) { |
| + initialValue = combine(initialValue, element); |
| + } |
| + return initialValue; |
| + } |
| + |
| + static List filter(Iterable source, List destination, bool f(o)) { |
| + for (final e in source) { |
| + if (f(e)) destination.add(e); |
| + } |
| + return destination; |
| + } |
| + |
| + static bool isEmpty(Iterable iterable) { |
| + return !iterable.iterator().hasNext; |
| + } |
| + |
| + // TODO(jjb): visiting list should be an identityHashSet when it exists |
| + |
| + /** |
| + * Returns a string representing the specified collection. If the |
| + * collection is a [List], the returned string looks like this: |
| + * [:'[element0, element1, ... elementN]':]. The value returned by its |
| + * [toString] method is used to represent each element. If the specified |
| + * collection is not a list, the returned string looks like this: |
| + * [:{element0, element1, ... elementN}:]. In other words, the strings |
| + * returned for lists are surrounded by square brackets, while the strings |
| + * returned for other collections are surrounded by curly braces. |
| + * |
| + * If the specified collection contains a reference to itself, either |
| + * directly or indirectly through other collections or maps, the contained |
| + * reference is rendered as [:'[...]':] if it is a list, or [:'{...}':] if |
| + * it is not. This prevents the infinite regress that would otherwise occur. |
| + * So, for example, calling this method on a list whose sole element is a |
| + * reference to itself would return [:'[[...]]':]. |
| + * |
| + * A typical implementation of a collection's [toString] method will |
| + * simply return the results of this method applied to the collection. |
| + */ |
| + static String collectionToString(Collection c) { |
| + var result = new StringBuffer(); |
| + _emitCollection(c, result, new List()); |
| + return result.toString(); |
| + } |
| + |
| + /** |
| + * Appends a string representing the specified collection to the specified |
| + * string buffer. The string is formatted as per [collectionToString]. |
| + * The [:visiting:] list contains references to all of the enclosing |
| + * collections and maps (which are currently in the process of being |
| + * emitted into [:result:]). The [:visiting:] parameter allows this method to |
| + * generate a [:'[...]':] or [:'{...}':] where required. In other words, |
| + * it allows this method and [_emitMap] to identify recursive collections |
| + * and maps. |
| + */ |
| + static void _emitCollection(Collection c, StringBuffer result, List visiting) { |
|
floitsch
2012/10/25 12:11:28
nyc: 80chars.
Anders Johnsen
2012/10/25 12:24:30
Done.
|
| + visiting.add(c); |
| + bool isList = c is List; |
| + result.add(isList ? '[' : '{'); |
| + |
| + bool first = true; |
| + for (var e in c) { |
| + if (!first) { |
| + result.add(', '); |
| + } |
| + first = false; |
| + _emitObject(e, result, visiting); |
| + } |
| + |
| + result.add(isList ? ']' : '}'); |
| + visiting.removeLast(); |
| + } |
| + |
| + /** |
| + * Appends a string representing the specified object to the specified |
| + * string buffer. If the object is a [Collection] or [Map], it is formatted |
| + * as per [collectionToString] or [mapToString]; otherwise, it is formatted |
| + * by invoking its own [toString] method. |
| + * |
| + * The [:visiting:] list contains references to all of the enclosing |
| + * collections and maps (which are currently in the process of being |
| + * emitted into [:result:]). The [:visiting:] parameter allows this method |
| + * to generate a [:'[...]':] or [:'{...}':] where required. In other words, |
| + * it allows this method and [_emitCollection] to identify recursive maps |
| + * and collections. |
| + */ |
| + static void _emitObject(Object o, StringBuffer result, List visiting) { |
| + if (o is Collection) { |
| + if (_containsRef(visiting, o)) { |
| + result.add(o is List ? '[...]' : '{...}'); |
| + } else { |
| + _emitCollection(o, result, visiting); |
| + } |
| + } else if (o is Map) { |
| + if (_containsRef(visiting, o)) { |
| + result.add('{...}'); |
| + } else { |
| + Maps._emitMap(o, result, visiting); |
| + } |
| + } else { // o is neither a collection nor a map |
| + result.add(o); |
| + } |
| + } |
| + |
| + /** |
| + * Returns true if the specified collection contains the specified object |
| + * reference. |
| + */ |
| + static _containsRef(Collection c, Object ref) { |
| + for (var e in c) { |
| + if (e === ref) return true; |
| + } |
| + return false; |
| + } |
| +} |
| + |
| + |
| +// TODO(ngeoffray): Rename to Lists. |
| +class Arrays { |
| + static void copy(List src, int srcStart, |
| + List dst, int dstStart, int count) { |
| + if (srcStart === null) srcStart = 0; |
| + if (dstStart === null) dstStart = 0; |
| + |
| + if (srcStart < dstStart) { |
| + for (int i = srcStart + count - 1, j = dstStart + count - 1; |
| + i >= srcStart; i--, j--) { |
| + dst[j] = src[i]; |
| + } |
| + } else { |
| + for (int i = srcStart, j = dstStart; i < srcStart + count; i++, j++) { |
| + dst[j] = src[i]; |
| + } |
| + } |
| + } |
| + |
| + static bool areEqual(List a, Object b) { |
| + if (a === b) return true; |
| + if (!(b is List)) return false; |
| + int length = a.length; |
| + if (length != b.length) return false; |
| + |
| + for (int i = 0; i < length; i++) { |
| + if (a[i] !== b[i]) return false; |
| + } |
| + return true; |
| + } |
| + |
| + /** |
| + * Returns the index in the list [a] of the given [element], starting |
| + * the search at index [startIndex] to [endIndex] (exclusive). |
| + * Returns -1 if [element] is not found. |
| + */ |
| + static int indexOf(List a, |
| + Object element, |
| + int startIndex, |
| + int endIndex) { |
| + if (startIndex >= a.length) { |
| + return -1; |
| + } |
| + if (startIndex < 0) { |
| + startIndex = 0; |
| + } |
| + for (int i = startIndex; i < endIndex; i++) { |
| + if (a[i] == element) { |
| + return i; |
| + } |
| + } |
| + return -1; |
| + } |
| + |
| + /** |
| + * Returns the last index in the list [a] of the given [element], starting |
| + * the search at index [startIndex] to 0. |
| + * Returns -1 if [element] is not found. |
| + */ |
| + static int lastIndexOf(List a, Object element, int startIndex) { |
| + if (startIndex < 0) { |
| + return -1; |
| + } |
| + if (startIndex >= a.length) { |
| + startIndex = a.length - 1; |
| + } |
| + for (int i = startIndex; i >= 0; i--) { |
| + if (a[i] == element) { |
| + return i; |
| + } |
| + } |
| + return -1; |
| + } |
| + |
| + static void rangeCheck(List a, int start, int length) { |
| + if (length < 0) { |
| + throw new ArgumentError("negative length $length"); |
| + } |
| + if (start < 0 ) { |
| + String message = "$start must be greater than or equal to 0"; |
| + throw new IndexOutOfRangeException(message); |
| + } |
| + if (start + length > a.length) { |
| + String message = "$start + $length must be in the range [0..${a.length})"; |
| + throw new IndexOutOfRangeException(message); |
| + } |
| + } |
| +} |
| + |
| + |
| +/* |
| + * Helper class which implements complex [Map] operations |
| + * in term of basic ones ([Map.getKeys], [Map.operator []], |
| + * [Map.operator []=] and [Map.remove].) Not all methods are |
| + * necessary to implement each particular operation. |
| + */ |
| +class Maps { |
| + static bool containsValue(Map map, value) { |
| + for (final v in map.getValues()) { |
| + if (value == v) { |
| + return true; |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + static bool containsKey(Map map, key) { |
| + for (final k in map.getKeys()) { |
| + if (key == k) { |
| + return true; |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + static putIfAbsent(Map map, key, ifAbsent()) { |
| + if (map.containsKey(key)) { |
| + return map[key]; |
| + } |
| + final v = ifAbsent(); |
| + map[key] = v; |
| + return v; |
| + } |
| + |
| + static clear(Map map) { |
| + for (final k in map.getKeys()) { |
| + map.remove(k); |
| + } |
| + } |
| + |
| + static forEach(Map map, void f(key, value)) { |
| + for (final k in map.getKeys()) { |
| + f(k, map[k]); |
| + } |
| + } |
| + |
| + static Collection getValues(Map map) { |
| + final result = []; |
| + for (final k in map.getKeys()) { |
| + result.add(map[k]); |
| + } |
| + return result; |
| + } |
| + |
| + static int length(Map map) => map.getKeys().length; |
| + |
| + static bool isEmpty(Map map) => length(map) == 0; |
| + |
| + /** |
| + * Returns a string representing the specified map. The returned string |
| + * looks like this: [:'{key0: value0, key1: value1, ... keyN: valueN}':]. |
| + * The value returned by its [toString] method is used to represent each |
| + * key or value. |
| + * |
| + * If the map collection contains a reference to itself, either |
| + * directly as a key or value, or indirectly through other collections |
| + * or maps, the contained reference is rendered as [:'{...}':]. This |
| + * prevents the infinite regress that would otherwise occur. So, for example, |
| + * calling this method on a map whose sole entry maps the string key 'me' |
| + * to a reference to the map would return [:'{me: {...}}':]. |
| + * |
| + * A typical implementation of a map's [toString] method will |
| + * simply return the results of this method applied to the collection. |
| + */ |
| + static String mapToString(Map m) { |
| + var result = new StringBuffer(); |
| + _emitMap(m, result, new List()); |
| + return result.toString(); |
| + } |
| + |
| + /** |
| + * Appends a string representing the specified map to the specified |
| + * string buffer. The string is formatted as per [mapToString]. |
| + * The [:visiting:] list contains references to all of the enclosing |
| + * collections and maps (which are currently in the process of being |
| + * emitted into [:result:]). The [:visiting:] parameter allows this method |
| + * to generate a [:'[...]':] or [:'{...}':] where required. In other words, |
| + * it allows this method and [_emitCollection] to identify recursive maps |
| + * and collections. |
| + */ |
| + static void _emitMap(Map m, StringBuffer result, List visiting) { |
| + visiting.add(m); |
| + result.add('{'); |
| + |
| + bool first = true; |
| + m.forEach((k, v) { |
| + if (!first) { |
| + result.add(', '); |
| + } |
| + first = false; |
| + Collections._emitObject(k, result, visiting); |
| + result.add(': '); |
| + Collections._emitObject(v, result, visiting); |
| + }); |
| + |
| + result.add('}'); |
| + visiting.removeLast(); |
| + } |
| +} |