-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c9b6340
commit 2376aaf
Showing
9 changed files
with
196 additions
and
254 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export 'collections/observable_list.dart'; | ||
export 'collections/observable_map.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
import 'dart:async'; | ||
import 'dart:collection'; | ||
|
||
import 'package:collection/collection.dart'; | ||
import 'package:observable/observable.dart'; | ||
|
||
/// A [Map] 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 updated key-value). You may | ||
/// accept an observable map to be notified of mutations: | ||
/// ``` | ||
/// set grades(Map<String, int> grades) { | ||
/// buildBook(grades); | ||
/// if (names is ObservableMap<String>, int) { | ||
/// grades.changes.listen(updateBook); | ||
/// } | ||
/// } | ||
/// ``` | ||
/// | ||
/// *See [MapDiffer] to manually diff two lists instead* | ||
abstract class ObservableMap<K, V> implements Map<K, V>, Observable { | ||
/// Creates a new observable map. | ||
factory ObservableMap() { | ||
return new _ObservableDelegatingMap(new HashMap<K, V>()); | ||
} | ||
|
||
/// Like [ObservableMap.from], but creates an empty type. | ||
factory ObservableMap.createFromType(Map<K, V> other) { | ||
ObservableMap<K, V> result; | ||
if (other is LinkedHashMap) { | ||
result = new ObservableMap<K, V>.linked(); | ||
} else if (other is SplayTreeMap) { | ||
result = new ObservableMap<K, V>.sorted(); | ||
} else { | ||
result = new ObservableMap<K, V>(); | ||
} | ||
return result; | ||
} | ||
|
||
/// Create a new observable map using [map] as a backing store. | ||
factory ObservableMap.delegate(Map<K, V> map) { | ||
return new _ObservableDelegatingMap<K, V>(map); | ||
} | ||
|
||
/// Creates a new observable map that contains all entries in [other]. | ||
/// | ||
/// It will attempt to use the same backing map type if the other map is | ||
/// either a [LinkedHashMap], [SplayTreeMap], or [HashMap]. Otherwise it will | ||
/// fall back to using a [HashMap]. | ||
factory ObservableMap.from(Map<K, V> other) { | ||
return new ObservableMap<K, V>.createFromType(other)..addAll(other); | ||
} | ||
|
||
/// Creates a new observable map using a [LinkedHashMap]. | ||
factory ObservableMap.linked() { | ||
return new _ObservableDelegatingMap<K, V>(new LinkedHashMap<K, V>()); | ||
} | ||
|
||
/// Creates a new observable map using a [SplayTreeMap]. | ||
factory ObservableMap.sorted() { | ||
return new _ObservableDelegatingMap<K, V>(new SplayTreeMap<K, V>()); | ||
} | ||
|
||
/// Creates a new observable map wrapping [other]. | ||
@Deprecated('Use ObservableMap.delegate for API consistency') | ||
factory ObservableMap.spy(Map<K, V> other) = ObservableMap<K, V>.delegate; | ||
} | ||
|
||
class _ObservableDelegatingMap<K, V> extends DelegatingMap<K, V> | ||
implements ObservableMap<K, V> { | ||
final _allChanges = new ChangeNotifier(); | ||
|
||
_ObservableDelegatingMap(Map<K, V> map) : super(map); | ||
|
||
// Observable | ||
|
||
@override | ||
Stream<List<ChangeRecord>> get changes => _allChanges.changes; | ||
|
||
// ChangeNotifier (deprecated for ObservableMap) | ||
|
||
@override | ||
bool deliverChanges() => _allChanges.deliverChanges(); | ||
|
||
@override | ||
bool get hasObservers => _allChanges.hasObservers; | ||
|
||
@override | ||
void notifyChange([ChangeRecord change]) { | ||
_allChanges.notifyChange(change); | ||
} | ||
|
||
@override | ||
/*=T*/ notifyPropertyChange/*<T>*/( | ||
Symbol field, | ||
/*=T*/ | ||
oldValue, | ||
/*=T*/ | ||
newValue, | ||
) { | ||
if (oldValue != newValue) { | ||
_allChanges.notifyChange( | ||
new PropertyChangeRecord/*<T>*/(this, field, oldValue, newValue), | ||
); | ||
} | ||
return newValue; | ||
} | ||
|
||
@override | ||
void observed() {} | ||
|
||
@override | ||
void unobserved() {} | ||
|
||
@override | ||
operator []=(K key, V newValue) { | ||
if (!hasObservers) { | ||
super[key] = newValue; | ||
return; | ||
} | ||
|
||
final oldLength = super.length; | ||
V oldValue = super[key]; | ||
super[key] = newValue; | ||
|
||
if (oldLength != length) { | ||
notifyPropertyChange(#length, oldLength, length); | ||
notifyChange(new MapChangeRecord<K, V>.insert(key, newValue)); | ||
} else { | ||
notifyChange(new MapChangeRecord<K, V>(key, oldValue, newValue)); | ||
} | ||
} | ||
|
||
@override | ||
void addAll(Map<K, V> other) { | ||
if (!hasObservers) { | ||
super.addAll(other); | ||
return; | ||
} | ||
|
||
other.forEach((k, v) => this[k] = v); | ||
} | ||
|
||
@override | ||
V putIfAbsent(K key, V ifAbsent()) { | ||
final oldLength = length; | ||
final result = super.putIfAbsent(key, ifAbsent); | ||
if (hasObservers && oldLength != length) { | ||
notifyPropertyChange(#length, oldLength, length); | ||
notifyChange(new MapChangeRecord<K, V>.insert(key, result)); | ||
} | ||
return result; | ||
} | ||
|
||
@override | ||
V remove(Object key) { | ||
final oldLength = length; | ||
final result = super.remove(key); | ||
if (hasObservers && oldLength != length) { | ||
notifyChange(new MapChangeRecord<K, V>.remove(key as K, result)); | ||
notifyPropertyChange(#length, oldLength, length); | ||
} | ||
return result; | ||
} | ||
|
||
@override | ||
void clear() { | ||
if (!hasObservers || isEmpty) { | ||
super.clear(); | ||
return; | ||
} | ||
final oldLength = length; | ||
forEach((k, v) { | ||
notifyChange(new MapChangeRecord<K, V>.remove(k, v)); | ||
}); | ||
notifyPropertyChange(#length, oldLength, 0); | ||
super.clear(); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.