comparison org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableSet.d @ 78:0a55d2d5a946

Added file for databinding
author Frank Benoit <benoit@tionex.de>
date Tue, 14 Apr 2009 11:35:29 +0200
parents
children 6be48cf9f95c
comparison
equal deleted inserted replaced
76:f05e6e8b2f2d 78:0a55d2d5a946
1 /*******************************************************************************
2 * Copyright (c) 2008 Matthew Hall and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Matthew Hall - initial API and implementation (bug 218269)
10 ******************************************************************************/
11
12 module org.eclipse.core.internal.databinding.observable.ValidatedObservableSet;
13
14 import java.lang.all;
15
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.Set;
21
22 import org.eclipse.core.databinding.observable.Diffs;
23 import org.eclipse.core.databinding.observable.IStaleListener;
24 import org.eclipse.core.databinding.observable.StaleEvent;
25 import org.eclipse.core.databinding.observable.set.IObservableSet;
26 import org.eclipse.core.databinding.observable.set.ISetChangeListener;
27 import org.eclipse.core.databinding.observable.set.ObservableSet;
28 import org.eclipse.core.databinding.observable.set.SetChangeEvent;
29 import org.eclipse.core.databinding.observable.set.SetDiff;
30 import org.eclipse.core.databinding.observable.value.IObservableValue;
31 import org.eclipse.core.databinding.observable.value.IValueChangeListener;
32 import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
33 import org.eclipse.core.runtime.Assert;
34 import org.eclipse.core.runtime.IStatus;
35
36 /**
37 * @since 3.3
38 *
39 */
40 public class ValidatedObservableSet : ObservableSet {
41 private IObservableSet target;
42 private IObservableValue validationStatus;
43
44 // Only true when out of sync with target due to validation status
45 private bool stale;
46
47 // True when validation status changes from invalid to valid.
48 private bool computeNextDiff = false;
49
50 private bool updatingTarget = false;
51
52 private ISetChangeListener targetChangeListener = new class() ISetChangeListener {
53 public void handleSetChange(SetChangeEvent event) {
54 if (updatingTarget)
55 return;
56 IStatus status = cast(IStatus) validationStatus.getValue();
57 if (isValid(status)) {
58 if (stale) {
59 // this.stale means we are out of sync with target,
60 // so reset wrapped list to exactly mirror target
61 stale = false;
62 updateWrappedSet(new HashSet(target));
63 } else {
64 SetDiff diff = event.diff;
65 if (computeNextDiff) {
66 diff = Diffs.computeSetDiff(wrappedSet, target);
67 computeNextDiff = false;
68 }
69 applyDiff(diff, wrappedSet);
70 fireSetChange(diff);
71 }
72 } else {
73 makeStale();
74 }
75 }
76 };
77
78 private IStaleListener targetStaleListener = new class() IStaleListener {
79 public void handleStale(StaleEvent staleEvent) {
80 fireStale();
81 }
82 };
83
84 private IValueChangeListener validationStatusChangeListener = new class() IValueChangeListener {
85 public void handleValueChange(ValueChangeEvent event) {
86 IStatus oldStatus = cast(IStatus) event.diff.getOldValue();
87 IStatus newStatus = cast(IStatus) event.diff.getNewValue();
88 if (stale && !isValid(oldStatus) && isValid(newStatus)) {
89 // this.stale means we are out of sync with target,
90 // reset wrapped set to exactly mirror target
91 stale = false;
92 updateWrappedSet(new HashSet(target));
93
94 // If the validation status becomes valid because of a change in
95 // target observable
96 computeNextDiff = true;
97 }
98 }
99 };
100
101 /**
102 * @param target
103 * @param validationStatus
104 */
105 public this(IObservableSet target,
106 IObservableValue validationStatus) {
107 super(target.getRealm(), new HashSet(target), target.getElementType());
108 Assert.isNotNull(validationStatus,
109 "Validation status observable cannot be null"); //$NON-NLS-1$
110 Assert
111 .isTrue(target.getRealm().equals(validationStatus.getRealm()),
112 "Target and validation status observables must be on the same realm"); //$NON-NLS-1$
113 this.target = target;
114 this.validationStatus = validationStatus;
115 target.addSetChangeListener(targetChangeListener);
116 target.addStaleListener(targetStaleListener);
117 validationStatus.addValueChangeListener(validationStatusChangeListener);
118 }
119
120 private void updateWrappedSet(Set newSet) {
121 Set oldSet = wrappedSet;
122 SetDiff diff = Diffs.computeSetDiff(oldSet, newSet);
123 wrappedSet = newSet;
124 fireSetChange(diff);
125 }
126
127 private static bool isValid(IStatus status) {
128 return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
129 }
130
131 private void applyDiff(SetDiff diff, Set set) {
132 for (Iterator iterator = diff.getRemovals().iterator(); iterator
133 .hasNext();) {
134 set.remove(iterator.next());
135 }
136 for (Iterator iterator = diff.getAdditions().iterator(); iterator
137 .hasNext();) {
138 set.add(iterator.next());
139 }
140 }
141
142 private void makeStale() {
143 if (!stale) {
144 stale = true;
145 fireStale();
146 }
147 }
148
149 private void updateTargetSet(SetDiff diff) {
150 updatingTarget = true;
151 try {
152 if (stale) {
153 stale = false;
154 applyDiff(Diffs.computeSetDiff(target, wrappedSet), target);
155 } else {
156 applyDiff(diff, target);
157 }
158 } finally {
159 updatingTarget = false;
160 }
161 }
162
163 public bool isStale() {
164 getterCalled();
165 return stale || target.isStale();
166 }
167
168 public bool add(Object o) {
169 getterCalled();
170 bool changed = wrappedSet.add(o);
171 if (changed) {
172 SetDiff diff = Diffs.createSetDiff(Collections.singleton(o),
173 Collections.EMPTY_SET);
174 updateTargetSet(diff);
175 fireSetChange(diff);
176 }
177 return changed;
178 }
179
180 public bool addAll(Collection c) {
181 getterCalled();
182 HashSet set = new HashSet(wrappedSet);
183 bool changed = set.addAll(c);
184 if (changed) {
185 SetDiff diff = Diffs.computeSetDiff(wrappedSet, set);
186 wrappedSet = set;
187 updateTargetSet(diff);
188 fireSetChange(diff);
189 }
190 return changed;
191 }
192
193 public void clear() {
194 getterCalled();
195 if (isEmpty())
196 return;
197 SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET, wrappedSet);
198 wrappedSet = new HashSet();
199 updateTargetSet(diff);
200 fireSetChange(diff);
201 }
202
203 public Iterator iterator() {
204 getterCalled();
205 final Iterator wrappedIterator = wrappedSet.iterator();
206 return new class() Iterator {
207 Object last = null;
208
209 public bool hasNext() {
210 return wrappedIterator.hasNext();
211 }
212
213 public Object next() {
214 return last = wrappedIterator.next();
215 }
216
217 public void remove() {
218 wrappedIterator.remove();
219 SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET,
220 Collections.singleton(last));
221 updateTargetSet(diff);
222 fireSetChange(diff);
223 }
224 };
225 }
226
227 public bool remove(Object o) {
228 getterCalled();
229 bool changed = wrappedSet.remove(o);
230 if (changed) {
231 SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET,
232 Collections.singleton(o));
233 updateTargetSet(diff);
234 fireSetChange(diff);
235 }
236 return changed;
237 }
238
239 public bool removeAll(Collection c) {
240 getterCalled();
241 Set set = new HashSet(wrappedSet);
242 bool changed = set.removeAll(c);
243 if (changed) {
244 SetDiff diff = Diffs.computeSetDiff(wrappedSet, set);
245 wrappedSet = set;
246 updateTargetSet(diff);
247 fireSetChange(diff);
248 }
249 return changed;
250 }
251
252 public bool retainAll(Collection c) {
253 getterCalled();
254 Set set = new HashSet(wrappedSet);
255 bool changed = set.retainAll(c);
256 if (changed) {
257 SetDiff diff = Diffs.computeSetDiff(wrappedSet, set);
258 wrappedSet = set;
259 updateTargetSet(diff);
260 fireSetChange(diff);
261 }
262 return changed;
263 }
264
265 public synchronized void dispose() {
266 target.removeSetChangeListener(targetChangeListener);
267 target.removeStaleListener(targetStaleListener);
268 validationStatus
269 .removeValueChangeListener(validationStatusChangeListener);
270 super.dispose();
271 }
272 }