mirror of
https://github.com/dart-lang/sdk
synced 2024-09-23 10:28:32 +00:00
Wrote functions to convert collections and maps to strings and invoked
these functions from the toString methods of all built-in Collection and Map implementations. Wrote smoke tests and basher tests. Review URL: https://chromiumcodereview.appspot.com//9431015 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@4421 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
372a1e8602
commit
a2f2769ec3
150
corelib/src/implementation/collections.dart
Normal file
150
corelib/src/implementation/collections.dart
Normal file
|
@ -0,0 +1,150 @@
|
|||
// 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 void forEach(Iterable<Object> iterable, void f(Object o)) {
|
||||
for (final e in iterable) {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
|
||||
static bool some(Iterable<Object> iterable, bool f(Object o)) {
|
||||
for (final e in iterable) {
|
||||
if (f(e)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool every(Iterable<Object> iterable, bool f(Object o)) {
|
||||
for (final e in iterable) {
|
||||
if (!f(e)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static List<Object> map(Iterable<Object> source,
|
||||
List<Object> destination,
|
||||
f(Object o)) {
|
||||
for (final e in source) {
|
||||
destination.add(f(e));
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
static List<Object> filter(Iterable<Object> source,
|
||||
List<Object> destination,
|
||||
bool f(Object o)) {
|
||||
for (final e in source) {
|
||||
if (f(e)) destination.add(e);
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
static bool isEmpty(Iterable<Object> 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) {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
{
|
||||
'sources': [
|
||||
'collections.dart',
|
||||
'dual_pivot_quicksort.dart',
|
||||
'duration_implementation.dart',
|
||||
'exceptions.dart',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
|
@ -268,6 +268,10 @@ class HashMapImplementation<K extends Hashable, V> implements HashMap<K, V> {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String toString() {
|
||||
return Maps.mapToString(this);
|
||||
}
|
||||
}
|
||||
|
||||
class HashSetImplementation<E extends Hashable> implements HashSet<E> {
|
||||
|
@ -376,6 +380,10 @@ class HashSetImplementation<E extends Hashable> implements HashSet<E> {
|
|||
return new HashSetIterator<E>(this);
|
||||
}
|
||||
|
||||
String toString() {
|
||||
return Collections.collectionToString(this);
|
||||
}
|
||||
|
||||
// The map backing this set. The associations in this map are all
|
||||
// of the form element -> element. If a value is not in the map,
|
||||
// then it is not in the set.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
|
@ -114,4 +114,8 @@ class LinkedHashMapImplementation<K extends Hashable, V>
|
|||
_map.clear();
|
||||
_list.clear();
|
||||
}
|
||||
|
||||
String toString() {
|
||||
return Maps.mapToString(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
|
@ -59,4 +59,55 @@ class Maps {
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
|
@ -231,6 +231,10 @@ class DoubleLinkedQueue<E> implements Queue<E> {
|
|||
_DoubleLinkedQueueIterator<E> iterator() {
|
||||
return new _DoubleLinkedQueueIterator<E>(_sentinel);
|
||||
}
|
||||
|
||||
String toString() {
|
||||
return Collections.collectionToString(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _DoubleLinkedQueueIterator<E> implements Iterator<E> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
|
@ -234,4 +234,8 @@ class SplayTree<K extends Comparable, V> implements Map<K, V> {
|
|||
forEach((K k, V v) { list.add(v); });
|
||||
return list;
|
||||
}
|
||||
|
||||
String toString() {
|
||||
return Maps.mapToString(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
|
@ -31,7 +31,7 @@ class ObjectArray<E> implements List<E> {
|
|||
void operator []=(int index, E value) native "ObjectArray_setIndexed";
|
||||
|
||||
String toString() {
|
||||
return Arrays.asString(this);
|
||||
return Collections.collectionToString(this);
|
||||
}
|
||||
|
||||
int get length() native "ObjectArray_getLength";
|
||||
|
@ -238,7 +238,7 @@ class ImmutableArray<E> implements List<E> {
|
|||
}
|
||||
|
||||
String toString() {
|
||||
return Arrays.asString(this);
|
||||
return Collections.collectionToString(this);
|
||||
}
|
||||
|
||||
int indexOf(E element, [int start = 0]) {
|
||||
|
|
|
@ -1,21 +1,9 @@
|
|||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
// TODO(ngeoffray): Rename to Lists.
|
||||
class Arrays {
|
||||
|
||||
static String asString(List list) {
|
||||
String result = "[";
|
||||
int len = list.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
// TODO(4466785): Deal with recursion and formatting.
|
||||
result += list[i].toString() + ", ";
|
||||
}
|
||||
result += "]";
|
||||
return result;
|
||||
}
|
||||
|
||||
static void copy(List src, int srcStart,
|
||||
List dst, int dstStart, int count) {
|
||||
if (srcStart === null) srcStart = 0;
|
||||
|
|
|
@ -141,6 +141,10 @@ class _ByteArrayBase {
|
|||
return this[length - 1];
|
||||
}
|
||||
|
||||
String toString() {
|
||||
return Collections.collectionToString(this);
|
||||
}
|
||||
|
||||
void setRange(int start, int length, List<int> from, [int startFrom = 0]) {
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("negative length $length");
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright (c) 2011, 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 void forEach(Iterable<Object> iterable, void f(Object o)) {
|
||||
for (final e in iterable) {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
|
||||
static bool some(Iterable<Object> iterable, bool f(Object o)) {
|
||||
for (final e in iterable) {
|
||||
if (f(e)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool every(Iterable<Object> iterable, bool f(Object o)) {
|
||||
for (final e in iterable) {
|
||||
if (!f(e)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static List<Object> map(Iterable<Object> source,
|
||||
List<Object> destination,
|
||||
f(Object o)) {
|
||||
for (final e in source) {
|
||||
destination.add(f(e));
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
static List<Object> filter(Iterable<Object> source,
|
||||
List<Object> destination,
|
||||
bool f(Object o)) {
|
||||
for (final e in source) {
|
||||
if (f(e)) destination.add(e);
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
static bool isEmpty(Iterable<Object> iterable) {
|
||||
return !iterable.iterator().hasNext();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
|
@ -227,7 +227,7 @@ class GrowableObjectArray<T> implements List<T> {
|
|||
}
|
||||
|
||||
String toString() {
|
||||
return Arrays.asString(this);
|
||||
return Collections.collectionToString(this);
|
||||
}
|
||||
|
||||
Iterator<T> iterator() {
|
||||
|
|
|
@ -88,8 +88,7 @@ class ImmutableMap<K, V> implements Map<K, V> {
|
|||
}
|
||||
|
||||
String toString() {
|
||||
// TODO(srdjan): Extend text representation.
|
||||
return "ImmutableMap";
|
||||
return Maps.mapToString(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
'array.dart',
|
||||
'arrays.dart',
|
||||
'bool.dart',
|
||||
'collections.dart',
|
||||
'date.cc',
|
||||
'date.dart',
|
||||
'double.cc',
|
||||
|
|
293
tests/corelib/src/CollectionToStringTest.dart
Normal file
293
tests/corelib/src/CollectionToStringTest.dart
Normal file
|
@ -0,0 +1,293 @@
|
|||
// 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.
|
||||
|
||||
/**
|
||||
* Tests for the toString methods on collections (including maps).
|
||||
*/
|
||||
|
||||
// TODO(jjb): seed random number generator when API allows it
|
||||
|
||||
final int NUM_TESTS = 3000;
|
||||
final int MAX_COLLECTION_SIZE = 7;
|
||||
|
||||
main() {
|
||||
smokeTest();
|
||||
exactTest();
|
||||
inexactTest();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test a few simple examples.
|
||||
*/
|
||||
void smokeTest() {
|
||||
// Non-const lists
|
||||
Expect.equals([].toString(), '[]');
|
||||
Expect.equals([1].toString(), '[1]');
|
||||
Expect.equals(['Elvis'].toString(), '[Elvis]');
|
||||
Expect.equals([1, 2].toString(), '[1, 2]');
|
||||
Expect.equals(['I', 'II'].toString(), '[I, II]');
|
||||
Expect.equals([[1, 2], [3, 4], [5, 6]].toString(), '[[1, 2], [3, 4], [5, 6]]');
|
||||
|
||||
// Const lists
|
||||
Expect.equals((const[]).toString(), '[]');
|
||||
Expect.equals((const[1]).toString(), '[1]');
|
||||
Expect.equals((const['Elvis']).toString(), '[Elvis]');
|
||||
Expect.equals((const[1, 2]).toString(), '[1, 2]');
|
||||
Expect.equals((const['I', 'II']).toString(), '[I, II]');
|
||||
Expect.equals((const[const[1, 2], const[3, 4], const[5, 6]]).toString(),
|
||||
'[[1, 2], [3, 4], [5, 6]]');
|
||||
|
||||
// Non-const maps - Note that all keys are strings; the spec currently demands this
|
||||
Expect.equals({}.toString(), '{}');
|
||||
Expect.equals({'Elvis': 'King'}.toString(), '{Elvis: King}');
|
||||
Expect.equals({'I': 1, 'II': 2}.toString(), '{I: 1, II: 2}');
|
||||
Expect.equals({'X':{'I':1, 'II':2}, 'Y':{'III':3, 'IV':4}, 'Z':{'V':5, 'VI':6}}.toString(),
|
||||
'{X: {I: 1, II: 2}, Y: {III: 3, IV: 4}, Z: {V: 5, VI: 6}}');
|
||||
|
||||
// Const maps
|
||||
Expect.equals(const{}.toString(), '{}');
|
||||
Expect.equals(const{'Elvis': 'King'}.toString(), '{Elvis: King}');
|
||||
Expect.equals(const{'I': 1, 'II': 2}.toString(), '{I: 1, II: 2}');
|
||||
Expect.equals(const{'X': const{'I': 1, 'II': 2}, 'Y': const{'III': 3, 'IV': 4},
|
||||
'Z': const{'V': 5, 'VI': 6}}.toString(),
|
||||
'{X: {I: 1, II: 2}, Y: {III: 3, IV: 4}, Z: {V: 5, VI: 6}}');
|
||||
}
|
||||
|
||||
// SERIOUS "BASHER" TESTS
|
||||
|
||||
/**
|
||||
* Generate a bunch of random collections (including Maps), and test that
|
||||
* there string form is as expected. The collections include collections
|
||||
* as elements, keys, and values, and include recursive references.
|
||||
*
|
||||
* This test restricts itself to collections with well-defined iteration
|
||||
* orders (i.e., no HashSet, HashMap).
|
||||
*/
|
||||
void exactTest() {
|
||||
for (int i = 0; i < NUM_TESTS; i++) {
|
||||
// Choose a size from 0 to MAX_COLLECTION_SIZE, favoring larger sizes
|
||||
int size = Math.sqrt(random(MAX_COLLECTION_SIZE * MAX_COLLECTION_SIZE)).toInt();
|
||||
|
||||
StringBuffer stringRep = new StringBuffer();
|
||||
Object o = randomCollection(size, stringRep, exact:true);
|
||||
Expect.equals(o.toString(), stringRep.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a bunch of random collections (including Maps), and test that
|
||||
* there string form is as expected. The collections include collections
|
||||
* as elements, keys, and values, and include recursive references.
|
||||
*
|
||||
* This test includes collections with ill-defined iteration orders (i.e.,
|
||||
* HashSet, HashMap). As a consequence, it can't use equality tests on the
|
||||
* string form. Instead, it performs equality tests on their "alphagrams."
|
||||
* This might allow false positives, but it does give a fair amount of
|
||||
* confidence.
|
||||
*/
|
||||
void inexactTest() {
|
||||
for (int i = 0; i < NUM_TESTS; i++) {
|
||||
// Choose a size from 0 to MAX_COLLECTION_SIZE, favoring larger sizes
|
||||
int size = Math.sqrt(random(MAX_COLLECTION_SIZE * MAX_COLLECTION_SIZE)).toInt();
|
||||
|
||||
StringBuffer stringRep = new StringBuffer();
|
||||
Object o = randomCollection(size, stringRep, exact:false);
|
||||
Expect.equals(alphagram(o.toString()), alphagram(stringRep.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random collection (or Map) of the specified size, placing its
|
||||
* string representation into the given string buffer.
|
||||
*
|
||||
* If exact is true, the returned collections will not be, and will not contain
|
||||
* a collection with ill-defined iteration order (i.e., a HashSet or HashMap).
|
||||
*/
|
||||
Object randomCollection(int size, StringBuffer stringRep, [bool exact]) {
|
||||
return randomCollectionHelper(size, exact, stringRep, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random collection (or map) of the specified size, placing its
|
||||
* string representation into the given string buffer. The beingMade
|
||||
* parameter is a list of collections currently under construction, i.e.,
|
||||
* candidates for recursive references.
|
||||
*
|
||||
* If exact is true, the returned collections will not be, and will not contain
|
||||
* a collection with ill-defined iteration order (i.e., a HashSet or HashMap).
|
||||
*/
|
||||
Object randomCollectionHelper(int size, bool exact, StringBuffer stringRep,
|
||||
List beingMade) {
|
||||
double interfaceFrac = Math.random();
|
||||
|
||||
if (exact) {
|
||||
if (interfaceFrac < 1/3) {
|
||||
return randomList(size, exact, stringRep, beingMade);
|
||||
} else if (interfaceFrac < 2/3) {
|
||||
return randomQueue(size, exact, stringRep, beingMade);
|
||||
} else {
|
||||
return randomMap(size, exact, stringRep, beingMade);
|
||||
}
|
||||
} else {
|
||||
if (interfaceFrac < 1/4) {
|
||||
return randomList(size, exact, stringRep, beingMade);
|
||||
} else if (interfaceFrac < 2/4) {
|
||||
return randomQueue(size, exact, stringRep, beingMade);
|
||||
} else if (interfaceFrac < 3/4) {
|
||||
return randomSet(size, exact, stringRep, beingMade);
|
||||
} else {
|
||||
return randomMap(size, exact, stringRep, beingMade);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random List of the specified size, placing its string
|
||||
* representation into the given string buffer. The beingMade
|
||||
* parameter is a list of collections currently under construction, i.e.,
|
||||
* candidates for recursive references.
|
||||
*
|
||||
* If exact is true, the returned collections will not be, and will not contain
|
||||
* a collection with ill-defined iteration order (i.e., a HashSet or HashMap).
|
||||
*/
|
||||
List randomList(int size, bool exact, StringBuffer stringRep, List beingMade) {
|
||||
return populateRandomCollection(size, exact, stringRep, beingMade, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like randomList, but returns a queue.
|
||||
*/
|
||||
Queue randomQueue(int size, bool exact, StringBuffer stringRep, List beingMade){
|
||||
return populateRandomCollection(size, exact, stringRep, beingMade, new Queue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Like randomList, but returns a Set.
|
||||
*/
|
||||
Set randomSet(int size, bool exact, StringBuffer stringRep, List beingMade) {
|
||||
// Until we have LinkedHashSet, method will only be called with exact==true
|
||||
return populateRandomSet(size, exact, stringRep, beingMade, new Set());
|
||||
}
|
||||
|
||||
/**
|
||||
* Like randomList, but returns a map.
|
||||
*/
|
||||
Map randomMap(int size, bool exact, StringBuffer stringRep, List beingMade) {
|
||||
if (exact) {
|
||||
return populateRandomMap(size, exact, stringRep, beingMade,
|
||||
new LinkedHashMap());
|
||||
} else {
|
||||
return populateRandomMap(size, exact, stringRep, beingMade,
|
||||
randomBool() ? new Map() : new LinkedHashMap());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the given empty collection with elements, emitting the string
|
||||
* representation of the collection to stringRep. The beingMade parameter is
|
||||
* a list of collections currently under construction, i.e., candidates for
|
||||
* recursive references.
|
||||
*
|
||||
* If exact is true, the elements of the returned collections will not be,
|
||||
* and will not contain a collection with ill-defined iteration order
|
||||
* (i.e., a HashSet or HashMap).
|
||||
*/
|
||||
Collection populateRandomCollection(int size, bool exact,
|
||||
StringBuffer stringRep, List beingMade, Collection coll) {
|
||||
beingMade.add(coll);
|
||||
stringRep.add(coll is List ? '[' : '{');
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i != 0) stringRep.add(', ');
|
||||
coll.add(randomElement(random(size), exact, stringRep, beingMade));
|
||||
}
|
||||
|
||||
stringRep.add(coll is List ? ']' : '}');
|
||||
beingMade.removeLast();
|
||||
return coll;
|
||||
}
|
||||
|
||||
/** Like populateRandomCollection, but for sets (elements must be hashable) */
|
||||
Set populateRandomSet(int size, bool exact, StringBuffer stringRep,
|
||||
List beingMade, Set set) {
|
||||
stringRep.add('{');
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i != 0) stringRep.add(', ');
|
||||
set.add(i);
|
||||
stringRep.add(i);
|
||||
}
|
||||
|
||||
stringRep.add('}');
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
/** Like populateRandomCollection, but for maps. */
|
||||
Map populateRandomMap(int size, bool exact, StringBuffer stringRep,
|
||||
List beingMade, Map map) {
|
||||
beingMade.add(map);
|
||||
stringRep.add('{');
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i != 0) stringRep.add(', ');
|
||||
|
||||
int key = i; // Ensures no duplicates
|
||||
stringRep.add(key);
|
||||
stringRep.add(': ');
|
||||
Object val = randomElement(random(size), exact, stringRep, beingMade);
|
||||
map[key] = val;
|
||||
}
|
||||
|
||||
stringRep.add('}');
|
||||
beingMade.removeLast();
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random element which can be an int, a collection, or a map,
|
||||
* and emits it to StringRep. The beingMade parameter is a list of collections
|
||||
* currently under construction, i.e., candidates for recursive references.
|
||||
*
|
||||
* If exact is true, the returned element will not be, and will not contain
|
||||
* a collection with ill-defined iteration order (i.e., a HashSet or HashMap).
|
||||
*/
|
||||
void randomElement(int size, bool exact, StringBuffer stringRep,
|
||||
List beingMade) {
|
||||
Object result;
|
||||
double elementTypeFrac = Math.random();
|
||||
if (elementTypeFrac < 1/3) {
|
||||
result = random(1000);
|
||||
stringRep.add(result);
|
||||
} else if (elementTypeFrac < 2/3) {
|
||||
// Element Is a random (new) collection
|
||||
result = randomCollectionHelper(size, exact, stringRep, beingMade);
|
||||
} else {
|
||||
// Element Is a random recursive ref
|
||||
result = beingMade[random(beingMade.length)];
|
||||
if (result is List)
|
||||
stringRep.add('[...]');
|
||||
else
|
||||
stringRep.add('{...}');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns a random int on [0, max) */
|
||||
int random(int max) {
|
||||
return (Math.random() * max).toInt();
|
||||
}
|
||||
|
||||
/** Returns a random boolean value. */
|
||||
bool randomBool() {
|
||||
return Math.random() < .5;
|
||||
}
|
||||
|
||||
/** Returns the alphabetized characters in a string. */
|
||||
String alphagram(String s) {
|
||||
List<int> chars = s.charCodes();
|
||||
chars.sort((int a, int b) => a - b);
|
||||
return new String.fromCharCodes(chars);
|
||||
}
|
Loading…
Reference in a new issue