| 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 |