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

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

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

Powered by Google App Engine
This is Rietveld 408576698