| // Copyright (c) 2016, 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. |
| |
| import 'dart:async'; |
| import 'dart:collection'; |
| |
| import 'package:collection/collection.dart'; |
| import 'package:observable/observable.dart'; |
| |
| /// A [Set] that broadcasts [changes] to subscribers for efficient mutations. |
| /// |
| /// When client code expects a read heavy/write light workload, it is often more |
| /// efficient to notify _when_ something has changed, instead of constantly |
| /// diffing lists to find a single change (like an inserted element). You may |
| /// accept an observable set to be notified of mutations: |
| /// ``` |
| /// set emails(Set<String> emails) { |
| /// emailUsers(emails); |
| /// if (names is ObservableSet<String>) { |
| /// emails.changes.listen(updateEmailList); |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// *See [SetDiffer] to manually diff two lists instead* |
| abstract class ObservableSet<E> |
| implements Observable<SetChangeRecord<E>>, Set<E> { |
| /// An empty observable set that never has changes. |
| static const ObservableSet EMPTY = const _UnmodifiableObservableSet( |
| const _UnmodifiableEmptySet(), |
| ); |
| |
| /// Create a new empty observable set. |
| factory ObservableSet() => new _DelegatingObservableSet<E>(new HashSet<E>()); |
| |
| /// Like [ObservableSet.from], but creates a new empty set. |
| factory ObservableSet.createFromType(Iterable<E> other) { |
| ObservableSet<E> result; |
| if (other is LinkedHashSet) { |
| result = new _DelegatingObservableSet<E>(new LinkedHashSet<E>()); |
| } else if (result is SplayTreeSet) { |
| result = new _DelegatingObservableSet<E>(new SplayTreeSet<E>()); |
| } else { |
| result = new _DelegatingObservableSet<E>(new HashSet<E>()); |
| } |
| return result; |
| } |
| |
| /// Create a new observable set using [set] as a backing store. |
| factory ObservableSet.delegate(Set<E> set) = _DelegatingObservableSet<E>; |
| |
| /// Creates a new observable set that contains all elements in [other]. |
| /// |
| /// It will attempt to use the same backing set type if the other set is |
| /// either a [LinkedHashSet], [SplayTreeSet], or [HashSet]. Otherwise it will |
| /// fall back to using a [HashSet]. |
| factory ObservableSet.from(Iterable<E> other) { |
| return new ObservableSet<E>.createFromType(other)..addAll(other); |
| } |
| |
| /// Creates a new observable map using a [LinkedHashSet]. |
| factory ObservableSet.linked() { |
| return new _DelegatingObservableSet<E>(new LinkedHashSet<E>()); |
| } |
| |
| /// Creates a new observable map using a [SplayTreeSet]. |
| factory ObservableSet.sorted() { |
| return new _DelegatingObservableSet<E>(new SplayTreeSet<E>()); |
| } |
| |
| /// Create a new unmodifiable set from [set]. |
| /// |
| /// [ObservableSet.changes] always returns an empty stream, and mutating or |
| /// adding change records throws an [UnsupportedError]. |
| factory ObservableSet.unmodifiable(Set<E> set) { |
| if (set is! UnmodifiableSetView<E>) { |
| set = new UnmodifiableSetView<E>(set); |
| } |
| return new _UnmodifiableObservableSet(set); |
| } |
| } |
| |
| class _DelegatingObservableSet<E> extends DelegatingSet<E> |
| with ChangeNotifier<SetChangeRecord<E>> |
| implements ObservableSet<E> { |
| _DelegatingObservableSet(Set<E> set) : super(set); |
| |
| @override |
| bool add(E value) { |
| if (super.add(value)) { |
| if (hasObservers) { |
| notifyChange(new SetChangeRecord<E>.add(value)); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| @override |
| void addAll(Iterable<E> values) { |
| values.forEach(add); |
| } |
| |
| @override |
| bool remove(Object value) { |
| if (super.remove(value)) { |
| if (hasObservers) { |
| notifyChange(new SetChangeRecord<E>.remove(value as E)); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| @override |
| void removeAll(Iterable<Object> values) { |
| values.toList().forEach(remove); |
| } |
| |
| @override |
| void removeWhere(bool test(E value)) { |
| removeAll(super.where(test)); |
| } |
| |
| @override |
| void retainAll(Iterable<Object> elements) { |
| retainWhere(elements.toSet().contains); |
| } |
| |
| @override |
| void retainWhere(bool test(E element)) { |
| removeWhere((e) => !test(e)); |
| } |
| } |
| |
| class _UnmodifiableEmptySet<E> extends IterableBase<E> implements Set<E> { |
| const _UnmodifiableEmptySet(); |
| |
| @override |
| bool add(E value) => false; |
| |
| @override |
| void addAll(Iterable<E> elements) {} |
| |
| @override |
| void clear() {} |
| |
| @override |
| bool containsAll(Iterable<Object> other) => other.isEmpty; |
| |
| @override |
| Set<E> difference(Set<Object> other) => other.toSet(); |
| |
| @override |
| Set<E> intersection(Set<Object> other) => this; |
| |
| @override |
| Iterator<E> get iterator => const <E>[].iterator; |
| |
| @override |
| E lookup(Object object) => null; |
| |
| @override |
| bool remove(Object value) => false; |
| |
| @override |
| void removeAll(Iterable<Object> elements) {} |
| |
| @override |
| void removeWhere(bool test(E element)) {} |
| |
| @override |
| void retainAll(Iterable<Object> elements) {} |
| |
| @override |
| void retainWhere(bool test(E element)) {} |
| |
| @override |
| Set<E> union(Set<E> other) => other.toSet(); |
| } |
| |
| class _UnmodifiableObservableSet<E> extends DelegatingSet<E> |
| implements ObservableSet<E> { |
| const _UnmodifiableObservableSet(Set<E> set) : super(set); |
| |
| @override |
| Stream<List<SetChangeRecord<E>>> get changes => const Stream.empty(); |
| |
| @override |
| bool deliverChanges() => false; |
| |
| // TODO: implement hasObservers |
| @override |
| final bool hasObservers = false; |
| |
| @override |
| void notifyChange([ChangeRecord change]) { |
| throw new UnsupportedError('Not modifiable'); |
| } |
| |
| @override |
| /*=T*/ notifyPropertyChange/*<T>*/( |
| Symbol field, |
| /*=T*/ |
| oldValue, |
| /*=T*/ |
| newValue, |
| ) { |
| throw new UnsupportedError('Not modifiable'); |
| } |
| |
| @override |
| void observed() {} |
| |
| @override |
| void unobserved() {} |
| } |