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