Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: sdk/lib/mdv_observe_impl/mdv_observe_impl.dart

Issue 17552019: Reorganize mdv and observe packages (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: merged Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/html/dartium/html_dartium.dart ('k') | sdk/lib/mdv_observe_impl/path_observer.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 // This library itself is undocumented and not supported for end use.
6 // Because dart:html must use some of this functionality, it has to be available
7 // via a dart:* library. The public APIs are reexported via package:mdv_observe.
8 // Generally we try to keep this library minimal, with utility types and
9 // functions in the package.
10 library dart.mdv_observe_impl;
11
12 import 'dart:async';
13 import 'dart:collection';
14
15 part 'path_observer.dart';
16
17 /**
18 * Interface representing an observable object. This is used by data in
19 * model-view architectures to notify interested parties of [changes].
20 *
21 * This object does not require any specific technique to implement
22 * observability.
23 *
24 * You can use [ObservableMixin] as a base class or mixin to implement this.
25 */
26 abstract class Observable {
27 /**
28 * The stream of change records to this object.
29 *
30 * Changes should be delivered in asynchronous batches by calling
31 * [queueChangeRecords].
32 *
33 * [deliverChangeRecords] can be called to force delivery.
34 */
35 Stream<List<ChangeRecord>> get changes;
36
37 // TODO(jmesserly): remove these ASAP.
38 /**
39 * *Warning*: this method is temporary until dart2js supports mirrors.
40 * Gets the value of a field or index. This should return null if it was
41 * not found.
42 */
43 getValueWorkaround(key);
44
45 /**
46 * *Warning*: this method is temporary until dart2js supports mirrors.
47 * Sets the value of a field or index. This should have no effect if the field
48 * was not found.
49 */
50 void setValueWorkaround(key, Object value);
51 }
52
53 /**
54 * Base class implementing [Observable].
55 *
56 * When a field, property, or indexable item is changed, a derived class should
57 * call [notifyPropertyChange]. See that method for an example.
58 */
59 typedef ObservableBase = Object with ObservableMixin;
60
61 /**
62 * Mixin for implementing [Observable] objects.
63 *
64 * When a field, property, or indexable item is changed, a derived class should
65 * call [notifyPropertyChange]. See that method for an example.
66 */
67 abstract class ObservableMixin implements Observable {
68 StreamController _broadcastController;
69 List<ChangeRecord> _changes;
70
71 Stream<List<ChangeRecord>> get changes {
72 if (_broadcastController == null) {
73 _broadcastController =
74 new StreamController<List<ChangeRecord>>.broadcast(sync: true);
75 }
76 return _broadcastController.stream;
77 }
78
79 void _deliverChanges() {
80 var changes = _changes;
81 _changes = null;
82 if (hasObservers && changes != null) {
83 // TODO(jmesserly): make "changes" immutable
84 _broadcastController.add(changes);
85 }
86 }
87
88 /**
89 * True if this object has any observers, and should call
90 * [notifyPropertyChange] for changes.
91 */
92 bool get hasObservers => _broadcastController != null &&
93 _broadcastController.hasListener;
94
95 /**
96 * Notify that the field [name] of this object has been changed.
97 *
98 * The [oldValue] and [newValue] are also recorded. If the two values are
99 * identical, no change will be recorded.
100 *
101 * For convenience this returns [newValue]. This makes it easy to use in a
102 * setter:
103 *
104 * var _myField;
105 * get myField => _myField;
106 * set myField(value) {
107 * _myField = notifyPropertyChange(
108 * const Symbol('myField'), _myField, value);
109 * }
110 */
111 // TODO(jmesserly): should this be == instead of identical, to prevent
112 // spurious loops?
113 notifyPropertyChange(Symbol field, Object oldValue, Object newValue) {
114 if (hasObservers && !identical(oldValue, newValue)) {
115 notifyChange(new PropertyChangeRecord(field));
116 }
117 return newValue;
118 }
119
120 /**
121 * Notify observers of a change. For most objects [notifyPropertyChange] is
122 * more convenient, but collections sometimes deliver other types of changes
123 * such as a [ListChangeRecord].
124 */
125 void notifyChange(ChangeRecord record) {
126 if (!hasObservers) return;
127
128 if (_changes == null) {
129 _changes = [];
130 queueChangeRecords(_deliverChanges);
131 }
132 _changes.add(record);
133 }
134 }
135
136
137 /** Records a change to an [Observable]. */
138 abstract class ChangeRecord {
139 /** True if the change affected the given item, otherwise false. */
140 bool change(key);
141 }
142
143 /** A change record to a field of an observable object. */
144 class PropertyChangeRecord extends ChangeRecord {
145 /** The field that was changed. */
146 final Symbol field;
147
148 PropertyChangeRecord(this.field);
149
150 bool changes(key) => key is Symbol && field == key;
151
152 String toString() => '#<PropertyChangeRecord $field>';
153 }
154
155 /** A change record for an observable list. */
156 class ListChangeRecord extends ChangeRecord {
157 /** The starting index of the change. */
158 final int index;
159
160 /** The number of items removed. */
161 final int removedCount;
162
163 /** The number of items added. */
164 final int addedCount;
165
166 ListChangeRecord(this.index, {this.removedCount: 0, this.addedCount: 0}) {
167 if (addedCount == 0 && removedCount == 0) {
168 throw new ArgumentError('added and removed counts should not both be '
169 'zero. Use 1 if this was a single item update.');
170 }
171 }
172
173 /** Returns true if the provided index was changed by this operation. */
174 bool changes(key) {
175 // If key isn't an int, or before the index, then it wasn't changed.
176 if (key is! int || key < index) return false;
177
178 // If this was a shift operation, anything after index is changed.
179 if (addedCount != removedCount) return true;
180
181 // Otherwise, anything in the update range was changed.
182 return key < index + addedCount;
183 }
184
185 String toString() => '#<ListChangeRecord index: $index, '
186 'removed: $removedCount, addedCount: $addedCount>';
187 }
188
189 /**
190 * Synchronously deliver [Observable.changes] for all observables.
191 * If new changes are added as a result of delivery, this will keep running
192 * until all pending change records are delivered.
193 */
194 // TODO(jmesserly): this is a bit different from the ES Harmony version, which
195 // allows delivery of changes to a particular observer:
196 // http://wiki.ecmascript.org/doku.php?id=harmony:observe#object.deliverchangere cords
197 // However the binding system needs delivery of everything, along the lines of:
198 // https://github.com/toolkitchen/mdv/blob/stable/src/model.js#L19
199 // https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js#L590
200 // TODO(jmesserly): in the future, we can use this to trigger dirty checking.
201 void deliverChangeRecords() {
202 if (_deliverCallbacks == null) return;
203
204 while (!_deliverCallbacks.isEmpty) {
205 var deliver = _deliverCallbacks.removeFirst();
206
207 try {
208 deliver();
209 } catch (e, s) {
210 // Schedule the error to be top-leveled later.
211 new Completer().completeError(e, s);
212 }
213 }
214
215 // Null it out, so [queueChangeRecords] will reschedule this method.
216 _deliverCallbacks = null;
217 }
218
219 /** Queues an action to happen during the [deliverChangeRecords] timeslice. */
220 void queueChangeRecords(void deliverChanges()) {
221 if (_deliverCallbacks == null) {
222 _deliverCallbacks = new Queue<Function>();
223 runAsync(deliverChangeRecords);
224 }
225 _deliverCallbacks.add(deliverChanges);
226 }
227
228 Queue _deliverCallbacks;
OLDNEW
« no previous file with comments | « sdk/lib/html/dartium/html_dartium.dart ('k') | sdk/lib/mdv_observe_impl/path_observer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698