| OLD | NEW |
| (Empty) | |
| 1 library change_detection; |
| 2 |
| 3 typedef EvalExceptionHandler(error, stack); |
| 4 |
| 5 /** |
| 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 |
| 8 * which they were registered. It also provides an efficient way of removing the |
| 9 * watch group. |
| 10 */ |
| 11 abstract class ChangeDetectorGroup<H> { |
| 12 /** |
| 13 * Watch a specific [field] on an [object]. |
| 14 * |
| 15 * If the [field] is: |
| 16 * - _name_ - Name of the property to watch. (If the [object] is a Map then |
| 17 * treat the name as a key.) |
| 18 * - _[]_ - Watch all items in an array. |
| 19 * - _{}_ - Watch all items in a Map. |
| 20 * - _._ - Watch the actual object identity. |
| 21 * |
| 22 * |
| 23 * Parameters: |
| 24 * - [object] to watch. |
| 25 * - [field] to watch on the [object]. |
| 26 * - [handler] an opaque object passed on to [ChangeRecord]. |
| 27 */ |
| 28 WatchRecord<H> watch(Object object, String field, H handler); |
| 29 |
| 30 /** Use to remove all watches in the group in an efficient manner. */ |
| 31 void remove(); |
| 32 |
| 33 /** Create a child [ChangeDetectorGroup] */ |
| 34 ChangeDetectorGroup<H> newGroup(); |
| 35 } |
| 36 |
| 37 /** |
| 38 * An interface for [ChangeDetector]. An application can have multiple instances |
| 39 * of the [ChangeDetector] to be used for checking different application domains
. |
| 40 * |
| 41 * [ChangeDetector] works by comparing the identity of the objects not by |
| 42 * calling the `.equals()` method. This is because ChangeDetector needs to have |
| 43 * predictable performance, and the developer can implement `.equals()` on top |
| 44 * of identity checks. |
| 45 * |
| 46 * - [H] A [ChangeRecord] has associated handler object. The handler object is |
| 47 * opaque to the [ChangeDetector] but it is meaningful to the code which |
| 48 * registered the watcher. It can be a data structure, an object, or a function. |
| 49 * It is up to the developer to attach meaning to it. |
| 50 */ |
| 51 abstract class ChangeDetector<H> extends ChangeDetectorGroup<H> { |
| 52 /** |
| 53 * 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 |
| 55 * same order as they were registered. |
| 56 */ |
| 57 ChangeRecord<H> collectChanges({ EvalExceptionHandler exceptionHandler, |
| 58 AvgStopwatch stopwatch }); |
| 59 } |
| 60 |
| 61 abstract class Record<H> { |
| 62 /** The observed object. */ |
| 63 Object get object; |
| 64 |
| 65 /** |
| 66 * The field which is being watched: |
| 67 * - _name_ - Name of the field to watch. |
| 68 * - _[]_ - Watch all items in an array. |
| 69 * - _{}_ - Watch all items in a Map. |
| 70 * - _._ - Watch the actual object identity. |
| 71 */ |
| 72 String get field; |
| 73 |
| 74 /** |
| 75 * 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 |
| 77 * the ChangeDetector and as such can be anything the application desires. |
| 78 */ |
| 79 H get handler; |
| 80 |
| 81 /** Current value of the [field] on the [object] */ |
| 82 get currentValue; |
| 83 /** Previous value of the [field] on the [object] */ |
| 84 get previousValue; |
| 85 } |
| 86 |
| 87 /** |
| 88 * [WatchRecord] API which allows changing what object is being watched and |
| 89 * manually triggering the checking. |
| 90 */ |
| 91 abstract class WatchRecord<H> extends Record<H> { |
| 92 /** Set a new object for checking */ |
| 93 set object(value); |
| 94 |
| 95 /** |
| 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(); |
| 100 |
| 101 void remove(); |
| 102 } |
| 103 |
| 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 /** |
| 115 * If the [ChangeDetector] is watching a [Map] then the [currentValue] of |
| 116 * [Record] will contain an instance of this object. A [MapChangeRecord] |
| 117 * 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 |
| 119 * current and previous value. |
| 120 */ |
| 121 abstract class MapChangeRecord<K, V> { |
| 122 /// The underlying iterable object |
| 123 Map get map; |
| 124 |
| 125 /// A list of [CollectionKeyValue]s which are in the iteration order. */ |
| 126 KeyValue<K, V> get mapHead; |
| 127 /// A list of changed items. |
| 128 ChangedKeyValue<K, V> get changesHead; |
| 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)); |
| 137 } |
| 138 |
| 139 /** |
| 140 * Each item in map is wrapped in [MapKeyValue], which can track |
| 141 * the [item]s [currentValue] and [previousValue] location. |
| 142 */ |
| 143 abstract class MapKeyValue<K, V> { |
| 144 /// The item. |
| 145 K get key; |
| 146 |
| 147 /// Previous item location in the list or [null] if addition. |
| 148 V get previousValue; |
| 149 |
| 150 /// Current item location in the list or [null] if removal. |
| 151 V get currentValue; |
| 152 } |
| 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 |
| 171 /** |
| 172 * If the [ChangeDetector] is watching an [Iterable] then the [currentValue] of |
| 173 * [Record] will contain this object. The [CollectionChangeRecord] contains the |
| 174 * changes to the collection since the last execution. The changes are reported |
| 175 * as a list of [CollectionChangeItem]s which contain the item as well as its |
| 176 * current and previous position in the list. |
| 177 */ |
| 178 abstract class CollectionChangeRecord<V> { |
| 179 /** The underlying iterable object */ |
| 180 Iterable get iterable; |
| 181 |
| 182 /** A list of [CollectionItem]s which are in the iteration order. */ |
| 183 CollectionItem<V> get collectionHead; |
| 184 /** A list of new [AddedItem]s. */ |
| 185 AddedItem<V> get additionsHead; |
| 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)); |
| 194 } |
| 195 |
| 196 /** |
| 197 * Each changed item in the collection is wrapped in a [CollectionChangeItem], |
| 198 * which tracks the [item]s [currentKey] and [previousKey] location. |
| 199 */ |
| 200 abstract class CollectionChangeItem<V> { |
| 201 /** Previous item location in the list or [null] if addition. */ |
| 202 int get previousIndex; |
| 203 |
| 204 /** Current item location in the list or [null] if removal. */ |
| 205 int get currentIndex; |
| 206 |
| 207 /** The item. */ |
| 208 V get item; |
| 209 } |
| 210 |
| 211 /** |
| 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 } |
| 218 |
| 219 /** |
| 220 * A linked list of new items added to the collection. These items are always in |
| 221 * the iteration order of the collection. |
| 222 */ |
| 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; |
| 241 } |
| 242 |
| 243 class AvgStopwatch extends Stopwatch { |
| 244 int _count = 0; |
| 245 |
| 246 int get count => _count; |
| 247 |
| 248 void reset() { |
| 249 _count = 0; |
| 250 super.reset(); |
| 251 } |
| 252 |
| 253 int increment(int count) => _count += count; |
| 254 |
| 255 double get ratePerMs => elapsedMicroseconds == 0 |
| 256 ? 0.0 |
| 257 : _count / elapsedMicroseconds * 1000; |
| 258 } |
| OLD | NEW |