| OLD | NEW |
| 1 library change_detection; | 1 library change_detection; |
| 2 | 2 |
| 3 typedef void EvalExceptionHandler(error, stack); | 3 typedef EvalExceptionHandler(error, stack); |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * An interface for [ChangeDetectorGroup] groups related watches together. It | 6 * An interface for [ChangeDetectorGroup] groups related watches together. It |
| 7 * guarantees that within the group all watches will be reported in the order in | 7 * guarantees that within the group all watches will be reported in the order in |
| 8 * which they were registered. It also provides an efficient way of removing the | 8 * which they were registered. It also provides an efficient way of removing the |
| 9 * watch group. | 9 * watch group. |
| 10 */ | 10 */ |
| 11 abstract class ChangeDetectorGroup<H> { | 11 abstract class ChangeDetectorGroup<H> { |
| 12 /** | 12 /** |
| 13 * Watch a specific [field] on an [object]. | 13 * Watch a specific [field] on an [object]. |
| 14 * | 14 * |
| 15 * If the [field] is: | 15 * If the [field] is: |
| 16 * * _name_ - Name of the property to watch. (If the [object] is a Map then | 16 * - _name_ - Name of the property to watch. (If the [object] is a Map then |
| 17 * treat the name as a key.) | 17 * treat the name as a key.) |
| 18 * * _null_ - Watch all the items for arrays and maps otherwise the object | 18 * - _[]_ - Watch all items in an array. |
| 19 * identity. | 19 * - _{}_ - Watch all items in a Map. |
| 20 * - _._ - Watch the actual object identity. |
| 21 * |
| 20 * | 22 * |
| 21 * Parameters: | 23 * Parameters: |
| 22 * * [object] to watch. | 24 * - [object] to watch. |
| 23 * * [field] to watch on the [object]. | 25 * - [field] to watch on the [object]. |
| 24 * * [handler] an opaque object passed on to [Record]. | 26 * - [handler] an opaque object passed on to [ChangeRecord]. |
| 25 */ | 27 */ |
| 26 WatchRecord<H> watch(Object object, String field, H handler); | 28 WatchRecord<H> watch(Object object, String field, H handler); |
| 27 | 29 |
| 28 /// Remove all the watches in an efficient manner. | 30 /** Use to remove all watches in the group in an efficient manner. */ |
| 29 void remove(); | 31 void remove(); |
| 30 | 32 |
| 31 /// Create a child [ChangeDetectorGroup] | 33 /** Create a child [ChangeDetectorGroup] */ |
| 32 ChangeDetectorGroup<H> newGroup(); | 34 ChangeDetectorGroup<H> newGroup(); |
| 33 } | 35 } |
| 34 | 36 |
| 35 /** | 37 /** |
| 36 * An interface for [ChangeDetector]. An application can have multiple instances | 38 * An interface for [ChangeDetector]. An application can have multiple instances |
| 37 * of the [ChangeDetector] to be used for checking different application | 39 * of the [ChangeDetector] to be used for checking different application domains
. |
| 38 * domains. | |
| 39 * | 40 * |
| 40 * [ChangeDetector] works by comparing the identity of the objects not by | 41 * [ChangeDetector] works by comparing the identity of the objects not by |
| 41 * calling the `.equals()` method. This is because ChangeDetector needs to have | 42 * calling the `.equals()` method. This is because ChangeDetector needs to have |
| 42 * predictable performance, and the developer can implement `.equals()` on top | 43 * predictable performance, and the developer can implement `.equals()` on top |
| 43 * of identity checks. | 44 * of identity checks. |
| 44 * | 45 * |
| 45 * [H] A [Record] has associated handler object. The handler object is opaque | 46 * - [H] A [ChangeRecord] has associated handler object. The handler object is |
| 46 * to the [ChangeDetector] but it is meaningful to the code which registered the | 47 * opaque to the [ChangeDetector] but it is meaningful to the code which |
| 47 * watcher. It can be a data structure, an object, or a function. It is up to | 48 * registered the watcher. It can be a data structure, an object, or a function. |
| 48 * the developer to attach meaning to it. | 49 * It is up to the developer to attach meaning to it. |
| 49 */ | 50 */ |
| 50 abstract class ChangeDetector<H> extends ChangeDetectorGroup<H> { | 51 abstract class ChangeDetector<H> extends ChangeDetectorGroup<H> { |
| 51 /** | 52 /** |
| 52 * This method does the work of collecting the changes and returns them as a | 53 * This method does the work of collecting the changes and returns them as a |
| 53 * linked list of [Record]s. The [Record]s are returned in the | 54 * linked list of [ChangeRecord]s. The [ChangeRecord]s are returned in the |
| 54 * same order as they were registered. | 55 * same order as they were registered. |
| 55 */ | 56 */ |
| 56 Iterator<Record<H>> collectChanges({EvalExceptionHandler exceptionHandler, | 57 ChangeRecord<H> collectChanges({ EvalExceptionHandler exceptionHandler, |
| 57 AvgStopwatch stopwatch }); | 58 AvgStopwatch stopwatch }); |
| 58 } | 59 } |
| 59 | 60 |
| 60 abstract class Record<H> { | 61 abstract class Record<H> { |
| 61 /** The observed object. */ | 62 /** The observed object. */ |
| 62 Object get object; | 63 Object get object; |
| 63 | 64 |
| 64 /** | 65 /** |
| 65 * The field which is being watched: | 66 * The field which is being watched: |
| 66 * * _name_ - Name of the field to watch. | 67 * - _name_ - Name of the field to watch. |
| 67 * * _null_ - Watch all the items for arrays and maps otherwise the object | 68 * - _[]_ - Watch all items in an array. |
| 68 * identity. | 69 * - _{}_ - Watch all items in a Map. |
| 70 * - _._ - Watch the actual object identity. |
| 69 */ | 71 */ |
| 70 String get field; | 72 String get field; |
| 71 | 73 |
| 72 /** | 74 /** |
| 73 * An application provided object which contains the specific logic which | 75 * An application provided object which contains the specific logic which |
| 74 * needs to be applied when the change is detected. The handler is opaque to | 76 * needs to be applied when the change is detected. The handler is opaque to |
| 75 * the ChangeDetector and as such can be anything the application desires. | 77 * the ChangeDetector and as such can be anything the application desires. |
| 76 */ | 78 */ |
| 77 H get handler; | 79 H get handler; |
| 78 | 80 |
| 79 /** | 81 /** Current value of the [field] on the [object] */ |
| 80 * * The current value of the [field] on the [object], | |
| 81 * * a [CollectionChangeRecord] if an iterable is observed, | |
| 82 * * a [MapChangeRecord] if a map is observed. | |
| 83 */ | |
| 84 get currentValue; | 82 get currentValue; |
| 85 /** | 83 /** Previous value of the [field] on the [object] */ |
| 86 * * Previous value of the [field] on the [object], | |
| 87 * * [:null:] when an iterable or a map are observed. | |
| 88 */ | |
| 89 get previousValue; | 84 get previousValue; |
| 90 } | 85 } |
| 91 | 86 |
| 92 /** | 87 /** |
| 93 * [WatchRecord] API which allows changing what object is being watched and | 88 * [WatchRecord] API which allows changing what object is being watched and |
| 94 * manually triggering the checking. | 89 * manually triggering the checking. |
| 95 */ | 90 */ |
| 96 abstract class WatchRecord<H> extends Record<H> { | 91 abstract class WatchRecord<H> extends Record<H> { |
| 97 /// Set a new object for checking | 92 /** Set a new object for checking */ |
| 98 set object(value); | 93 set object(value); |
| 99 | 94 |
| 100 /// Returns [:true:] when changes have been detected | 95 /** |
| 101 bool check(); | 96 * Check to see if the field on the object has changed. Returns [null] if no |
| 97 * change, or a [ChangeRecord] if a change has been detected. |
| 98 */ |
| 99 ChangeRecord<H> check(); |
| 102 | 100 |
| 103 void remove(); | 101 void remove(); |
| 104 } | 102 } |
| 105 | 103 |
| 106 /** | 104 /** |
| 105 * Provides information about the changes which were detected in objects. |
| 106 * |
| 107 * It exposes a `nextChange` method for traversing all of the changes. |
| 108 */ |
| 109 abstract class ChangeRecord<H> extends Record<H> { |
| 110 /** Next [ChangeRecord] */ |
| 111 ChangeRecord<H> get nextChange; |
| 112 } |
| 113 |
| 114 /** |
| 107 * If the [ChangeDetector] is watching a [Map] then the [currentValue] of | 115 * If the [ChangeDetector] is watching a [Map] then the [currentValue] of |
| 108 * [Record] will contain an instance of [MapChangeRecord]. A [MapChangeRecord] | 116 * [Record] will contain an instance of this object. A [MapChangeRecord] |
| 109 * contains the changes to the map since the last execution. The changes are | 117 * contains the changes to the map since the last execution. The changes are |
| 110 * reported as a list of [MapKeyValue]s which contain the key as well as its | 118 * reported as a list of [MapKeyValue]s which contain the key as well as its |
| 111 * current and previous value. | 119 * current and previous value. |
| 112 */ | 120 */ |
| 113 abstract class MapChangeRecord<K, V> { | 121 abstract class MapChangeRecord<K, V> { |
| 114 /// The underlying map object | 122 /// The underlying iterable object |
| 115 Map get map; | 123 Map get map; |
| 116 | 124 |
| 117 void forEachItem(void f(MapKeyValue<K, V> item)); | 125 /// A list of [CollectionKeyValue]s which are in the iteration order. */ |
| 118 void forEachPreviousItem(void f(MapKeyValue<K, V> previousItem)); | 126 KeyValue<K, V> get mapHead; |
| 119 void forEachChange(void f(MapKeyValue<K, V> change)); | 127 /// A list of changed items. |
| 120 void forEachAddition(void f(MapKeyValue<K, V> addition)); | 128 ChangedKeyValue<K, V> get changesHead; |
| 121 void forEachRemoval(void f(MapKeyValue<K, V> removal)); | 129 /// A list of new added items. |
| 130 AddedKeyValue<K, V> get additionsHead; |
| 131 /// A list of removed items |
| 132 RemovedKeyValue<K, V> get removalsHead; |
| 133 |
| 134 void forEachChange(void f(ChangedKeyValue<K, V> change)); |
| 135 void forEachAddition(void f(AddedKeyValue<K, V> addition)); |
| 136 void forEachRemoval(void f(RemovedKeyValue<K, V> removal)); |
| 122 } | 137 } |
| 123 | 138 |
| 124 /** | 139 /** |
| 125 * Each item in map is wrapped in [MapKeyValue], which can track | 140 * Each item in map is wrapped in [MapKeyValue], which can track |
| 126 * the [item]s [currentValue] and [previousValue] location. | 141 * the [item]s [currentValue] and [previousValue] location. |
| 127 */ | 142 */ |
| 128 abstract class MapKeyValue<K, V> { | 143 abstract class MapKeyValue<K, V> { |
| 129 /// The item. | 144 /// The item. |
| 130 K get key; | 145 K get key; |
| 131 | 146 |
| 132 /// Previous item location in the list or [null] if addition. | 147 /// Previous item location in the list or [null] if addition. |
| 133 V get previousValue; | 148 V get previousValue; |
| 134 | 149 |
| 135 /// Current item location in the list or [null] if removal. | 150 /// Current item location in the list or [null] if removal. |
| 136 V get currentValue; | 151 V get currentValue; |
| 137 } | 152 } |
| 138 | 153 |
| 154 abstract class KeyValue<K, V> extends MapKeyValue<K, V> { |
| 155 KeyValue<K, V> get nextKeyValue; |
| 156 } |
| 157 |
| 158 abstract class AddedKeyValue<K, V> extends MapKeyValue<K, V> { |
| 159 AddedKeyValue<K, V> get nextAddedKeyValue; |
| 160 } |
| 161 |
| 162 abstract class RemovedKeyValue<K, V> extends MapKeyValue<K, V> { |
| 163 RemovedKeyValue<K, V> get nextRemovedKeyValue; |
| 164 } |
| 165 |
| 166 abstract class ChangedKeyValue<K, V> extends MapKeyValue<K, V> { |
| 167 ChangedKeyValue<K, V> get nextChangedKeyValue; |
| 168 } |
| 169 |
| 170 |
| 139 /** | 171 /** |
| 140 * If the [ChangeDetector] is watching an [Iterable] then the [currentValue] of | 172 * If the [ChangeDetector] is watching an [Iterable] then the [currentValue] of |
| 141 * [Record] will contain an instance of [CollectionChangeRecord]. The | 173 * [Record] will contain this object. The [CollectionChangeRecord] contains the |
| 142 * [CollectionChangeRecord] contains the changes to the collection since the | 174 * changes to the collection since the last execution. The changes are reported |
| 143 * last execution. The changes are reported as a list of [CollectionChangeItem]s | 175 * as a list of [CollectionChangeItem]s which contain the item as well as its |
| 144 * which contain the item as well as its current and previous index. | 176 * current and previous position in the list. |
| 145 */ | 177 */ |
| 146 abstract class CollectionChangeRecord<V> { | 178 abstract class CollectionChangeRecord<V> { |
| 147 /** The underlying iterable object */ | 179 /** The underlying iterable object */ |
| 148 Iterable get iterable; | 180 Iterable get iterable; |
| 149 int get length; | |
| 150 | 181 |
| 151 void forEachItem(void f(CollectionChangeItem<V> item)); | 182 /** A list of [CollectionItem]s which are in the iteration order. */ |
| 152 void forEachPreviousItem(void f(CollectionChangeItem<V> previousItem)); | 183 CollectionItem<V> get collectionHead; |
| 153 void forEachAddition(void f(CollectionChangeItem<V> addition)); | 184 /** A list of new [AddedItem]s. */ |
| 154 void forEachMove(void f(CollectionChangeItem<V> move)); | 185 AddedItem<V> get additionsHead; |
| 155 void forEachRemoval(void f(CollectionChangeItem<V> removal)); | 186 /** A list of [MovedItem]s. */ |
| 187 MovedItem<V> get movesHead; |
| 188 /** A list of [RemovedItem]s. */ |
| 189 RemovedItem<V> get removalsHead; |
| 190 |
| 191 void forEachAddition(void f(AddedItem<V> addition)); |
| 192 void forEachMove(void f(MovedItem<V> move)); |
| 193 void forEachRemoval(void f(RemovedItem<V> removal)); |
| 156 } | 194 } |
| 157 | 195 |
| 158 /** | 196 /** |
| 159 * Each changed item in the collection is wrapped in a [CollectionChangeItem], | 197 * Each changed item in the collection is wrapped in a [CollectionChangeItem], |
| 160 * which tracks the [item]s [currentKey] and [previousKey] location. | 198 * which tracks the [item]s [currentKey] and [previousKey] location. |
| 161 */ | 199 */ |
| 162 abstract class CollectionChangeItem<V> { | 200 abstract class CollectionChangeItem<V> { |
| 163 /** Previous item location in the list or [:null:] if addition. */ | 201 /** Previous item location in the list or [null] if addition. */ |
| 164 int get previousIndex; | 202 int get previousIndex; |
| 165 | 203 |
| 166 /** Current item location in the list or [:null:] if removal. */ | 204 /** Current item location in the list or [null] if removal. */ |
| 167 int get currentIndex; | 205 int get currentIndex; |
| 168 | 206 |
| 169 /** The item. */ | 207 /** The item. */ |
| 170 V get item; | 208 V get item; |
| 171 } | 209 } |
| 172 | 210 |
| 173 typedef dynamic FieldGetter(object); | 211 /** |
| 174 typedef void FieldSetter(object, value); | 212 * Used to create a linked list of collection items. These items are always in |
| 213 * the iteration order of the collection. |
| 214 */ |
| 215 abstract class CollectionItem<V> extends CollectionChangeItem<V> { |
| 216 CollectionItem<V> get nextCollectionItem; |
| 217 } |
| 175 | 218 |
| 176 abstract class FieldGetterFactory { | 219 /** |
| 177 get isMethodInvoke; | 220 * A linked list of new items added to the collection. These items are always in |
| 178 bool isMethod(Object object, String name); | 221 * the iteration order of the collection. |
| 179 Function method(Object object, String name); | 222 */ |
| 180 FieldGetter getter(Object object, String name); | 223 abstract class AddedItem<V> extends CollectionChangeItem<V> { |
| 224 AddedItem<V> get nextAddedItem; |
| 225 } |
| 226 |
| 227 /** |
| 228 * A linked list of items moved in the collection. These items are always in |
| 229 * the iteration order of the collection. |
| 230 */ |
| 231 abstract class MovedItem<V> extends CollectionChangeItem<V> { |
| 232 MovedItem<V> get nextMovedItem; |
| 233 } |
| 234 |
| 235 /** |
| 236 * A linked list of items removed from the collection. These items are always |
| 237 * in the iteration order of the collection. |
| 238 */ |
| 239 abstract class RemovedItem<V> extends CollectionChangeItem<V> { |
| 240 RemovedItem<V> get nextRemovedItem; |
| 181 } | 241 } |
| 182 | 242 |
| 183 class AvgStopwatch extends Stopwatch { | 243 class AvgStopwatch extends Stopwatch { |
| 184 int _count = 0; | 244 int _count = 0; |
| 185 | 245 |
| 186 int get count => _count; | 246 int get count => _count; |
| 187 | 247 |
| 188 void reset() { | 248 void reset() { |
| 189 _count = 0; | 249 _count = 0; |
| 190 super.reset(); | 250 super.reset(); |
| 191 } | 251 } |
| 192 | 252 |
| 193 int increment(int count) => _count += count; | 253 int increment(int count) => _count += count; |
| 194 | 254 |
| 195 double get ratePerMs => elapsedMicroseconds == 0 | 255 double get ratePerMs => elapsedMicroseconds == 0 |
| 196 ? 0.0 | 256 ? 0.0 |
| 197 : _count / elapsedMicroseconds * 1000; | 257 : _count / elapsedMicroseconds * 1000; |
| 198 } | 258 } |
| OLD | NEW |