Index: pkg/observe/lib/src/path_observer.dart |
diff --git a/pkg/observe/lib/src/path_observer.dart b/pkg/observe/lib/src/path_observer.dart |
index c6e0be02bc30869968f14ca8d61dbf16d5144461..1b6e1a34dc1a80b7638bf918a0897b763616e81a 100644 |
--- a/pkg/observe/lib/src/path_observer.dart |
+++ b/pkg/observe/lib/src/path_observer.dart |
@@ -21,7 +21,7 @@ part of observe; |
* |
* This class is used to implement [Node.bind] and similar functionality. |
*/ |
-class PathObserver extends ChangeNotifierBase { |
+class PathObserver extends ChangeNotifier { |
/** The path string. */ |
final String path; |
@@ -119,21 +119,19 @@ class PathObserver extends ChangeNotifierBase { |
} |
void _updateObservedValues([int start = 0]) { |
- bool changed = false; |
+ var oldValue, newValue; |
for (int i = start; i < _segments.length; i++) { |
- final newValue = _getObjectProperty(_values[i], _segments[i]); |
- if (identical(_values[i + 1], newValue)) { |
+ oldValue = _values[i + 1]; |
+ newValue = _getObjectProperty(_values[i], _segments[i]); |
+ if (identical(oldValue, newValue)) { |
_observePath(start, i); |
return; |
} |
_values[i + 1] = newValue; |
- changed = true; |
} |
_observePath(start); |
- if (changed) { |
- notifyChange(new PropertyChangeRecord(#value)); |
- } |
+ notifyPropertyChange(#value, oldValue, newValue); |
} |
void _observePath([int start = 0, int end]) { |
@@ -160,7 +158,7 @@ class PathObserver extends ChangeNotifierBase { |
} |
for (var record in records) { |
- if (record.changes(_segments[i])) { |
+ if (_changeRecordMatches(record, _segments[i])) { |
_updateObservedValues(i); |
return; |
} |
@@ -170,6 +168,20 @@ class PathObserver extends ChangeNotifierBase { |
} |
} |
+bool _changeRecordMatches(record, key) { |
+ if (record is ListChangeRecord) { |
+ return key is int && (record as ListChangeRecord).indexChanged(key); |
+ } |
+ if (record is PropertyChangeRecord) { |
+ return (record as PropertyChangeRecord).name == key; |
+ } |
+ if (record is MapChangeRecord) { |
+ if (key is Symbol) key = MirrorSystem.getName(key); |
+ return (record as MapChangeRecord).key == key; |
+ } |
+ return false; |
+} |
+ |
_getObjectProperty(object, property) { |
if (object == null) { |
return null; |
@@ -185,12 +197,12 @@ _getObjectProperty(object, property) { |
if (property is Symbol) { |
var mirror = reflect(object); |
- try { |
- return mirror.getField(property).reflectee; |
- } catch (e) {} |
+ var result = _tryGetField(mirror, property); |
+ if (result != null) return result.reflectee; |
} |
if (object is Map) { |
+ if (property is Symbol) property = MirrorSystem.getName(property); |
return object[property]; |
} |
@@ -209,13 +221,11 @@ bool _setObjectProperty(object, property, value) { |
if (property is Symbol) { |
var mirror = reflect(object); |
- try { |
- mirror.setField(property, value); |
- return true; |
- } catch (e) {} |
+ if (_trySetField(mirror, property, value)) return true; |
} |
if (object is Map) { |
+ if (property is Symbol) property = MirrorSystem.getName(property); |
object[property] = value; |
return true; |
} |
@@ -223,6 +233,58 @@ bool _setObjectProperty(object, property, value) { |
return false; |
} |
+InstanceMirror _tryGetField(InstanceMirror mirror, Symbol name) { |
+ try { |
+ return mirror.getField(name); |
+ } on NoSuchMethodError catch (e) { |
+ if (_hasMember(mirror, name, (m) => |
+ m is VariableMirror || m is MethodMirror && m.isGetter)) { |
+ // The field/getter is there but threw a NoSuchMethod exception. |
+ // This is a legitimate error in the code so rethrow. |
+ rethrow; |
+ } |
+ // The field isn't there. PathObserver does not treat this as an error. |
+ return null; |
+ } |
+} |
+ |
+bool _trySetField(InstanceMirror mirror, Symbol name, Object value) { |
+ try { |
+ mirror.setField(name, value); |
+ return true; |
+ } on NoSuchMethodError catch (e) { |
+ if (_hasMember(mirror, name, (m) => m is VariableMirror) || |
+ _hasMember(mirror, _setterName(name))) { |
+ // The field/setter is there but threw a NoSuchMethod exception. |
+ // This is a legitimate error in the code so rethrow. |
+ rethrow; |
+ } |
+ // The field isn't there. PathObserver does not treat this as an error. |
+ return false; |
+ } |
+} |
+ |
+// TODO(jmesserly): workaround for: |
+// https://code.google.com/p/dart/issues/detail?id=10029 |
+Symbol _setterName(Symbol getter) => |
+ new Symbol('${MirrorSystem.getName(getter)}='); |
+ |
+bool _hasMember(InstanceMirror mirror, Symbol name, [bool test(member)]) { |
+ var type = mirror.type; |
+ while (type != null) { |
+ final member = type.members[name]; |
+ if (member != null && (test == null || test(member))) return true; |
+ |
+ try { |
+ type = type.superclass; |
+ } on UnsupportedError catch (e) { |
+ // TODO(jmesserly): dart2js throws this error when the type is not |
+ // reflectable. |
+ return false; |
+ } |
+ } |
+ return false; |
+} |
// From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js |