Index: lib/src/utils.dart |
diff --git a/lib/src/utils.dart b/lib/src/utils.dart |
index 5d35fd21505607b5f39c1c1c60f62e0ad951dda7..2bead98e47458e926c7cfae2970b725fba986bba 100644 |
--- a/lib/src/utils.dart |
+++ b/lib/src/utils.dart |
@@ -55,47 +55,6 @@ class Pair<E, F> { |
int get hashCode => first.hashCode ^ last.hashCode; |
} |
-/// A completer that waits until all added [Future]s complete. |
-// TODO(rnystrom): Copied from web_components. Remove from here when it gets |
-// added to dart:core. (See #6626.) |
-class FutureGroup<T> { |
- int _pending = 0; |
- Completer<List<T>> _completer = new Completer<List<T>>(); |
- final List<Future<T>> futures = <Future<T>>[]; |
- bool completed = false; |
- |
- final List<T> _values = <T>[]; |
- |
- /// Wait for [task] to complete. |
- Future<T> add(Future<T> task) { |
- if (completed) { |
- throw new StateError("The FutureGroup has already completed."); |
- } |
- |
- _pending++; |
- futures.add(task.then((value) { |
- if (completed) return; |
- |
- _pending--; |
- _values.add(value); |
- |
- if (_pending <= 0) { |
- completed = true; |
- _completer.complete(_values); |
- } |
- }).catchError((e, stackTrace) { |
- if (completed) return; |
- |
- completed = true; |
- _completer.completeError(e, stackTrace); |
- })); |
- |
- return task; |
- } |
- |
- Future<List> get future => _completer.future; |
-} |
- |
/// Like [new Future], but avoids around issue 11911 by using [new Future.value] |
/// under the covers. |
Future newFuture(callback()) => new Future.value().then((_) => callback()); |
@@ -145,7 +104,7 @@ Future captureErrors(Future callback(), {bool captureStackChains: false}) { |
/// only returns once all Futures have completed, successfully or not. |
/// |
/// This will wrap the first error thrown in a [SilentException] and rethrow it. |
-Future waitAndPrintErrors(Iterable<Future> futures) { |
+Future<List/*<T>*/> waitAndPrintErrors/*<T>*/(Iterable<Future/*<T>*/> futures) { |
return Future.wait(futures.map((future) { |
return future.catchError((error, stackTrace) { |
log.exception(error, stackTrace); |
@@ -160,8 +119,8 @@ Future waitAndPrintErrors(Iterable<Future> futures) { |
/// completes. |
/// |
/// The stream will be passed through unchanged. |
-StreamTransformer onDoneTransformer(void onDone()) { |
- return new StreamTransformer.fromHandlers(handleDone: (sink) { |
+StreamTransformer/*<T, T>*/ onDoneTransformer/*<T>*/(void onDone()) { |
+ return new StreamTransformer/*<T, T>*/.fromHandlers(handleDone: (sink) { |
onDone(); |
sink.close(); |
}); |
@@ -274,23 +233,6 @@ bool isLoopback(String host) { |
} |
} |
-/// Flattens nested lists inside an iterable into a single list containing only |
-/// non-list elements. |
-List flatten(Iterable nested) { |
- var result = []; |
- helper(list) { |
- for (var element in list) { |
- if (element is List) { |
- helper(element); |
- } else { |
- result.add(element); |
- } |
- } |
- } |
- helper(nested); |
- return result; |
-} |
- |
/// Randomly chooses a single element in [elements]. |
/*=T*/ choose/*<T>*/(List/*<T>*/ elements) => |
elements[random.nextInt(elements.length)]; |
@@ -312,7 +254,7 @@ bool overlaps(Set set1, Set set2) { |
} |
/// Returns a list containing the sorted elements of [iter]. |
-List ordered(Iterable<Comparable> iter) { |
+List/*<T>*/ ordered/*<T extends Comparable<T>>*/(Iterable/*<T>*/ iter) { |
var list = iter.toList(); |
list.sort(); |
return list; |
@@ -346,84 +288,13 @@ Iterable<Pair> pairs(Iterable iter) { |
}); |
} |
-/// Creates a new map from [map] with new keys and values. |
-/// |
-/// The return values of [key] are used as the keys and the return values of |
-/// [value] are used as the values for the new map. |
-/// |
-/// [key] defaults to returning the original key and [value] defaults to |
-/// returning the original value. |
-Map mapMap(Map map, {key(key, value), value(key, value)}) { |
- if (key == null) key = (key, _) => key; |
- if (value == null) value = (_, value) => value; |
- |
- var result = {}; |
- map.forEach((mapKey, mapValue) { |
- result[key(mapKey, mapValue)] = value(mapKey, mapValue); |
- }); |
- return result; |
-} |
- |
-/// Like [Map.fromIterable], but [key] and [value] may return [Future]s. |
-Future<Map> mapFromIterableAsync(Iterable iter, {key(element), |
- value(element)}) { |
- if (key == null) key = (element) => element; |
- if (value == null) value = (element) => element; |
- |
- var map = new Map(); |
- return Future.wait(iter.map((element) { |
- return Future.wait([ |
- new Future.sync(() => key(element)), |
- new Future.sync(() => value(element)) |
- ]).then((results) { |
- map[results[0]] = results[1]; |
- }); |
- })).then((_) => map); |
-} |
- |
-/// Returns a new map with all entries in both [map1] and [map2]. |
-/// |
-/// If there are overlapping keys, [map2]'s value wins. |
-Map mergeMaps(Map map1, Map map2) { |
- var result = {}; |
- result.addAll(map1); |
- result.addAll(map2); |
- return result; |
-} |
- |
-/// Returns the transitive closure of [graph]. |
-/// |
-/// This assumes [graph] represents a graph with a vertex for each key and an |
-/// edge betweek each key and the values for that key. |
-Map<dynamic, Set> transitiveClosure(Map<dynamic, Iterable> graph) { |
- // This uses the Floyd-Warshall algorithm |
- // (https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm). |
- var result = {}; |
- graph.forEach((vertex, edges) { |
- result[vertex] = new Set.from(edges)..add(vertex); |
- }); |
- |
- for (var vertex1 in graph.keys) { |
- for (var vertex2 in graph.keys) { |
- for (var vertex3 in graph.keys) { |
- if (result[vertex2].contains(vertex1) && |
- result[vertex1].contains(vertex3)) { |
- result[vertex2].add(vertex3); |
- } |
- } |
- } |
- } |
- |
- return result; |
-} |
- |
/// Given a list of filenames, returns a set of patterns that can be used to |
/// filter for those filenames. |
/// |
/// For a given path, that path ends with some string in the returned set if |
/// and only if that path's basename is in [files]. |
Set<String> createFileFilter(Iterable<String> files) { |
- return files.expand((file) { |
+ return files.expand/*<String>*/((file) { |
var result = ["/$file"]; |
if (Platform.operatingSystem == 'windows') result.add("\\$file"); |
return result; |
@@ -436,7 +307,7 @@ Set<String> createFileFilter(Iterable<String> files) { |
/// For a given path, that path contains some string in the returned set if |
/// and only if one of that path's components is in [dirs]. |
Set<String> createDirectoryFilter(Iterable<String> dirs) { |
- return dirs.expand((dir) { |
+ return dirs.expand/*<String>*/((dir) { |
var result = ["/$dir/"]; |
if (Platform.operatingSystem == 'windows') { |
result..add("/$dir\\")..add("\\$dir/")..add("\\$dir\\"); |
@@ -533,7 +404,7 @@ void chainToCompleter(Future future, Completer completer) { |
/// emitting the same values and errors as [stream], but only if at least one |
/// value can be read successfully. If an error occurs before any values are |
/// emitted, the returned Future completes to that error. |
-Future<Stream> validateStream(Stream stream) { |
+Future<Stream/*<T>*/> validateStream/*<T>*/(Stream/*<T>*/ stream) { |
var completer = new Completer<Stream>(); |
var controller = new StreamController(sync: true); |
@@ -594,66 +465,6 @@ Pair<Stream, StreamSubscription> streamWithSubscription(Stream stream) { |
return new Pair<Stream, StreamSubscription>(controller.stream, subscription); |
} |
-// TODO(nweiz): remove this when issue 7787 is fixed. |
-/// Creates two single-subscription [Stream]s that each emit all values and |
-/// errors from [stream]. |
-/// |
-/// This is useful if [stream] is single-subscription but multiple subscribers |
-/// are necessary. |
-Pair<Stream, Stream> tee(Stream stream) { |
- var controller1 = new StreamController(sync: true); |
- var controller2 = new StreamController(sync: true); |
- stream.listen((value) { |
- controller1.add(value); |
- controller2.add(value); |
- }, onError: (error, [stackTrace]) { |
- controller1.addError(error, stackTrace); |
- controller2.addError(error, stackTrace); |
- }, onDone: () { |
- controller1.close(); |
- controller2.close(); |
- }); |
- return new Pair<Stream, Stream>(controller1.stream, controller2.stream); |
-} |
- |
-/// Merges [stream1] and [stream2] into a single stream that emits events from |
-/// both sources. |
-Stream mergeStreams(Stream stream1, Stream stream2) { |
- var doneCount = 0; |
- var controller = new StreamController(sync: true); |
- |
- for (var stream in [stream1, stream2]) { |
- stream.listen( |
- controller.add, |
- onError: controller.addError, |
- onDone: () { |
- doneCount++; |
- if (doneCount == 2) controller.close(); |
- }); |
- } |
- |
- return controller.stream; |
-} |
- |
-/// Returns a [Stream] that will emit the same values as the stream returned by |
-/// [callback]. |
-/// |
-/// [callback] will only be called when the returned [Stream] gets a subscriber. |
-Stream callbackStream(Stream callback()) { |
- var subscription; |
- var controller; |
- controller = new StreamController(onListen: () { |
- subscription = callback().listen(controller.add, |
- onError: controller.addError, |
- onDone: controller.close); |
- }, |
- onCancel: () => subscription.cancel(), |
- onPause: () => subscription.pause(), |
- onResume: () => subscription.resume(), |
- sync: true); |
- return controller.stream; |
-} |
- |
/// A regular expression matching a trailing CR character. |
final _trailingCR = new RegExp(r"\r$"); |
@@ -728,7 +539,7 @@ Uri addQueryParameters(Uri url, Map<String, String> parameters) { |
/// Convert a URL query string (or `application/x-www-form-urlencoded` body) |
/// into a [Map] from parameter names to values. |
Map<String, String> queryToMap(String queryList) { |
- var map = {}; |
+ var map = <String, String>{}; |
for (var pair in queryList.split("&")) { |
var split = split1(pair, "="); |
if (split.isEmpty) continue; |
@@ -755,7 +566,7 @@ String mapToQuery(Map<String, String> map) { |
} |
/// Returns the union of all elements in each set in [sets]. |
-Set unionAll(Iterable<Set> sets) => |
+Set/*<T>*/ unionAll/*<T>*/(Iterable<Set/*<T>*/> sets) => |
sets.fold(new Set(), (union, set) => union.union(set)); |
// TODO(nweiz): remove this when issue 9068 has been fixed. |
@@ -795,13 +606,12 @@ String niceDuration(Duration duration) { |
// If we're using verbose logging, be more verbose but more accurate when |
// reporting timing information. |
- if (log.verbosity.isLevelVisible(log.Level.FINE)) { |
- ms = padLeft(ms.toString(), 3, '0'); |
- } else { |
- ms ~/= 100; |
- } |
+ var msString = log.verbosity.isLevelVisible(log.Level.FINE) |
+ ? padLeft(ms.toString(), 3, '0') |
+ : (ms ~/ 100).toString(); |
- return "$result${hasMinutes ? padLeft(s.toString(), 2, '0') : s}.${ms}s"; |
+ return "$result${hasMinutes ? padLeft(s.toString(), 2, '0') : s}" |
+ ".${msString}s"; |
} |
/// Decodes a URL-encoded string. |
@@ -815,26 +625,27 @@ String urlDecode(String encoded) => |
/// within. |
/// |
/// Completes with the fully resolved structure. |
-Future awaitObject(object) { |
+Future/*<T>*/ awaitObject/*<T>*/(/*=T*/ object) async { |
// Unroll nested futures. |
- if (object is Future) return object.then(awaitObject); |
+ if (object is Future) return await awaitObject(await object); |
+ |
if (object is Iterable) { |
- return Future.wait(object.map(awaitObject).toList()); |
+ // TODO(nweiz): Remove the unnecessary as check when sdk#26965 is fixed. |
+ return await Future.wait((object as Iterable).map(awaitObject)) |
+ as List/*=T*/; |
} |
- if (object is! Map) return new Future.value(object); |
- var pairs = <Future<Pair>>[]; |
- object.forEach((key, value) { |
- pairs.add(awaitObject(value) |
- .then((resolved) => new Pair(key, resolved))); |
- }); |
- return Future.wait(pairs).then((resolvedPairs) { |
- var map = {}; |
- for (var pair in resolvedPairs) { |
- map[pair.first] = pair.last; |
- } |
- return map; |
- }); |
+ if (object is Map) { |
+ // TODO(nweiz): Remove the unnecessary as check when sdk#26965 is fixed. |
+ var oldMap = object as Map; |
+ var newMap = {}; |
+ await Future.wait(oldMap.keys.map((key) async { |
+ newMap[key] = await awaitObject(await oldMap[key]); |
+ })); |
+ return newMap as Map/*=T*/; |
+ } |
+ |
+ return object; |
} |
/// Whether "special" strings such as Unicode characters or color escapes are |