1 /******************************************************************************* 2 * Copyright (c) 2005, 2017 IBM Corporation 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 * IBM Corporation - initial API and implementation 13 * Brad Reynolds - bug 147515 14 * Matthew Hall - bug 221351, 247875, 246782, 249526, 268022, 251424 15 * Ovidio Mallo - bug 241318 16 * Stefan Xenos <sxenos@gmail.com> - Bug 335792 17 * Stefan Xenos <sxenos@gmail.com> - Bug 474065 18 *******************************************************************************/ 19 package org.eclipse.core.internal.databinding.observable.masterdetail; 20 21 import java.util.ArrayList; 22 import java.util.Collection; 23 import java.util.Collections; 24 import java.util.List; 25 26 import org.eclipse.core.databinding.observable.Diffs; 27 import org.eclipse.core.databinding.observable.IObserving; 28 import org.eclipse.core.databinding.observable.ObservableTracker; 29 import org.eclipse.core.databinding.observable.list.IListChangeListener; 30 import org.eclipse.core.databinding.observable.list.IObservableList; 31 import org.eclipse.core.databinding.observable.list.ObservableList; 32 import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; 33 import org.eclipse.core.databinding.observable.value.IObservableValue; 34 import org.eclipse.core.databinding.observable.value.IValueChangeListener; 35 import org.eclipse.core.runtime.Assert; 36 37 /** 38 * @param <M> 39 * type of the master observable 40 * @param <E> 41 * type of the elements in the inner observable list 42 * @since 3.2 43 * 44 */ 45 public class DetailObservableList<M, E> extends ObservableList<E>implements IObserving { 46 47 private boolean updating = false; 48 49 private IListChangeListener<E> innerChangeListener = event -> { 50 if (!updating) { 51 fireListChange(Diffs.unmodifiableDiff(event.diff)); 52 } 53 }; 54 55 private M currentOuterValue; 56 57 private IObservableList<E> innerObservableList; 58 59 private IObservableFactory<? super M, IObservableList<E>> factory; 60 61 private IObservableValue<M> outerObservableValue; 62 63 private Object detailType; 64 65 /** 66 * @param factory 67 * @param outerObservableValue 68 * @param detailType 69 */ DetailObservableList( IObservableFactory<? super M, IObservableList<E>> factory, IObservableValue<M> outerObservableValue, Object detailType)70 public DetailObservableList( 71 IObservableFactory<? super M, IObservableList<E>> factory, 72 IObservableValue<M> outerObservableValue, Object detailType) { 73 super(outerObservableValue.getRealm(), Collections.<E> emptyList(), detailType); 74 Assert.isTrue(!outerObservableValue.isDisposed(), 75 "Master observable is disposed"); //$NON-NLS-1$ 76 77 this.factory = factory; 78 this.outerObservableValue = outerObservableValue; 79 this.detailType = detailType; 80 81 outerObservableValue.addDisposeListener(staleEvent -> dispose()); 82 83 ObservableTracker.setIgnore(true); 84 try { 85 updateInnerObservableList(); 86 } finally { 87 ObservableTracker.setIgnore(false); 88 } 89 outerObservableValue.addValueChangeListener(outerChangeListener); 90 } 91 92 IValueChangeListener<M> outerChangeListener = event -> { 93 if (isDisposed()) 94 return; 95 ObservableTracker.setIgnore(true); 96 try { 97 List<E> oldList = new ArrayList<>(wrappedList); 98 updateInnerObservableList(); 99 fireListChange(Diffs.computeListDiff(oldList, wrappedList)); 100 } finally { 101 ObservableTracker.setIgnore(false); 102 } 103 }; 104 updateInnerObservableList()105 private void updateInnerObservableList() { 106 if (innerObservableList != null) { 107 innerObservableList.removeListChangeListener(innerChangeListener); 108 innerObservableList.dispose(); 109 } 110 currentOuterValue = outerObservableValue.getValue(); 111 if (currentOuterValue == null) { 112 innerObservableList = null; 113 wrappedList = Collections.emptyList(); 114 } else { 115 ObservableTracker.setIgnore(true); 116 try { 117 innerObservableList = factory 118 .createObservable(currentOuterValue); 119 } finally { 120 ObservableTracker.setIgnore(false); 121 } 122 DetailObservableHelper.warnIfDifferentRealms(getRealm(), 123 innerObservableList.getRealm()); 124 wrappedList = innerObservableList; 125 126 if (detailType != null) { 127 Object innerValueType = innerObservableList.getElementType(); 128 Assert.isTrue(getElementType().equals(innerValueType), 129 "Cannot change value type in a nested observable list"); //$NON-NLS-1$ 130 } 131 innerObservableList.addListChangeListener(innerChangeListener); 132 } 133 } 134 135 @Override add(final E o)136 public boolean add(final E o) { 137 ObservableTracker.setIgnore(true); 138 try { 139 return wrappedList.add(o); 140 } finally { 141 ObservableTracker.setIgnore(false); 142 } 143 } 144 145 @Override add(final int index, final E element)146 public void add(final int index, final E element) { 147 ObservableTracker.setIgnore(true); 148 try { 149 wrappedList.add(index, element); 150 } finally { 151 ObservableTracker.setIgnore(false); 152 } 153 } 154 155 @Override remove(final Object o)156 public boolean remove(final Object o) { 157 ObservableTracker.setIgnore(true); 158 try { 159 return wrappedList.remove(o); 160 } finally { 161 ObservableTracker.setIgnore(false); 162 } 163 } 164 165 @Override set(final int index, final E element)166 public E set(final int index, final E element) { 167 ObservableTracker.setIgnore(true); 168 try { 169 return wrappedList.set(index, element); 170 } finally { 171 ObservableTracker.setIgnore(false); 172 } 173 } 174 175 @Override move(final int oldIndex, final int newIndex)176 public E move(final int oldIndex, final int newIndex) { 177 if (innerObservableList != null) { 178 ObservableTracker.setIgnore(true); 179 try { 180 return innerObservableList.move(oldIndex, newIndex); 181 } finally { 182 ObservableTracker.setIgnore(false); 183 } 184 } 185 return super.move(oldIndex, newIndex); 186 } 187 188 @Override remove(final int index)189 public E remove(final int index) { 190 ObservableTracker.setIgnore(true); 191 try { 192 return wrappedList.remove(index); 193 } finally { 194 ObservableTracker.setIgnore(false); 195 } 196 } 197 198 @Override addAll(final Collection<? extends E> c)199 public boolean addAll(final Collection<? extends E> c) { 200 ObservableTracker.setIgnore(true); 201 try { 202 return wrappedList.addAll(c); 203 } finally { 204 ObservableTracker.setIgnore(false); 205 } 206 } 207 208 @Override addAll(final int index, final Collection<? extends E> c)209 public boolean addAll(final int index, final Collection<? extends E> c) { 210 ObservableTracker.setIgnore(true); 211 try { 212 return wrappedList.addAll(index, c); 213 } finally { 214 ObservableTracker.setIgnore(false); 215 } 216 } 217 218 @Override removeAll(final Collection<?> c)219 public boolean removeAll(final Collection<?> c) { 220 ObservableTracker.setIgnore(true); 221 try { 222 return wrappedList.removeAll(c); 223 } finally { 224 ObservableTracker.setIgnore(false); 225 } 226 } 227 228 @Override retainAll(final Collection<?> c)229 public boolean retainAll(final Collection<?> c) { 230 ObservableTracker.setIgnore(true); 231 try { 232 return wrappedList.retainAll(c); 233 } finally { 234 ObservableTracker.setIgnore(false); 235 } 236 } 237 238 @Override clear()239 public void clear() { 240 ObservableTracker.setIgnore(true); 241 try { 242 wrappedList.clear(); 243 } finally { 244 ObservableTracker.setIgnore(false); 245 } 246 } 247 248 @Override dispose()249 public synchronized void dispose() { 250 super.dispose(); 251 252 if (outerObservableValue != null) { 253 outerObservableValue.removeValueChangeListener(outerChangeListener); 254 } 255 if (innerObservableList != null) { 256 innerObservableList.removeListChangeListener(innerChangeListener); 257 innerObservableList.dispose(); 258 } 259 outerObservableValue = null; 260 outerChangeListener = null; 261 currentOuterValue = null; 262 factory = null; 263 innerObservableList = null; 264 innerChangeListener = null; 265 } 266 267 @Override getObserved()268 public Object getObserved() { 269 if (innerObservableList instanceof IObserving) { 270 return ((IObserving) innerObservableList).getObserved(); 271 } 272 return null; 273 } 274 } 275