1 /******************************************************************************* 2 * Copyright (c) 2010, 2017 Ovidio Mallo 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 * Ovidio Mallo - initial API and implementation (bug 305367) 13 * Stefan Xenos <sxenos@gmail.com> - Bug 335792 14 ******************************************************************************/ 15 16 package org.eclipse.core.internal.databinding.observable.masterdetail; 17 18 import java.util.HashMap; 19 import java.util.Map; 20 21 import org.eclipse.core.databinding.observable.IObserving; 22 import org.eclipse.core.databinding.observable.IStaleListener; 23 import org.eclipse.core.databinding.observable.ObservableTracker; 24 import org.eclipse.core.databinding.observable.map.ComputedObservableMap; 25 import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; 26 import org.eclipse.core.databinding.observable.set.IObservableSet; 27 import org.eclipse.core.databinding.observable.value.IObservableValue; 28 import org.eclipse.core.internal.databinding.identity.IdentitySet; 29 30 /** 31 * @param <M> 32 * type of the master observables in the master set 33 * @param <E> 34 * type of the detail elements 35 * @since 1.4 36 */ 37 public class SetDetailValueObservableMap<M, E> extends 38 ComputedObservableMap<M, E> implements IObserving { 39 40 private IObservableFactory<? super M, IObservableValue<E>> observableValueFactory; 41 42 private Map<M, IObservableValue<E>> detailObservableValueMap = new HashMap<>(); 43 44 private IdentitySet<IObservableValue<?>> staleDetailObservables = new IdentitySet<>(); 45 46 private IStaleListener detailStaleListener = staleEvent -> addStaleDetailObservable( 47 (IObservableValue<?>) staleEvent.getObservable()); 48 49 /** 50 * @param masterKeySet 51 * @param observableValueFactory 52 * @param detailValueType 53 */ SetDetailValueObservableMap( IObservableSet<M> masterKeySet, IObservableFactory<? super M, IObservableValue<E>> observableValueFactory, Object detailValueType)54 public SetDetailValueObservableMap( 55 IObservableSet<M> masterKeySet, 56 IObservableFactory<? super M, IObservableValue<E>> observableValueFactory, 57 Object detailValueType) { 58 super(masterKeySet, detailValueType); 59 this.observableValueFactory = observableValueFactory; 60 } 61 62 @Override hookListener(final M addedKey)63 protected void hookListener(final M addedKey) { 64 final IObservableValue<E> detailValue = getDetailObservableValue(addedKey); 65 66 detailValue.addValueChangeListener(event -> { 67 if (!event.getObservableValue().isStale()) { 68 staleDetailObservables.remove(detailValue); 69 } 70 71 fireSingleChange(addedKey, event.diff.getOldValue(), event.diff.getNewValue()); 72 }); 73 74 detailValue.addStaleListener(detailStaleListener); 75 } 76 77 @Override unhookListener(Object removedKey)78 protected void unhookListener(Object removedKey) { 79 if (isDisposed()) { 80 return; 81 } 82 83 IObservableValue<E> detailValue = detailObservableValueMap.remove(removedKey); 84 staleDetailObservables.remove(detailValue); 85 detailValue.dispose(); 86 } 87 getDetailObservableValue(M masterKey)88 private IObservableValue<E> getDetailObservableValue(M masterKey) { 89 IObservableValue<E> detailValue = detailObservableValueMap.get(masterKey); 90 91 if (detailValue == null) { 92 ObservableTracker.setIgnore(true); 93 try { 94 detailValue = observableValueFactory.createObservable(masterKey); 95 } finally { 96 ObservableTracker.setIgnore(false); 97 } 98 99 detailObservableValueMap.put(masterKey, detailValue); 100 101 if (detailValue.isStale()) { 102 addStaleDetailObservable(detailValue); 103 } 104 } 105 106 return detailValue; 107 } 108 addStaleDetailObservable(IObservableValue<?> detailObservable)109 private void addStaleDetailObservable(IObservableValue<?> detailObservable) { 110 boolean wasStale = isStale(); 111 staleDetailObservables.add(detailObservable); 112 if (!wasStale) { 113 fireStale(); 114 } 115 } 116 117 @Override doGet(M key)118 protected E doGet(M key) { 119 IObservableValue<E> detailValue = getDetailObservableValue(key); 120 return detailValue.getValue(); 121 } 122 123 @Override doPut(M key, E value)124 protected E doPut(M key, E value) { 125 IObservableValue<E> detailValue = getDetailObservableValue(key); 126 E oldValue = detailValue.getValue(); 127 detailValue.setValue(value); 128 return oldValue; 129 } 130 131 @Override containsKey(Object key)132 public boolean containsKey(Object key) { 133 getterCalled(); 134 135 return keySet().contains(key); 136 } 137 138 @Override remove(Object key)139 public E remove(Object key) { 140 checkRealm(); 141 142 if (!containsKey(key)) { 143 return null; 144 } 145 146 @SuppressWarnings("unchecked") 147 IObservableValue<E> detailValue = getDetailObservableValue((M) key); 148 E oldValue = detailValue.getValue(); 149 150 keySet().remove(key); 151 152 return oldValue; 153 } 154 155 @Override size()156 public int size() { 157 getterCalled(); 158 159 return keySet().size(); 160 } 161 162 @Override isStale()163 public boolean isStale() { 164 return super.isStale() || staleDetailObservables != null 165 && !staleDetailObservables.isEmpty(); 166 } 167 168 @Override getObserved()169 public Object getObserved() { 170 return keySet(); 171 } 172 173 @Override dispose()174 public synchronized void dispose() { 175 super.dispose(); 176 177 observableValueFactory = null; 178 detailObservableValueMap = null; 179 detailStaleListener = null; 180 staleDetailObservables = null; 181 } 182 getterCalled()183 private void getterCalled() { 184 ObservableTracker.getterCalled(this); 185 } 186 } 187