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 |