Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(528)

Unified Diff: lib/observe/map.dart

Issue 12225039: Support for observable models, fixes #259 (Closed) Base URL: https://github.com/dart-lang/web-ui.git@master
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/observe/list.dart ('k') | lib/observe/reference.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/observe/map.dart
diff --git a/lib/observe/map.dart b/lib/observe/map.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ede08fc0abaa7cdc0ca6ffe94a2306f28cbb928f
--- /dev/null
+++ b/lib/observe/map.dart
@@ -0,0 +1,221 @@
+// Copyright (c) 2013, 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.
+
+library web_ui.observe.map;
+
+import 'dart:collection';
+import 'package:web_ui/observe.dart';
+import 'list.dart';
+
+typedef Map<K, dynamic> MapFactory<K>();
+
+// TODO(jmesserly): this needs to be faster. We currently require multiple
+// lookups per key to get the old value. Most likely this needs to be based on
+// a modified HashMap source code.
+/**
+ * Represents an observable map of model values. If any items are added,
+ * removed, or replaced, then observers that are registered with
+ * [observe] will be notified.
+ */
+class ObservableMap<K, V> implements Map<K, V> {
+ final Map<K, V> _map;
+ final Map<K, Object> _observeKey;
+ Object _observeLength;
+ _ObservableMapKeyIterable<K, V> _keys;
+ _ObservableMapValueIterable<K, V> _values;
+
+ /**
+ * Creates an observable map, optionally using the provided factory
+ * [createMap] to construct a custom map type.
+ */
+ ObservableMap({MapFactory<K> createMap})
+ : _map = createMap != null ? createMap() : new Map<K, V>(),
+ _observeKey = createMap != null ? createMap() : new Map<K, Object>() {
+ _keys = new _ObservableMapKeyIterable<K, V>(this);
+ _values = new _ObservableMapValueIterable<K, V>(this);
+ }
+
+ /** Creates a new observable map using a [LinkedHashMap]. */
+ ObservableMap.linked() : this(createMap: () => new LinkedHashMap());
+
+ /**
+ * Creates an observable map that contains all key value pairs of [other].
+ */
+ factory ObservableMap.from(Map<K, V> other, {MapFactory<K> createMap}) {
+ var result = new ObservableMap<K, V>(createMap: createMap);
+ other.forEach((K key, V value) { result[key] = value; });
+ return result;
+ }
+
+
+ Iterable<K> get keys => _keys;
+
+ Iterable<V> get values => _values;
+
+ int get length {
+ _notifyReadLength();
+ return _map.length;
+ }
+
+ bool get isEmpty => length == 0;
+
+ void _notifyReadKey(K key) {
+ if (observeReads) _observeKey[key] = notifyRead(_observeKey[key]);
+ }
+
+ void _notifyReadLength() {
+ if (observeReads) _observeLength = notifyRead(_observeLength);
+ }
+
+ void _notifyReadAll() {
+ if (!observeReads) return;
+ _observeLength = notifyRead(_observeLength);
+ for (K key in _map.keys) {
+ _observeKey[key] = notifyRead(_observeKey[key]);
+ }
+ }
+
+ void _notifyWriteLength(int originalLength) {
+ if (_observeLength != null && originalLength != _map.length) {
+ _observeLength = notifyWrite(_observeLength);
+ }
+ }
+
+ void _notifyWriteKey(K key) {
+ var observer = _observeKey.remove(key);
+ if (observer != null) notifyWrite(observer);
+ }
+
+ bool containsValue(V value) {
+ _notifyReadAll();
+ return _map.containsValue(value);
+ }
+
+ bool containsKey(K key) {
+ _notifyReadKey(key);
+ return _map.containsKey(key);
+ }
+
+ V operator [](K key) {
+ _notifyReadKey(key);
+ return _map[key];
+ }
+
+ void operator []=(K key, V value) {
+ int len = _map.length;
+ V oldValue = _map[key];
+ _map[key] = value;
+ // Note: if length changed, it means the key was added, so we need to
+ // _notifyWriteKey. Also _notifyWriteLength will check if length changed.
+ if (len != _map.length || oldValue != value) {
+ _notifyWriteKey(key);
+ _notifyWriteLength(len);
+ }
+ }
+
+ V putIfAbsent(K key, V ifAbsent()) {
+ // notifyRead because result depends on if the key already exists
+ _notifyReadKey(key);
+
+ int len = _map.length;
+ V result = _map.putIfAbsent(key, ifAbsent);
+ // Note: if length changed, it means the key was added, so we need to
+ // _notifyWriteKey. Also _notifyWriteLength will check if length changed.
+ if (len != _map.length) {
+ _notifyWriteKey(key);
+ _notifyWriteLength(len);
+ }
+ return result;
+ }
+
+ V remove(K key) {
+ // notifyRead because result depends on if the key already exists
+ _notifyReadKey(key);
+
+ int len = _map.length;
+ V result = _map.remove(key);
+ if (len != _map.length) {
+ _notifyWriteKey(key);
+ _notifyWriteLength(len);
+ }
+ return result;
+ }
+
+ void clear() {
+ int len = _map.length;
+ _map.clear();
+ _notifyWriteLength(len);
+ _observeKey.values.forEach(notifyWrite);
+ _observeKey.clear();
+ }
+
+ void forEach(void f(K key, V value)) {
+ _notifyReadAll();
+ _map.forEach(f);
+ }
+
+ String toString() => Maps.mapToString(this);
+}
+
+class _ObservableMapKeyIterable<K, V> extends Iterable<K> {
+ final ObservableMap<K, V> _map;
+ _ObservableMapKeyIterable(this._map);
+
+ Iterator<K> get iterator => new _ObservableMapKeyIterator<K, V>(_map);
+}
+
+class _ObservableMapKeyIterator<K, V> implements Iterator<K> {
+ final ObservableMap<K, V> _map;
+ final Iterator<K> _keys;
+
+ _ObservableMapKeyIterator(ObservableMap<K, V> map)
+ : _map = map,
+ _keys = map._map.keys.iterator;
+
+ bool moveNext() {
+ _map._notifyReadLength();
+ return _keys.moveNext();
+ }
+
+ K get current {
+ var key = _keys.current;
+ if (key != null) _map._notifyReadKey(key);
+ return key;
+ }
+}
+
+
+class _ObservableMapValueIterable<K, V> extends Iterable<V> {
+ final ObservableMap<K, V> _map;
+ _ObservableMapValueIterable(this._map);
+
+ Iterator<V> get iterator => new _ObservableMapValueIterator<K, V>(_map);
+}
+
+class _ObservableMapValueIterator<K, V> implements Iterator<V> {
+ final ObservableMap<K, V> _map;
+ final Iterator<K> _keys;
+ final Iterator<V> _values;
+
+ _ObservableMapValueIterator(ObservableMap<K, V> map)
+ : _map = map,
+ _keys = map._map.keys.iterator,
+ _values = map._map.values.iterator;
+
+ bool moveNext() {
+ _map._notifyReadLength();
+ bool moreKeys = _keys.moveNext();
+ bool moreValues = _values.moveNext();
+ if (moreKeys != moreValues) {
+ throw new StateError('keys and values should be the same length');
+ }
+ return moreValues;
+ }
+
+ V get current {
+ var key = _keys.current;
+ if (key != null) _map._notifyReadKey(key);
+ return _values.current;
+ }
+}
« no previous file with comments | « lib/observe/list.dart ('k') | lib/observe/reference.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698