1 /******************************************************************************* 2 * Copyright (c) 2008, 2017 Matthew Hall and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Matthew Hall - initial API and implementation (bug 194734) 13 * Matthew Hall - bugs 265561, 262287, 268203, 268688, 301774 14 * Stefan Xenos <sxenos@gmail.com> - Bug 335792 15 ******************************************************************************/ 16 17 package org.eclipse.core.internal.databinding.property.set; 18 19 import java.util.Collection; 20 import java.util.Collections; 21 import java.util.ConcurrentModificationException; 22 import java.util.HashSet; 23 import java.util.Iterator; 24 import java.util.Set; 25 26 import org.eclipse.core.databinding.observable.Diffs; 27 import org.eclipse.core.databinding.observable.Realm; 28 import org.eclipse.core.databinding.observable.set.AbstractObservableSet; 29 import org.eclipse.core.databinding.observable.set.SetDiff; 30 import org.eclipse.core.databinding.property.INativePropertyListener; 31 import org.eclipse.core.databinding.property.IPropertyObservable; 32 import org.eclipse.core.databinding.property.SimplePropertyEvent; 33 import org.eclipse.core.databinding.property.set.SimpleSetProperty; 34 35 /** 36 * @param <S> 37 * type of the source object 38 * @param <E> 39 * type of the elements in the set 40 * @since 1.2 41 * 42 */ 43 public class SimplePropertyObservableSet<S, E> extends AbstractObservableSet<E> 44 implements IPropertyObservable<SimpleSetProperty<S, E>> { 45 private S source; 46 private SimpleSetProperty<S, E> property; 47 48 private volatile boolean updating = false; 49 50 private volatile int modCount = 0; 51 52 private INativePropertyListener<S> listener; 53 54 private Set<E> cachedSet; 55 private boolean stale; 56 57 /** 58 * @param realm 59 * @param source 60 * @param property 61 */ SimplePropertyObservableSet(Realm realm, S source, SimpleSetProperty<S, E> property)62 public SimplePropertyObservableSet(Realm realm, S source, SimpleSetProperty<S, E> property) { 63 super(realm); 64 this.source = source; 65 this.property = property; 66 } 67 68 @Override firstListenerAdded()69 protected void firstListenerAdded() { 70 if (!isDisposed() && listener == null) { 71 listener = property.adaptListener(event -> { 72 if (!isDisposed() && !updating) { 73 getRealm().exec(() -> { 74 if (event.type == SimplePropertyEvent.CHANGE) { 75 modCount++; 76 notifyIfChanged(event.diff); 77 } else if (event.type == SimplePropertyEvent.STALE && !stale) { 78 stale = true; 79 fireStale(); 80 } 81 }); 82 } 83 }); 84 } 85 86 getRealm().exec(() -> { 87 cachedSet = new HashSet<>(getSet()); 88 stale = false; 89 90 if (listener != null) 91 listener.addTo(source); 92 }); 93 } 94 95 @Override lastListenerRemoved()96 protected void lastListenerRemoved() { 97 if (listener != null) 98 listener.removeFrom(source); 99 100 cachedSet.clear(); 101 cachedSet = null; 102 stale = false; 103 } 104 105 @Override getWrappedSet()106 protected Set<E> getWrappedSet() { 107 return getSet(); 108 } 109 110 @Override getElementType()111 public Object getElementType() { 112 return property.getElementType(); 113 } 114 115 // Queries 116 getSet()117 private Set<E> getSet() { 118 return property.getSet(source); 119 } 120 121 @Override contains(Object o)122 public boolean contains(Object o) { 123 getterCalled(); 124 return getSet().contains(o); 125 } 126 127 @Override containsAll(Collection<?> c)128 public boolean containsAll(Collection<?> c) { 129 getterCalled(); 130 return getSet().containsAll(c); 131 } 132 133 @Override isEmpty()134 public boolean isEmpty() { 135 getterCalled(); 136 return getSet().isEmpty(); 137 } 138 139 @Override toArray()140 public Object[] toArray() { 141 getterCalled(); 142 return getSet().toArray(); 143 } 144 145 @Override toArray(T[] a)146 public <T> T[] toArray(T[] a) { 147 getterCalled(); 148 return getSet().toArray(a); 149 } 150 151 // Single change operations 152 updateSet(Set<E> set, SetDiff<E> diff)153 private void updateSet(Set<E> set, SetDiff<E> diff) { 154 if (!diff.isEmpty()) { 155 boolean wasUpdating = updating; 156 updating = true; 157 try { 158 property.updateSet(source, diff); 159 modCount++; 160 } finally { 161 updating = wasUpdating; 162 } 163 164 notifyIfChanged(null); 165 } 166 } 167 168 @Override add(E o)169 public boolean add(E o) { 170 checkRealm(); 171 172 Set<E> set = getSet(); 173 if (set.contains(o)) 174 return false; 175 176 SetDiff<E> diff = Diffs.createSetDiff(Collections.singleton(o), Collections.<E>emptySet()); 177 updateSet(set, diff); 178 179 return true; 180 } 181 182 @Override iterator()183 public Iterator<E> iterator() { 184 getterCalled(); 185 return new Iterator<E>() { 186 int expectedModCount = modCount; 187 Set<E> set = new HashSet<>(getSet()); 188 Iterator<E> iterator = set.iterator(); 189 E last = null; 190 191 @Override 192 public boolean hasNext() { 193 getterCalled(); 194 checkForComodification(); 195 return iterator.hasNext(); 196 } 197 198 @Override 199 public E next() { 200 getterCalled(); 201 checkForComodification(); 202 last = iterator.next(); 203 return last; 204 } 205 206 @Override 207 public void remove() { 208 checkRealm(); 209 checkForComodification(); 210 211 SetDiff<E> diff = Diffs.createSetDiff(Collections.<E>emptySet(), Collections.singleton(last)); 212 updateSet(set, diff); 213 214 iterator.remove(); // stay in sync 215 216 last = null; 217 expectedModCount = modCount; 218 } 219 220 private void checkForComodification() { 221 if (expectedModCount != modCount) 222 throw new ConcurrentModificationException(); 223 } 224 }; 225 } 226 227 @Override remove(Object o)228 public boolean remove(Object o) { 229 getterCalled(); 230 231 Set<E> set = getSet(); 232 if (!set.contains(o)) 233 return false; 234 235 @SuppressWarnings("unchecked") 236 // if o is contained, it is an E 237 SetDiff<E> diff = Diffs.createSetDiff(Collections.<E>emptySet(), Collections.singleton((E) o)); 238 updateSet(set, diff); 239 240 return true; 241 } 242 243 // Bulk change operations 244 245 @Override addAll(Collection<? extends E> c)246 public boolean addAll(Collection<? extends E> c) { 247 getterCalled(); 248 249 if (c.isEmpty()) 250 return false; 251 252 Set<E> set = getSet(); 253 if (set.containsAll(c)) 254 return false; 255 256 Set<E> additions = new HashSet<>(c); 257 additions.removeAll(set); 258 259 if (additions.isEmpty()) 260 return false; 261 262 SetDiff<E> diff = Diffs.createSetDiff(additions, Collections.<E>emptySet()); 263 updateSet(set, diff); 264 265 return true; 266 } 267 268 @Override removeAll(Collection<?> c)269 public boolean removeAll(Collection<?> c) { 270 getterCalled(); 271 272 if (c.isEmpty()) 273 return false; 274 275 Set<E> set = getSet(); 276 if (set.isEmpty()) 277 return false; 278 279 Set<Object> removals = new HashSet<>(c); 280 removals.retainAll(set); 281 @SuppressWarnings("unchecked") 282 // because we have removed everything that is not an E 283 Set<E> typedRemovals = (Set<E>) removals; 284 285 if (removals.isEmpty()) 286 return false; 287 288 SetDiff<E> diff = Diffs.createSetDiff(Collections.<E>emptySet(), typedRemovals); 289 updateSet(set, diff); 290 291 return true; 292 } 293 294 @Override retainAll(Collection<?> c)295 public boolean retainAll(Collection<?> c) { 296 getterCalled(); 297 298 Set<E> set = getSet(); 299 if (set.isEmpty()) 300 return false; 301 302 if (c.isEmpty()) { 303 clear(); 304 return true; 305 } 306 307 Set<E> removals = new HashSet<>(set); 308 removals.removeAll(c); 309 310 if (removals.isEmpty()) 311 return false; 312 313 SetDiff<E> diff = Diffs.createSetDiff(Collections.<E>emptySet(), removals); 314 updateSet(set, diff); 315 316 return true; 317 } 318 319 @Override clear()320 public void clear() { 321 getterCalled(); 322 323 Set<E> set = getSet(); 324 if (set.isEmpty()) 325 return; 326 327 SetDiff<E> diff = Diffs.createSetDiff(Collections.<E>emptySet(), set); 328 updateSet(set, diff); 329 } 330 notifyIfChanged(SetDiff<E> diff)331 private void notifyIfChanged(SetDiff<E> diff) { 332 if (hasListeners()) { 333 Set<E> oldSet = cachedSet; 334 Set<E> newSet = cachedSet = new HashSet<>(getSet()); 335 if (diff == null) 336 diff = Diffs.computeSetDiff(oldSet, newSet); 337 if (!diff.isEmpty() || stale) { 338 stale = false; 339 fireSetChange(diff); 340 } 341 } 342 } 343 344 @Override isStale()345 public boolean isStale() { 346 getterCalled(); 347 return stale; 348 } 349 350 @Override equals(Object o)351 public boolean equals(Object o) { 352 getterCalled(); 353 return getSet().equals(o); 354 } 355 356 @Override hashCode()357 public int hashCode() { 358 getterCalled(); 359 return getSet().hashCode(); 360 } 361 362 @Override getObserved()363 public Object getObserved() { 364 return source; 365 } 366 367 @Override getProperty()368 public SimpleSetProperty<S, E> getProperty() { 369 return property; 370 } 371 372 @Override dispose()373 public synchronized void dispose() { 374 if (!isDisposed()) { 375 if (listener != null) 376 listener.removeFrom(source); 377 property = null; 378 source = null; 379 listener = null; 380 stale = false; 381 } 382 super.dispose(); 383 } 384 } 385