Specs: Implement the Dispatcher classes.

R=abarth@chromium.org

Review URL: https://codereview.chromium.org/919693007
This commit is contained in:
Hixie 2015-02-12 13:40:35 -08:00
parent f22842f3bc
commit aa99c2f67b
2 changed files with 109 additions and 34 deletions

View File

@ -6,6 +6,111 @@ SKY MODULE
<!-- part of sky:core -->
<script>
import 'dart:collection';
import 'dart:async';
class ExceptionAndStackTrace<T> {
const ExceptionAndStackTrace(this.exception, this.stackTrace);
final T exception;
final StackTrace stackTrace;
}
class ExceptionListException<T> extends IterableMixin<ExceptionAndStackTrace<T>> implements Exception {
List<ExceptionAndStackTrace<T>> _exceptions;
void add(T exception, [StackTrace stackTrace = null]) {
if (_exceptions == null)
_exceptions = new List<ExceptionAndStackTrace<T>>();
_exceptions.add(new ExceptionAndStackTrace<T>(exception, stackTrace));
}
int get length => _exceptions == null ? 0 : _exceptions.length;
Iterator<ExceptionAndStackTrace<T>> get iterator => _exceptions.iterator;
}
typedef bool Filter<T>(T t);
typedef void Handler<T>(T t);
class DispatcherController<T> {
DispatcherController() : dispatcher = new Dispatcher<T>();
final Dispatcher<T> dispatcher;
void add(T data) => dispatcher._add(data);
}
class Pair<A, B> {
const Pair(this.a, this.b);
final A a;
final B b;
}
class Dispatcher<T> {
List<Pair<Handler, ZoneUnaryCallback>> _listeners;
void listen(Handler<T> handler) {
// you should not throw out of this handler
if (_listeners == null)
_listeners = new List<Pair<Handler, ZoneUnaryCallback>>();
_listeners.add(new Pair<Handler, ZoneUnaryCallback>(handler, Zone.current.bindUnaryCallback(handler)));
}
bool unlisten(Handler<T> handler) {
if (_listeners == null)
return false;
var target = _listeners.lastWhere((v) => v.a == handler, orElse: () => null);
if (target == null)
return false;
_listeners.remove(target);
return true;
}
void _add(T data) {
if (_listeners == null)
return;
ExceptionListException exceptions = new ExceptionListException();
// we make a copy of the list here so that the listeners can
// mutate our list without worry
_listeners.toList().forEach((Pair<Handler, ZoneUnaryCallback> item) {
try {
item.b(data);
} catch (exception, stackTrace) {
exceptions.add(exception, stackTrace);
}
});
if (exceptions.length > 0)
throw exceptions;
}
Dispatcher<T> where(Filter<T> filter) {
var subdispatcher = new Dispatcher<T>();
listen((T data) {
if (filter(data))
subdispatcher._add(data);
});
return subdispatcher;
}
Dispatcher<T> until(Filter<T> filter) {
var subdispatcher = new Dispatcher<T>();
Handler handler;
handler = (T data) {
if (filter(data))
unlisten(handler);
else
subdispatcher._add(data);
};
listen(handler);
return subdispatcher;
}
Future<T> firstWhere(Filter<T> filter) {
Completer completer = new Completer();
Handler handler;
handler = (T data) {
if (filter(data)) {
completer.complete(data);
unlisten(handler);
}
};
listen(handler);
return completer.future;
}
}
abstract class Event<ReturnType> {
Event() { init(); }
void init() { }
@ -29,12 +134,12 @@ abstract class Event<ReturnType> {
}
class EventTarget {
EventTarget() : _eventsController = new DispatcherController<@nonnull Event>();
EventTarget() : _eventsController = new DispatcherController<Event>();
Dispatcher get events => _eventsController.dispatcher;
EventTarget parentNode;
List<@nonnull EventTarget> getEventDispatchChain() {
List<EventTarget> getEventDispatchChain() {
if (this.parentNode == null) {
return [this];
} else {
@ -46,7 +151,7 @@ class EventTarget {
final DispatcherController _eventsController;
dynamic dispatchEvent(@nonnull Event event, { dynamic defaultResult: null }) { // O(N*M) where N is the length of the chain and M is the average number of listeners per link in the chain
dynamic dispatchEvent(Event event, { dynamic defaultResult: null }) { // O(N*M) where N is the length of the chain and M is the average number of listeners per link in the chain
// note: this will throw an ExceptionListException<ExceptionListException> if any of the listeners threw
assert(event != null); // event must be non-null
event.handled = false;
@ -71,7 +176,7 @@ class EventTarget {
return event.result;
}
void _dispatchEventLocally(@nonnull Event event) {
void _dispatchEventLocally(Event event) {
event._currentTarget = this;
_eventsController.add(event);
}

View File

@ -105,33 +105,3 @@ assumed to exist:
ClassName = SuperclassName;
ClassName.namedConstructor = SuperclassName.otherNamedConstructor;
```
* It is assumed that the standard library includes something that
matches this pattern:
```dart
class DispatcherController<T> {
Dispatcher<T> _dispatcher;
Dispatcher<T> get dispatcher => _dispatcher;
void add(T data) {
// ...
}
}
typedef bool Filter<T>(T t);
typedef void Handler<T>(T t);
class Dispatcher<T> {
Dispatcher<T> where(Filter<T> filter) { /*...*/ return this; }
void listen(Handler<T> handler) { /* ... */ }
}
class ExceptionListException<T> extends Exception with IterableMixin<T> {
List<T> _exceptions;
void add(T exception) {
if (_exceptions == null)
_exceptions = new List<T>();
_exceptions.add(exception);
}
int get length => _exceptions == null ? 0 : _exceptions.length;
Iterator<T> iterator() => _exceptions.iterator();
}
```