Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 173246 Details for
Bug 305367
[DataBinding] Detail value observables for observable (list|set|map)s
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
[patch]
updated patch for Eclipse 3.7
clipboard.txt (text/plain), 75.17 KB, created by
Ovidio Mallo
on 2010-07-01 13:48:07 EDT
(
hide
)
Description:
updated patch for Eclipse 3.7
Filename:
MIME Type:
Creator:
Ovidio Mallo
Created:
2010-07-01 13:48:07 EDT
Size:
75.17 KB
patch
obsolete
>### Eclipse Workspace Patch 1.0 >#P org.eclipse.core.databinding.observable >Index: META-INF/MANIFEST.MF >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.core.databinding.observable/META-INF/MANIFEST.MF,v >retrieving revision 1.5 >diff -u -r1.5 MANIFEST.MF >--- META-INF/MANIFEST.MF 25 Aug 2009 04:57:26 -0000 1.5 >+++ META-INF/MANIFEST.MF 1 Jul 2010 17:46:44 -0000 >@@ -2,7 +2,7 @@ > Bundle-ManifestVersion: 2 > Bundle-Name: %pluginName > Bundle-SymbolicName: org.eclipse.core.databinding.observable >-Bundle-Version: 1.3.0.qualifier >+Bundle-Version: 1.4.0.qualifier > Bundle-ClassPath: . > Bundle-Vendor: %providerName > Bundle-Localization: plugin >Index: src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java,v >retrieving revision 1.10 >diff -u -r1.10 MasterDetailObservables.java >--- src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java 17 Oct 2008 16:44:25 -0000 1.10 >+++ src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java 1 Jul 2010 17:46:44 -0000 >@@ -9,6 +9,7 @@ > * IBM Corporation - initial API and implementation > * Brad Reynolds - bug 147515 > * Matthew Hall - bug 221704, 226289 >+ * Ovidio Mallo - bugs 305367 > *******************************************************************************/ > > package org.eclipse.core.databinding.observable.masterdetail; >@@ -21,6 +22,9 @@ > import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableMap; > import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableSet; > import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableValue; >+import org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailValueObservableList; >+import org.eclipse.core.internal.databinding.observable.masterdetail.MapDetailValueObservableMap; >+import org.eclipse.core.internal.databinding.observable.masterdetail.SetDetailValueObservableMap; > > /** > * Allows for the observation of an attribute, the detail, of an observable >@@ -29,7 +33,7 @@ > * @since 1.0 > */ > public class MasterDetailObservables { >- >+ > /** > * Creates a detail observable value from a master observable value and a > * factory. This can be used to create observable values that represent a >@@ -145,4 +149,84 @@ > return new DetailObservableMap(detailFactory, master, detailKeyType, > detailValueType); > } >+ >+ /** >+ * Creates an unmodifiable observable list of detail values from an >+ * observable list of master values. For every master value in the given >+ * list, the provided factory is used to create the corresponding detail >+ * observable value. This can e.g. be used to create a list of values that >+ * represent the same property of a set of selected objects in a table. >+ * >+ * @param masterList >+ * the list of master values to track >+ * @param detailFactory >+ * a factory for creating {@link IObservableValue} instances >+ * given a current value of one of the masters >+ * @param detailType >+ * the value type of the detail values, typically of type >+ * java.lang.Class and can be <code>null</code> >+ * @return a list of detail values of the given value type that, for any >+ * current value of any of the master values, behaves like the >+ * observable value created by the factory for that current value. >+ * >+ * @since 1.4 >+ */ >+ public static IObservableList detailListValues(IObservableList masterList, >+ IObservableFactory detailFactory, Object detailType) { >+ return new ListDetailValueObservableList(masterList, detailFactory, >+ detailType); >+ } >+ >+ /** >+ * Creates an unmodifiable observable map of detail values from an >+ * observable set of master values. For every master value in the given set, >+ * the provided factory is used to create the corresponding detail >+ * observable value which is added to a map whose key is the master value. >+ * >+ * @param masterSet >+ * the set of master values to track >+ * @param detailFactory >+ * a factory for creating {@link IObservableValue} instances >+ * given a current value of one of the masters >+ * @param detailType >+ * the value type of the detail values, typically of type >+ * java.lang.Class and can be <code>null</code> >+ * @return a map of detail values of the given value type that, for any >+ * current value of any of the master values, behaves like the >+ * observable value created by the factory for that current value. >+ * >+ * @since 1.4 >+ */ >+ public static IObservableMap detailSetValues(IObservableSet masterSet, >+ IObservableFactory detailFactory, Object detailType) { >+ return new SetDetailValueObservableMap(masterSet, detailFactory, >+ detailType); >+ } >+ >+ /** >+ * Creates an unmodifiable observable map of detail values from an >+ * observable map of master values. For every master value in the given map, >+ * the provided factory is used to create the corresponding detail >+ * observable value. This can e.g. be used to create a map of values that >+ * represent the same property of a set of selected objects in a table. >+ * >+ * @param masterMap >+ * the set of master values to track >+ * @param detailFactory >+ * a factory for creating {@link IObservableValue} instances >+ * given a current value of one of the masters >+ * @param detailType >+ * the value type of the detail values, typically of type >+ * java.lang.Class and can be <code>null</code> >+ * @return a list of detail values of the given value type that, for any >+ * current value of any of the master values, behaves like the >+ * observable value created by the factory for that current value. >+ * >+ * @since 1.4 >+ */ >+ public static IObservableMap detailMapValues(IObservableMap masterMap, >+ IObservableFactory detailFactory, Object detailType) { >+ return new MapDetailValueObservableMap(masterMap, detailFactory, >+ detailType); >+ } > } >Index: src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java >diff -N src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,268 @@ >+/******************************************************************************* >+ * Copyright (c) 2010 Ovidio Mallo and others. >+ * All rights reserved. This program and the accompanying materials >+ * are made available under the terms of the Eclipse Public License v1.0 >+ * which accompanies this distribution, and is available at >+ * http://www.eclipse.org/legal/epl-v10.html >+ * >+ * Contributors: >+ * Ovidio Mallo - initial API and implementation (bug 305367) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.observable.masterdetail; >+ >+import java.util.ArrayList; >+import java.util.BitSet; >+import java.util.Collection; >+import java.util.Collections; >+import java.util.Iterator; >+import java.util.RandomAccess; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.DisposeEvent; >+import org.eclipse.core.databinding.observable.IDisposeListener; >+import org.eclipse.core.databinding.observable.IObserving; >+import org.eclipse.core.databinding.observable.IStaleListener; >+import org.eclipse.core.databinding.observable.ObservableTracker; >+import org.eclipse.core.databinding.observable.StaleEvent; >+import org.eclipse.core.databinding.observable.list.AbstractObservableList; >+import org.eclipse.core.databinding.observable.list.IListChangeListener; >+import org.eclipse.core.databinding.observable.list.IObservableList; >+import org.eclipse.core.databinding.observable.list.ListChangeEvent; >+import org.eclipse.core.databinding.observable.list.ListDiff; >+import org.eclipse.core.databinding.observable.list.ListDiffEntry; >+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; >+import org.eclipse.core.databinding.observable.value.IObservableValue; >+import org.eclipse.core.databinding.observable.value.IValueChangeListener; >+import org.eclipse.core.databinding.observable.value.ValueChangeEvent; >+import org.eclipse.core.internal.databinding.identity.IdentitySet; >+ >+/** >+ * @since 1.3 >+ */ >+public class ListDetailValueObservableList extends AbstractObservableList >+ implements IObserving, RandomAccess { >+ >+ private IObservableList masterList; >+ >+ private IObservableFactory detailFactory; >+ >+ private Object detailType; >+ >+ private ArrayList detailList; >+ >+ private IdentitySet staleDetailObservables = new IdentitySet(); >+ >+ private IListChangeListener masterListListener = new IListChangeListener() { >+ public void handleListChange(ListChangeEvent event) { >+ adaptMasterListDiff(event.diff); >+ } >+ }; >+ >+ private IValueChangeListener detailValueListener = new IValueChangeListener() { >+ public void handleValueChange(ValueChangeEvent event) { >+ if (!event.getObservable().isStale()) { >+ staleDetailObservables.remove(event.getObservable()); >+ } >+ handleDetailValueChange(event); >+ } >+ }; >+ >+ private IStaleListener masterStaleListener = new IStaleListener() { >+ public void handleStale(StaleEvent staleEvent) { >+ fireStale(); >+ } >+ }; >+ >+ private IStaleListener detailStaleListener = new IStaleListener() { >+ public void handleStale(StaleEvent staleEvent) { >+ boolean wasStale = isStale(); >+ staleDetailObservables.add((staleEvent.getObservable())); >+ if (!wasStale) { >+ fireStale(); >+ } >+ } >+ }; >+ >+ /** >+ * >+ * @param masterList >+ * @param detailFactory >+ * @param detailType >+ */ >+ public ListDetailValueObservableList(IObservableList masterList, >+ IObservableFactory detailFactory, Object detailType) { >+ super(masterList.getRealm()); >+ this.masterList = masterList; >+ this.detailFactory = detailFactory; >+ this.detailType = detailType; >+ this.detailList = new ArrayList(); >+ >+ masterList.addListChangeListener(masterListListener); >+ masterList.addStaleListener(masterStaleListener); >+ masterList.addDisposeListener(new IDisposeListener() { >+ public void handleDispose(DisposeEvent event) { >+ ListDetailValueObservableList.this.dispose(); >+ } >+ }); >+ >+ ListDiff initMasterDiff = Diffs.computeListDiff(Collections.EMPTY_LIST, >+ masterList); >+ adaptMasterListDiff(initMasterDiff); >+ } >+ >+ private void adaptMasterListDiff(ListDiff masterListDiff) { >+ boolean wasStale = isStale(); >+ >+ ListDiffEntry[] masterEntries = masterListDiff.getDifferences(); >+ ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length]; >+ for (int i = 0; i < masterEntries.length; i++) { >+ ListDiffEntry masterEntry = masterEntries[i]; >+ int index = masterEntry.getPosition(); >+ >+ Object detailValue; >+ if (masterEntry.isAddition()) { >+ Object masterElement = masterEntry.getElement(); >+ detailValue = addDetailObservable(masterElement, index); >+ } else { >+ detailValue = removeDetailObservable(index); >+ } >+ >+ // Create the corresponding diff for the detail list. >+ detailEntries[i] = Diffs.createListDiffEntry(index, masterEntry >+ .isAddition(), detailValue); >+ } >+ >+ if (!wasStale && isStale()) { >+ fireStale(); >+ } >+ >+ // Fire a list change event with the adapted diffs on the detail list. >+ fireListChange(Diffs.createListDiff(detailEntries)); >+ } >+ >+ private Object addDetailObservable(Object masterElement, int index) { >+ IObservableValue detail; >+ ObservableTracker.setIgnore(true); >+ try { >+ detail = (IObservableValue) detailFactory >+ .createObservable(masterElement); >+ } finally { >+ ObservableTracker.setIgnore(false); >+ } >+ >+ detailList.add(index, detail); >+ >+ detail.addValueChangeListener(detailValueListener); >+ >+ detail.addStaleListener(detailStaleListener); >+ if (detail.isStale()) { >+ staleDetailObservables.add(detail); >+ } >+ >+ return detail.getValue(); >+ } >+ >+ private Object removeDetailObservable(int index) { >+ IObservableValue detail = (IObservableValue) detailList.remove(index); >+ staleDetailObservables.remove(detail); >+ Object detailValue = detail.getValue(); >+ detail.dispose(); >+ return detailValue; >+ } >+ >+ private void handleDetailValueChange(ValueChangeEvent event) { >+ IObservableValue detail = event.getObservableValue(); >+ >+ BitSet detailIndexes = new BitSet(); >+ for (int i = 0; i < detailList.size(); i++) { >+ if (detailList.get(i) == detail) { >+ detailIndexes.set(i); >+ } >+ } >+ >+ Object oldValue = event.diff.getOldValue(); >+ Object newValue = event.diff.getNewValue(); >+ ListDiffEntry[] diffEntries = new ListDiffEntry[2 * detailIndexes >+ .cardinality()]; >+ int diffIndex = 0; >+ for (int b = 0; b != -1; b = detailIndexes.nextSetBit(b + 1)) { >+ diffEntries[diffIndex++] = Diffs.createListDiffEntry(b, false, >+ oldValue); >+ diffEntries[diffIndex++] = Diffs.createListDiffEntry(b, true, >+ newValue); >+ } >+ fireListChange(Diffs.createListDiff(diffEntries)); >+ } >+ >+ protected int doGetSize() { >+ return detailList.size(); >+ } >+ >+ public Object get(int index) { >+ ObservableTracker.getterCalled(this); >+ return ((IObservableValue) detailList.get(index)).getValue(); >+ } >+ >+ public Object set(int index, Object element) { >+ IObservableValue detail = (IObservableValue) detailList.get(index); >+ Object oldElement = detail.getValue(); >+ detail.setValue(element); >+ return oldElement; >+ } >+ >+ public Object move(int oldIndex, int newIndex) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean remove(Object o) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean removeAll(Collection c) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean retainAll(Collection c) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public Object getElementType() { >+ return detailType; >+ } >+ >+ public boolean isStale() { >+ return super.isStale() >+ || (masterList != null && masterList.isStale()) >+ || (staleDetailObservables != null && !staleDetailObservables >+ .isEmpty()); >+ } >+ >+ public Object getObserved() { >+ return masterList; >+ } >+ >+ public synchronized void dispose() { >+ if (masterList != null) { >+ masterList.removeListChangeListener(masterListListener); >+ masterList.removeStaleListener(masterStaleListener); >+ } >+ >+ if (detailList != null) { >+ for (Iterator iter = detailList.iterator(); iter.hasNext();) { >+ IObservableValue detailValue = (IObservableValue) iter.next(); >+ detailValue.dispose(); >+ } >+ detailList.clear(); >+ } >+ >+ masterList = null; >+ detailFactory = null; >+ detailType = null; >+ masterListListener = null; >+ detailValueListener = null; >+ staleDetailObservables = null; >+ >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/observable/masterdetail/MapDetailValueObservableMap.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/observable/masterdetail/MapDetailValueObservableMap.java >diff -N src/org/eclipse/core/internal/databinding/observable/masterdetail/MapDetailValueObservableMap.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/observable/masterdetail/MapDetailValueObservableMap.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,402 @@ >+/******************************************************************************* >+ * Copyright (c) 2010 Ovidio Mallo and others. >+ * All rights reserved. This program and the accompanying materials >+ * are made available under the terms of the Eclipse Public License v1.0 >+ * which accompanies this distribution, and is available at >+ * http://www.eclipse.org/legal/epl-v10.html >+ * >+ * Contributors: >+ * Ovidio Mallo - initial API and implementation (bug 305367) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.observable.masterdetail; >+ >+import java.util.AbstractSet; >+import java.util.Collections; >+import java.util.HashMap; >+import java.util.Iterator; >+import java.util.Map; >+import java.util.Set; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.DisposeEvent; >+import org.eclipse.core.databinding.observable.IDisposeListener; >+import org.eclipse.core.databinding.observable.IObserving; >+import org.eclipse.core.databinding.observable.IStaleListener; >+import org.eclipse.core.databinding.observable.ObservableTracker; >+import org.eclipse.core.databinding.observable.StaleEvent; >+import org.eclipse.core.databinding.observable.map.AbstractObservableMap; >+import org.eclipse.core.databinding.observable.map.IMapChangeListener; >+import org.eclipse.core.databinding.observable.map.IObservableMap; >+import org.eclipse.core.databinding.observable.map.MapChangeEvent; >+import org.eclipse.core.databinding.observable.map.MapDiff; >+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; >+import org.eclipse.core.databinding.observable.value.IObservableValue; >+import org.eclipse.core.databinding.observable.value.IValueChangeListener; >+import org.eclipse.core.databinding.observable.value.ValueChangeEvent; >+import org.eclipse.core.internal.databinding.identity.IdentityMap; >+import org.eclipse.core.internal.databinding.identity.IdentitySet; >+import org.eclipse.core.internal.databinding.observable.Util; >+ >+/** >+ * @since 1.3 >+ */ >+public class MapDetailValueObservableMap extends AbstractObservableMap >+ implements IObserving { >+ >+ private IObservableMap masterMap; >+ >+ private IObservableFactory observableValueFactory; >+ >+ private Object detailValueType; >+ >+ private Set entrySet; >+ >+ private IMapChangeListener masterMapListener = new IMapChangeListener() { >+ public void handleMapChange(MapChangeEvent event) { >+ adaptMasterMapDiff(event.diff); >+ } >+ }; >+ >+ private Map detailObservableValueMap = new HashMap(); >+ >+ private IdentitySet staleDetailObservables = new IdentitySet(); >+ >+ private IStaleListener masterStaleListener = new IStaleListener() { >+ public void handleStale(StaleEvent staleEvent) { >+ fireStale(); >+ } >+ }; >+ >+ private IStaleListener detailStaleListener = new IStaleListener() { >+ public void handleStale(StaleEvent staleEvent) { >+ addStaleDetailObservable((IObservableValue) staleEvent >+ .getObservable()); >+ } >+ }; >+ >+ /** >+ * @param masterMap >+ * @param observableValueFactory >+ * @param detailValueType >+ */ >+ public MapDetailValueObservableMap(IObservableMap masterMap, >+ IObservableFactory observableValueFactory, Object detailValueType) { >+ super(masterMap.getRealm()); >+ this.masterMap = masterMap; >+ this.observableValueFactory = observableValueFactory; >+ this.detailValueType = detailValueType; >+ >+ // Add change/stale/dispose listeners on the master map. >+ masterMap.addMapChangeListener(masterMapListener); >+ masterMap.addStaleListener(masterStaleListener); >+ masterMap.addDisposeListener(new IDisposeListener() { >+ public void handleDispose(DisposeEvent event) { >+ MapDetailValueObservableMap.this.dispose(); >+ } >+ }); >+ >+ // Initialize the map with the current state of the master map. >+ MapDiff initMasterDiff = Diffs.computeMapDiff(Collections.EMPTY_MAP, >+ masterMap); >+ adaptMasterMapDiff(initMasterDiff); >+ } >+ >+ private void adaptMasterMapDiff(MapDiff diff) { >+ // Collect the detail values for the master values in the input diff. >+ IdentityMap oldValues = new IdentityMap(); >+ IdentityMap newValues = new IdentityMap(); >+ >+ // Handle the addition of master values. >+ Set addedKeys = diff.getAddedKeys(); >+ for (Iterator iter = addedKeys.iterator(); iter.hasNext();) { >+ Object addedKey = iter.next(); >+ >+ // For added master values, we set up a new detail observable. >+ hookListener(addedKey); >+ >+ // Get the value of the created detail observable for the new diff. >+ IObservableValue detailValue = getDetailObservableValue(addedKey); >+ newValues.put(addedKey, detailValue.getValue()); >+ } >+ >+ // Handle the removal of master values. >+ Set removedKeys = diff.getRemovedKeys(); >+ for (Iterator iter = removedKeys.iterator(); iter.hasNext();) { >+ Object removedKey = iter.next(); >+ >+ // First of all, get the current detail value and add it to the set >+ // of old values of the new diff. >+ IObservableValue detailValue = getDetailObservableValue(removedKey); >+ oldValues.put(removedKey, detailValue.getValue()); >+ >+ // For removed master values, we dispose the existing detail >+ // observable. >+ unhookListener(removedKey); >+ } >+ >+ // Handle changed master values. >+ Set changedKeys = diff.getChangedKeys(); >+ for (Iterator iter = changedKeys.iterator(); iter.hasNext();) { >+ Object changedKey = iter.next(); >+ >+ // Get the detail value prior to the change and add it to the set of >+ // old values of the new diff. >+ IObservableValue oldDetailValue = getDetailObservableValue(changedKey); >+ oldValues.put(changedKey, oldDetailValue.getValue()); >+ >+ // Remove the old detail value for the old master value and add it >+ // again for the new master value. >+ unhookListener(changedKey); >+ hookListener(changedKey); >+ >+ // Get the new detail value and add it to the set of new values. >+ IObservableValue newDetailValue = getDetailObservableValue(changedKey); >+ newValues.put(changedKey, newDetailValue.getValue()); >+ } >+ >+ // The different key sets are the same, only the values change. >+ fireMapChange(Diffs.createMapDiff(addedKeys, removedKeys, changedKeys, >+ oldValues, newValues)); >+ } >+ >+ private void hookListener(final Object addedKey) { >+ final IObservableValue detailValue = getDetailObservableValue(addedKey); >+ >+ detailValue.addValueChangeListener(new IValueChangeListener() { >+ public void handleValueChange(ValueChangeEvent event) { >+ if (!event.getObservableValue().isStale()) { >+ staleDetailObservables.remove(detailValue); >+ } >+ >+ fireMapChange(Diffs.createMapDiffSingleChange(addedKey, >+ event.diff.getOldValue(), event.diff.getNewValue())); >+ } >+ }); >+ >+ detailValue.addStaleListener(detailStaleListener); >+ } >+ >+ private void unhookListener(Object removedKey) { >+ if (isDisposed()) { >+ return; >+ } >+ >+ IObservableValue detailValue = (IObservableValue) detailObservableValueMap >+ .remove(removedKey); >+ staleDetailObservables.remove(detailValue); >+ detailValue.dispose(); >+ } >+ >+ private IObservableValue getDetailObservableValue(Object masterKey) { >+ IObservableValue detailValue = (IObservableValue) detailObservableValueMap >+ .get(masterKey); >+ >+ if (detailValue == null) { >+ ObservableTracker.setIgnore(true); >+ try { >+ detailValue = (IObservableValue) observableValueFactory >+ .createObservable(masterMap.get(masterKey)); >+ } finally { >+ ObservableTracker.setIgnore(false); >+ } >+ >+ detailObservableValueMap.put(masterKey, detailValue); >+ >+ if (detailValue.isStale()) { >+ addStaleDetailObservable(detailValue); >+ } >+ } >+ >+ return detailValue; >+ } >+ >+ private void addStaleDetailObservable(IObservableValue detailObservable) { >+ boolean wasStale = isStale(); >+ staleDetailObservables.add(detailObservable); >+ if (!wasStale) { >+ fireStale(); >+ } >+ } >+ >+ public Set keySet() { >+ getterCalled(); >+ >+ return masterMap.keySet(); >+ } >+ >+ public Object get(Object key) { >+ getterCalled(); >+ >+ if (!containsKey(key)) { >+ return null; >+ } >+ >+ IObservableValue detailValue = getDetailObservableValue(key); >+ return detailValue.getValue(); >+ } >+ >+ public Object put(Object key, Object value) { >+ if (!containsKey(key)) { >+ return null; >+ } >+ >+ IObservableValue detailValue = getDetailObservableValue(key); >+ Object oldValue = detailValue.getValue(); >+ detailValue.setValue(value); >+ return oldValue; >+ } >+ >+ public boolean containsKey(Object key) { >+ getterCalled(); >+ >+ return masterMap.containsKey(key); >+ } >+ >+ public Object remove(Object key) { >+ checkRealm(); >+ >+ if (!containsKey(key)) { >+ return null; >+ } >+ >+ IObservableValue detailValue = getDetailObservableValue(key); >+ Object oldValue = detailValue.getValue(); >+ >+ masterMap.remove(key); >+ >+ return oldValue; >+ } >+ >+ public int size() { >+ getterCalled(); >+ >+ return masterMap.size(); >+ } >+ >+ public boolean isStale() { >+ return super.isStale() >+ || (masterMap != null && masterMap.isStale()) >+ || (staleDetailObservables != null && !staleDetailObservables >+ .isEmpty()); >+ } >+ >+ public Object getKeyType() { >+ return masterMap.getKeyType(); >+ } >+ >+ public Object getValueType() { >+ return detailValueType; >+ } >+ >+ public Object getObserved() { >+ return masterMap; >+ } >+ >+ public synchronized void dispose() { >+ if (masterMap != null) { >+ masterMap.removeMapChangeListener(masterMapListener); >+ masterMap.removeStaleListener(masterStaleListener); >+ } >+ >+ if (detailObservableValueMap != null) { >+ for (Iterator iter = detailObservableValueMap.values().iterator(); iter >+ .hasNext();) { >+ IObservableValue detailValue = (IObservableValue) iter.next(); >+ detailValue.dispose(); >+ } >+ detailObservableValueMap.clear(); >+ } >+ >+ masterMap = null; >+ observableValueFactory = null; >+ detailValueType = null; >+ detailObservableValueMap = null; >+ masterStaleListener = null; >+ detailStaleListener = null; >+ staleDetailObservables = null; >+ >+ super.dispose(); >+ } >+ >+ public Set entrySet() { >+ getterCalled(); >+ >+ if (entrySet == null) { >+ entrySet = new EntrySet(); >+ } >+ return entrySet; >+ } >+ >+ private void getterCalled() { >+ ObservableTracker.getterCalled(this); >+ } >+ >+ private class EntrySet extends AbstractSet { >+ >+ public Iterator iterator() { >+ final Iterator keyIterator = keySet().iterator(); >+ return new Iterator() { >+ >+ public boolean hasNext() { >+ return keyIterator.hasNext(); >+ } >+ >+ public Object next() { >+ Object key = keyIterator.next(); >+ return new MapEntry(key); >+ } >+ >+ public void remove() { >+ keyIterator.remove(); >+ } >+ }; >+ } >+ >+ public int size() { >+ return MapDetailValueObservableMap.this.size(); >+ } >+ } >+ >+ private final class MapEntry implements Map.Entry { >+ >+ private final Object key; >+ >+ private MapEntry(Object key) { >+ this.key = key; >+ } >+ >+ public Object getKey() { >+ MapDetailValueObservableMap.this.getterCalled(); >+ return key; >+ } >+ >+ public Object getValue() { >+ return MapDetailValueObservableMap.this.get(getKey()); >+ } >+ >+ public Object setValue(Object value) { >+ return MapDetailValueObservableMap.this.put(getKey(), value); >+ } >+ >+ public boolean equals(Object o) { >+ MapDetailValueObservableMap.this.getterCalled(); >+ if (o == this) >+ return true; >+ if (o == null) >+ return false; >+ if (!(o instanceof Map.Entry)) >+ return false; >+ Map.Entry that = (Map.Entry) o; >+ return Util.equals(this.getKey(), that.getKey()) >+ && Util.equals(this.getValue(), that.getValue()); >+ } >+ >+ public int hashCode() { >+ MapDetailValueObservableMap.this.getterCalled(); >+ Object value = getValue(); >+ return (getKey() == null ? 0 : getKey().hashCode()) >+ ^ (value == null ? 0 : value.hashCode()); >+ } >+ } >+} >Index: src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java >diff -N src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,178 @@ >+/******************************************************************************* >+ * Copyright (c) 2010 Ovidio Mallo and others. >+ * All rights reserved. This program and the accompanying materials >+ * are made available under the terms of the Eclipse Public License v1.0 >+ * which accompanies this distribution, and is available at >+ * http://www.eclipse.org/legal/epl-v10.html >+ * >+ * Contributors: >+ * Ovidio Mallo - initial API and implementation (bug 305367) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.observable.masterdetail; >+ >+import java.util.HashMap; >+import java.util.Map; >+ >+import org.eclipse.core.databinding.observable.IObserving; >+import org.eclipse.core.databinding.observable.IStaleListener; >+import org.eclipse.core.databinding.observable.ObservableTracker; >+import org.eclipse.core.databinding.observable.StaleEvent; >+import org.eclipse.core.databinding.observable.map.ComputedObservableMap; >+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; >+import org.eclipse.core.databinding.observable.set.IObservableSet; >+import org.eclipse.core.databinding.observable.value.IObservableValue; >+import org.eclipse.core.databinding.observable.value.IValueChangeListener; >+import org.eclipse.core.databinding.observable.value.ValueChangeEvent; >+import org.eclipse.core.internal.databinding.identity.IdentitySet; >+ >+/** >+ * @since 1.3 >+ */ >+public class SetDetailValueObservableMap extends ComputedObservableMap >+ implements IObserving { >+ >+ private IObservableFactory observableValueFactory; >+ >+ private Map detailObservableValueMap = new HashMap(); >+ >+ private IdentitySet staleDetailObservables = new IdentitySet(); >+ >+ private IStaleListener detailStaleListener = new IStaleListener() { >+ public void handleStale(StaleEvent staleEvent) { >+ addStaleDetailObservable((IObservableValue) staleEvent >+ .getObservable()); >+ } >+ }; >+ >+ /** >+ * @param masterKeySet >+ * @param observableValueFactory >+ * @param detailValueType >+ */ >+ public SetDetailValueObservableMap(IObservableSet masterKeySet, >+ IObservableFactory observableValueFactory, Object detailValueType) { >+ super(masterKeySet, detailValueType); >+ this.observableValueFactory = observableValueFactory; >+ } >+ >+ protected void hookListener(final Object addedKey) { >+ final IObservableValue detailValue = getDetailObservableValue(addedKey); >+ >+ detailValue.addValueChangeListener(new IValueChangeListener() { >+ public void handleValueChange(ValueChangeEvent event) { >+ if (!event.getObservableValue().isStale()) { >+ staleDetailObservables.remove(detailValue); >+ } >+ >+ fireSingleChange(addedKey, event.diff.getOldValue(), event.diff >+ .getNewValue()); >+ } >+ }); >+ >+ detailValue.addStaleListener(detailStaleListener); >+ } >+ >+ protected void unhookListener(Object removedKey) { >+ if (isDisposed()) { >+ return; >+ } >+ >+ IObservableValue detailValue = (IObservableValue) detailObservableValueMap >+ .remove(removedKey); >+ staleDetailObservables.remove(detailValue); >+ detailValue.dispose(); >+ } >+ >+ private IObservableValue getDetailObservableValue(Object masterKey) { >+ IObservableValue detailValue = (IObservableValue) detailObservableValueMap >+ .get(masterKey); >+ >+ if (detailValue == null) { >+ ObservableTracker.setIgnore(true); >+ try { >+ detailValue = (IObservableValue) observableValueFactory >+ .createObservable(masterKey); >+ } finally { >+ ObservableTracker.setIgnore(false); >+ } >+ >+ detailObservableValueMap.put(masterKey, detailValue); >+ >+ if (detailValue.isStale()) { >+ addStaleDetailObservable(detailValue); >+ } >+ } >+ >+ return detailValue; >+ } >+ >+ private void addStaleDetailObservable(IObservableValue detailObservable) { >+ boolean wasStale = isStale(); >+ staleDetailObservables.add(detailObservable); >+ if (!wasStale) { >+ fireStale(); >+ } >+ } >+ >+ protected Object doGet(Object key) { >+ IObservableValue detailValue = getDetailObservableValue(key); >+ return detailValue.getValue(); >+ } >+ >+ protected Object doPut(Object key, Object value) { >+ IObservableValue detailValue = getDetailObservableValue(key); >+ Object oldValue = detailValue.getValue(); >+ detailValue.setValue(value); >+ return oldValue; >+ } >+ >+ public boolean containsKey(Object key) { >+ getterCalled(); >+ >+ return keySet().contains(key); >+ } >+ >+ public Object remove(Object key) { >+ checkRealm(); >+ >+ if (!containsKey(key)) { >+ return null; >+ } >+ >+ IObservableValue detailValue = getDetailObservableValue(key); >+ Object oldValue = detailValue.getValue(); >+ >+ keySet().remove(key); >+ >+ return oldValue; >+ } >+ >+ public int size() { >+ getterCalled(); >+ >+ return keySet().size(); >+ } >+ >+ public boolean isStale() { >+ return super.isStale() || staleDetailObservables != null >+ && !staleDetailObservables.isEmpty(); >+ } >+ >+ public Object getObserved() { >+ return keySet(); >+ } >+ >+ public synchronized void dispose() { >+ super.dispose(); >+ >+ observableValueFactory = null; >+ detailObservableValueMap = null; >+ detailStaleListener = null; >+ staleDetailObservables = null; >+ } >+ >+ private void getterCalled() { >+ ObservableTracker.getterCalled(this); >+ } >+} >#P org.eclipse.core.databinding.property >Index: META-INF/MANIFEST.MF >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.core.databinding.property/META-INF/MANIFEST.MF,v >retrieving revision 1.4 >diff -u -r1.4 MANIFEST.MF >--- META-INF/MANIFEST.MF 10 Mar 2010 07:20:10 -0000 1.4 >+++ META-INF/MANIFEST.MF 1 Jul 2010 17:46:45 -0000 >@@ -2,7 +2,7 @@ > Bundle-ManifestVersion: 2 > Bundle-Name: %pluginName > Bundle-SymbolicName: org.eclipse.core.databinding.property >-Bundle-Version: 1.3.0.qualifier >+Bundle-Version: 1.4.0.qualifier > Bundle-ClassPath: . > Bundle-Vendor: %providerName > Bundle-Localization: plugin >Index: src/org/eclipse/core/databinding/property/value/ValueProperty.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/ValueProperty.java,v >retrieving revision 1.5 >diff -u -r1.5 ValueProperty.java >--- src/org/eclipse/core/databinding/property/value/ValueProperty.java 1 Jun 2010 19:22:40 -0000 1.5 >+++ src/org/eclipse/core/databinding/property/value/ValueProperty.java 1 Jul 2010 17:46:45 -0000 >@@ -7,15 +7,19 @@ > * > * Contributors: > * Matthew Hall - initial API and implementation (bug 194734) >- * Matthew Hall - bug 195222 >+ * Matthew Hall - bugs 195222 >+ * Ovidio Mallo - bugs 305367 > ******************************************************************************/ > > package org.eclipse.core.databinding.property.value; > > import org.eclipse.core.databinding.observable.IObservable; > import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.list.IObservableList; >+import org.eclipse.core.databinding.observable.map.IObservableMap; > import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; > import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; >+import org.eclipse.core.databinding.observable.set.IObservableSet; > import org.eclipse.core.databinding.observable.value.IObservableValue; > import org.eclipse.core.databinding.property.list.IListProperty; > import org.eclipse.core.databinding.property.map.IMapProperty; >@@ -108,8 +112,32 @@ > } > > public IObservableValue observeDetail(IObservableValue master) { >- return MasterDetailObservables.detailValue(master, valueFactory(master >- .getRealm()), getValueType()); >+ return MasterDetailObservables.detailValue(master, >+ valueFactory(master.getRealm()), getValueType()); >+ } >+ >+ /** >+ * @since 1.4 >+ */ >+ public IObservableList observeDetail(IObservableList master) { >+ return MasterDetailObservables.detailListValues(master, >+ valueFactory(master.getRealm()), getValueType()); >+ } >+ >+ /** >+ * @since 1.4 >+ */ >+ public IObservableMap observeDetail(IObservableSet master) { >+ return MasterDetailObservables.detailSetValues(master, >+ valueFactory(master.getRealm()), getValueType()); >+ } >+ >+ /** >+ * @since 1.4 >+ */ >+ public IObservableMap observeDetail(IObservableMap master) { >+ return MasterDetailObservables.detailMapValues(master, >+ valueFactory(master.getRealm()), getValueType()); > } > > public final IValueProperty value(IValueProperty detailValue) { >#P org.eclipse.jface.tests.databinding >Index: src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailValueObservableListTest.java >=================================================================== >RCS file: src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailValueObservableListTest.java >diff -N src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailValueObservableListTest.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailValueObservableListTest.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,351 @@ >+/******************************************************************************* >+ * Copyright (c) 2010 Ovidio Mallo and others. >+ * All rights reserved. This program and the accompanying materials >+ * are made available under the terms of the Eclipse Public License v1.0 >+ * which accompanies this distribution, and is available at >+ * http://www.eclipse.org/legal/epl-v10.html >+ * >+ * Contributors: >+ * Ovidio Mallo - initial API and implementation (bug 305367) >+ ******************************************************************************/ >+ >+package org.eclipse.core.tests.internal.databinding.observable.masterdetail; >+ >+import java.util.ArrayList; >+import java.util.Collections; >+import java.util.List; >+ >+import junit.framework.Test; >+import junit.framework.TestSuite; >+ >+import org.eclipse.core.databinding.beans.BeansObservables; >+import org.eclipse.core.databinding.observable.IObservable; >+import org.eclipse.core.databinding.observable.IObservableCollection; >+import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.list.IObservableList; >+import org.eclipse.core.databinding.observable.list.ListDiff; >+import org.eclipse.core.databinding.observable.list.ListDiffEntry; >+import org.eclipse.core.databinding.observable.list.WritableList; >+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; >+import org.eclipse.core.databinding.observable.value.WritableValue; >+import org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailValueObservableList; >+import org.eclipse.jface.databinding.conformance.ObservableListContractTest; >+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate; >+import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker; >+import org.eclipse.jface.examples.databinding.model.SimplePerson; >+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase; >+ >+/** >+ * @since 1.3 >+ */ >+public class ListDetailValueObservableListTest extends >+ AbstractDefaultRealmTestCase { >+ >+ public static Test suite() { >+ TestSuite suite = new TestSuite(ListDetailValueObservableListTest.class >+ .getName()); >+ suite.addTestSuite(ListDetailValueObservableListTest.class); >+ suite.addTest(ObservableListContractTest.suite(new Delegate())); >+ return suite; >+ } >+ >+ public void testUnmodifiability() { >+ WritableList masterObservableList = new WritableList(); >+ masterObservableList.add(new SimplePerson()); >+ masterObservableList.add(new SimplePerson()); >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ masterObservableList, BeansObservables.valueFactory("name"), >+ null); >+ >+ try { >+ ldol.add("name"); >+ fail("ListDetailValueObservableList must not be modifiable."); >+ } catch (UnsupportedOperationException e) { >+ // expected exception >+ } >+ >+ try { >+ ldol.remove(masterObservableList.get(0)); >+ fail("ListDetailValueObservableList must not be modifiable."); >+ } catch (UnsupportedOperationException e) { >+ // expected exception >+ } >+ >+ try { >+ ldol.removeAll(Collections.singleton(masterObservableList.get(0))); >+ fail("ListDetailValueObservableList must not be modifiable."); >+ } catch (UnsupportedOperationException e) { >+ // expected exception >+ } >+ >+ try { >+ ldol.retainAll(Collections.EMPTY_LIST); >+ fail("ListDetailValueObservableList must not be modifiable."); >+ } catch (UnsupportedOperationException e) { >+ // expected exception >+ } >+ >+ try { >+ ldol.move(0, 1); >+ fail("ListDetailValueObservableList must not be modifiable."); >+ } catch (UnsupportedOperationException e) { >+ // expected exception >+ } >+ } >+ >+ public void testGetElementType() { >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ new WritableList(), BeansObservables.valueFactory("name"), >+ String.class); >+ >+ assertSame(String.class, ldol.getElementType()); >+ } >+ >+ public void testGetObserved() { >+ WritableList masterList = new WritableList(); >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ masterList, BeansObservables.valueFactory("name"), String.class); >+ >+ // The observed object is the master list. >+ assertSame(masterList, ldol.getObserved()); >+ } >+ >+ public void testMasterListInitiallyNotEmpty() { >+ WritableList masterList = new WritableList(); >+ SimplePerson person = new SimplePerson(); >+ person.setName("name"); >+ masterList.add(person); >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ masterList, BeansObservables.valueFactory("name"), String.class); >+ >+ // Make sure that a non-empty master list is initialized correctly. >+ assertEquals(masterList.size(), ldol.size()); >+ assertEquals(person.getName(), ldol.get(0)); >+ } >+ >+ public void testAddRemove() { >+ WritableList masterList = new WritableList(); >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ masterList, BeansObservables.valueFactory("name"), String.class); >+ >+ // Initially, the detail list is empty. >+ assertTrue(ldol.isEmpty()); >+ >+ // Add a first person and check that its name is in the detail list. >+ SimplePerson p1 = new SimplePerson(); >+ p1.setName("name1"); >+ masterList.add(p1); >+ assertEquals(masterList.size(), ldol.size()); >+ assertEquals(p1.getName(), ldol.get(0)); >+ >+ // Add a second person and check that it's name is in the detail list. >+ SimplePerson p2 = new SimplePerson(); >+ p2.setName("name2"); >+ masterList.add(p2); >+ assertEquals(masterList.size(), ldol.size()); >+ assertEquals(p2.getName(), ldol.get(1)); >+ >+ // Remove the first person from the master list and check that we still >+ // have the name of the second person in the detail list. >+ masterList.remove(0); >+ assertEquals(masterList.size(), ldol.size()); >+ assertEquals(p2.getName(), ldol.get(0)); >+ >+ // Remove the second person as well. >+ masterList.remove(0); >+ assertTrue(ldol.isEmpty()); >+ } >+ >+ public void testChangeDetail() { >+ WritableList masterList = new WritableList(); >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ masterList, BeansObservables.valueFactory("name"), String.class); >+ >+ // Change the detail attribute explicitly. >+ SimplePerson p1 = new SimplePerson(); >+ p1.setName("name1"); >+ masterList.add(p1); >+ assertEquals(p1.getName(), ldol.get(0)); >+ p1.setName("name2"); >+ assertEquals(p1.getName(), ldol.get(0)); >+ >+ // Change the detail attribute by changing the master. >+ SimplePerson p2 = new SimplePerson(); >+ p2.setName("name3"); >+ masterList.set(0, p2); >+ assertEquals(p2.getName(), ldol.get(0)); >+ } >+ >+ public void testSet() { >+ WritableList masterList = new WritableList(); >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ masterList, BeansObservables.valueFactory("name"), String.class); >+ >+ // Change the detail attribute explicitly. >+ SimplePerson person = new SimplePerson(); >+ person.setName("name1"); >+ masterList.add(person); >+ assertEquals(person.getName(), ldol.get(0)); >+ >+ // Set a new name on the detail list. >+ ldol.set(0, "name2"); >+ // Check that the name has been propagated to the master. >+ assertEquals("name2", person.getName()); >+ assertEquals(person.getName(), ldol.get(0)); >+ } >+ >+ public void testDetailObservableChangeEvent() { >+ WritableList masterList = new WritableList(); >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ masterList, BeansObservables.valueFactory("name"), String.class); >+ >+ ListChangeEventTracker changeTracker = ListChangeEventTracker >+ .observe(ldol); >+ >+ SimplePerson person = new SimplePerson(); >+ person.setName("old name"); >+ >+ // Initially, we should not have received any event. >+ assertEquals(0, changeTracker.count); >+ >+ // Add the person and check that we receive an addition event on the >+ // correct index and with the correct value. >+ masterList.add(person); >+ assertEquals(1, changeTracker.count); >+ assertEquals(1, changeTracker.event.diff.getDifferences().length); >+ assertTrue(changeTracker.event.diff.getDifferences()[0].isAddition()); >+ assertEquals(0, changeTracker.event.diff.getDifferences()[0] >+ .getPosition()); >+ assertEquals(person.getName(), changeTracker.event.diff >+ .getDifferences()[0].getElement()); >+ >+ // Change the detail property and check that we receive a replace event. >+ person.setName("new name"); >+ assertEquals(2, changeTracker.count); >+ assertIsSingleReplaceDiff(changeTracker.event.diff, 0, "old name", >+ "new name"); >+ } >+ >+ private void assertIsSingleReplaceDiff(ListDiff diff, int index, >+ Object oldElement, Object newElement) { >+ // We should have 2 diff entries. >+ assertEquals(2, diff.getDifferences().length); >+ >+ ListDiffEntry entry1 = diff.getDifferences()[0]; >+ ListDiffEntry entry2 = diff.getDifferences()[1]; >+ >+ // One diff entry must be an addition, the other a removal. >+ assertTrue(entry1.isAddition() != entry2.isAddition()); >+ >+ // Check for the index on the diff entries. >+ assertEquals(index, entry1.getPosition()); >+ assertEquals(index, entry2.getPosition()); >+ >+ // Check for the old/new element values on both diff entries. >+ if (entry1.isAddition()) { >+ assertEquals(oldElement, entry2.getElement()); >+ assertEquals(newElement, entry1.getElement()); >+ } else { >+ assertEquals(oldElement, entry1.getElement()); >+ assertEquals(newElement, entry2.getElement()); >+ } >+ } >+ >+ public void testMasterNull() { >+ WritableList masterObservableList = new WritableList(); >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ masterObservableList, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ // Make sure null values are handled gracefully. >+ masterObservableList.add(null); >+ assertEquals(1, ldol.size()); >+ assertNull(ldol.get(0)); >+ } >+ >+ public void testDetailObservableValuesAreDisposed() { >+ final List detailObservables = new ArrayList(); >+ IObservableFactory detailValueFactory = new IObservableFactory() { >+ public IObservable createObservable(Object target) { >+ WritableValue detailObservable = new WritableValue(); >+ // Remember the created observables. >+ detailObservables.add(detailObservable); >+ return detailObservable; >+ } >+ }; >+ >+ WritableList masterList = new WritableList(); >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ masterList, detailValueFactory, null); >+ >+ masterList.add(new Object()); >+ masterList.add(new Object()); >+ >+ assertEquals(ldol.size(), detailObservables.size()); >+ >+ // No detail observables should be disposed yet. >+ assertFalse(((WritableValue) detailObservables.get(0)).isDisposed()); >+ assertFalse(((WritableValue) detailObservables.get(1)).isDisposed()); >+ >+ // Only the detail observable for the removed master should be disposed. >+ masterList.remove(1); >+ assertFalse(((WritableValue) detailObservables.get(0)).isDisposed()); >+ assertTrue(((WritableValue) detailObservables.get(1)).isDisposed()); >+ >+ // After disposing the detail list, all detail observables should be >+ // disposed. >+ ldol.dispose(); >+ assertTrue(((WritableValue) detailObservables.get(0)).isDisposed()); >+ assertTrue(((WritableValue) detailObservables.get(1)).isDisposed()); >+ } >+ >+ public void testDisposeOnMasterDisposed() { >+ WritableList masterList = new WritableList(); >+ ListDetailValueObservableList ldol = new ListDetailValueObservableList( >+ masterList, BeansObservables.valueFactory("name"), String.class); >+ >+ // Initially, nothing should be disposed. >+ assertFalse(masterList.isDisposed()); >+ assertFalse(ldol.isDisposed()); >+ >+ // Upon disposing the master list, the detail list should be disposed as >+ // well. >+ masterList.dispose(); >+ assertTrue(masterList.isDisposed()); >+ assertTrue(ldol.isDisposed()); >+ } >+ >+ private static class Delegate extends >+ AbstractObservableCollectionContractDelegate { >+ public IObservableCollection createObservableCollection(Realm realm, >+ int elementCount) { >+ WritableList masterList = new WritableList(realm); >+ for (int i = 0; i < elementCount; i++) { >+ masterList.add(new SimplePerson()); >+ } >+ >+ return new TestListDetailValueObservableList(masterList, >+ BeansObservables.valueFactory(realm, "name"), String.class); >+ } >+ >+ public void change(IObservable observable) { >+ TestListDetailValueObservableList ldol = (TestListDetailValueObservableList) observable; >+ ldol.masterList.add(new SimplePerson()); >+ } >+ >+ public Object getElementType(IObservableCollection collection) { >+ return String.class; >+ } >+ } >+ >+ private static class TestListDetailValueObservableList extends >+ ListDetailValueObservableList { >+ final IObservableList masterList; >+ >+ public TestListDetailValueObservableList(IObservableList masterList, >+ IObservableFactory detailValueFactory, Object detailType) { >+ super(masterList, detailValueFactory, detailType); >+ this.masterList = masterList; >+ } >+ } >+} >Index: src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/MapDetailValueObservableMapTest.java >=================================================================== >RCS file: src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/MapDetailValueObservableMapTest.java >diff -N src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/MapDetailValueObservableMapTest.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/MapDetailValueObservableMapTest.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,309 @@ >+/******************************************************************************* >+ * Copyright (c) 2010 Ovidio Mallo and others. >+ * All rights reserved. This program and the accompanying materials >+ * are made available under the terms of the Eclipse Public License v1.0 >+ * which accompanies this distribution, and is available at >+ * http://www.eclipse.org/legal/epl-v10.html >+ * >+ * Contributors: >+ * Ovidio Mallo - initial API and implementation (bug 305367) >+ ******************************************************************************/ >+ >+package org.eclipse.core.tests.internal.databinding.observable.masterdetail; >+ >+import java.util.HashMap; >+import java.util.Map; >+ >+import junit.framework.Test; >+import junit.framework.TestSuite; >+ >+import org.eclipse.core.databinding.beans.BeansObservables; >+import org.eclipse.core.databinding.observable.IObservable; >+import org.eclipse.core.databinding.observable.map.WritableMap; >+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; >+import org.eclipse.core.databinding.observable.value.WritableValue; >+import org.eclipse.core.internal.databinding.observable.masterdetail.MapDetailValueObservableMap; >+import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker; >+import org.eclipse.jface.examples.databinding.model.SimplePerson; >+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase; >+ >+/** >+ * @since 1.3 >+ */ >+public class MapDetailValueObservableMapTest extends >+ AbstractDefaultRealmTestCase { >+ >+ public static Test suite() { >+ TestSuite suite = new TestSuite(MapDetailValueObservableMapTest.class >+ .getName()); >+ suite.addTestSuite(MapDetailValueObservableMapTest.class); >+ return suite; >+ } >+ >+ public void testGetKeyType() { >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ new WritableMap(SimplePerson.class, SimplePerson.class), >+ BeansObservables.valueFactory("name"), String.class); >+ >+ assertSame(SimplePerson.class, mdom.getKeyType()); >+ } >+ >+ public void testGetValueType() { >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ new WritableMap(), BeansObservables.valueFactory("name"), >+ String.class); >+ >+ assertSame(String.class, mdom.getValueType()); >+ } >+ >+ public void testGetObserved() { >+ WritableMap masterMap = new WritableMap(); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, BeansObservables.valueFactory("name"), String.class); >+ >+ // The observed object is the master key set. >+ assertSame(masterMap, mdom.getObserved()); >+ } >+ >+ public void testMasterSetInitiallyNotEmpty() { >+ WritableMap masterMap = new WritableMap(); >+ SimplePerson person = new SimplePerson(); >+ person.setName("name"); >+ masterMap.put(person, person); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, BeansObservables.valueFactory("name"), String.class); >+ >+ // Make sure that a non-empty master key set is initialized correctly. >+ assertEquals(masterMap.size(), mdom.size()); >+ assertEquals(person.getName(), mdom.get(person)); >+ } >+ >+ public void testAddRemove() { >+ WritableMap masterMap = new WritableMap(); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, BeansObservables.valueFactory("name"), String.class); >+ >+ // Initially, the detail map is empty. >+ assertTrue(mdom.isEmpty()); >+ >+ // Add a first person and check that its name is in the detail list. >+ SimplePerson p1 = new SimplePerson(); >+ p1.setName("name1"); >+ masterMap.put(p1, p1); >+ assertEquals(masterMap.size(), mdom.size()); >+ assertEquals(p1.getName(), mdom.get(p1)); >+ >+ // Add a second person and check that it's name is in the detail list. >+ SimplePerson p2 = new SimplePerson(); >+ p2.setName("name2"); >+ masterMap.put(p2, p2); >+ assertEquals(masterMap.size(), mdom.size()); >+ assertEquals(p2.getName(), mdom.get(p2)); >+ >+ // Remove the first person from the master list and check that we still >+ // have the name of the second person in the detail list. >+ masterMap.remove(p1); >+ assertEquals(masterMap.size(), mdom.size()); >+ assertEquals(p2.getName(), mdom.get(p2)); >+ >+ // Remove the second person as well. >+ masterMap.remove(p2); >+ assertTrue(mdom.isEmpty()); >+ } >+ >+ public void testChangeDetail() { >+ WritableMap masterMap = new WritableMap(); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, BeansObservables.valueFactory("name"), String.class); >+ >+ // Change the detail attribute explicitly. >+ SimplePerson p1 = new SimplePerson(); >+ p1.setName("name1"); >+ masterMap.put(p1, p1); >+ assertEquals(p1.getName(), mdom.get(p1)); >+ p1.setName("name2"); >+ assertEquals(p1.getName(), mdom.get(p1)); >+ >+ // Change the detail attribute by changing the master. >+ SimplePerson p2 = new SimplePerson(); >+ p2.setName("name3"); >+ masterMap.put(p1, p2); >+ assertEquals(p2.getName(), mdom.get(p1)); >+ } >+ >+ public void testPut() { >+ WritableMap masterMap = new WritableMap(); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, BeansObservables.valueFactory("name"), String.class); >+ >+ // Change the detail attribute explicitly. >+ SimplePerson person = new SimplePerson(); >+ person.setName("name1"); >+ masterMap.put(person, person); >+ assertEquals(person.getName(), mdom.get(person)); >+ >+ // Set a new name on the detail map. >+ mdom.put(person, "name2"); >+ // Check that the name has been propagated to the master. >+ assertEquals("name2", person.getName()); >+ assertEquals(person.getName(), mdom.get(person)); >+ } >+ >+ public void testContainsValue() { >+ WritableMap masterMap = new WritableMap(); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, BeansObservables.valueFactory("name"), String.class); >+ >+ // Add a person with a given name. >+ SimplePerson person = new SimplePerson(); >+ person.setName("name"); >+ masterMap.put(person, person); >+ >+ // Make sure the name of the person is contained. >+ assertTrue(mdom.containsValue(person.getName())); >+ >+ // Remove the person and make sure that it's name cannot be found >+ // anymore. >+ masterMap.remove(person); >+ assertFalse(mdom.containsValue(person.getName())); >+ } >+ >+ public void testRemove() { >+ WritableMap masterMap = new WritableMap(); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, BeansObservables.valueFactory("name"), String.class); >+ >+ // Add two person objects to the map. >+ SimplePerson p1 = new SimplePerson(); >+ SimplePerson p2 = new SimplePerson(); >+ masterMap.put(p1, p1); >+ masterMap.put(p2, p2); >+ >+ // Initially, both person objects should be contained in the detail map. >+ assertTrue(mdom.containsKey(p1)); >+ assertTrue(mdom.containsKey(p2)); >+ >+ // Remove one person and check that it is not contained anymore. >+ mdom.remove(p1); >+ assertFalse(mdom.containsKey(p1)); >+ assertTrue(mdom.containsKey(p2)); >+ >+ // Trying to remove a non-existent is allowed but has no effect. >+ mdom.remove(p1); >+ assertFalse(mdom.containsKey(p1)); >+ assertTrue(mdom.containsKey(p2)); >+ } >+ >+ public void testDetailObservableChangeEvent() { >+ WritableMap masterMap = new WritableMap(); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, BeansObservables.valueFactory("name"), String.class); >+ >+ MapChangeEventTracker changeTracker = MapChangeEventTracker >+ .observe(mdom); >+ >+ SimplePerson person = new SimplePerson(); >+ person.setName("old name"); >+ >+ // Initially, we should not have received any event. >+ assertEquals(0, changeTracker.count); >+ >+ // Add the person and check that we receive an addition event on the >+ // correct index and with the correct value. >+ masterMap.put(person, person); >+ assertEquals(1, changeTracker.count); >+ assertEquals(1, changeTracker.event.diff.getAddedKeys().size()); >+ assertEquals(0, changeTracker.event.diff.getRemovedKeys().size()); >+ assertEquals(0, changeTracker.event.diff.getChangedKeys().size()); >+ assertSame(person, changeTracker.event.diff.getAddedKeys().iterator() >+ .next()); >+ assertNull(changeTracker.event.diff.getOldValue(person)); >+ assertEquals("old name", changeTracker.event.diff.getNewValue(person)); >+ >+ // Change the detail property and check that we receive a replace >+ person.setName("new name"); >+ assertEquals(2, changeTracker.count); >+ assertEquals(0, changeTracker.event.diff.getAddedKeys().size()); >+ assertEquals(0, changeTracker.event.diff.getRemovedKeys().size()); >+ assertEquals(1, changeTracker.event.diff.getChangedKeys().size()); >+ assertSame(person, changeTracker.event.diff.getChangedKeys().iterator() >+ .next()); >+ assertEquals("old name", changeTracker.event.diff.getOldValue(person)); >+ assertEquals("new name", changeTracker.event.diff.getNewValue(person)); >+ } >+ >+ public void testMasterNull() { >+ WritableMap masterMap = new WritableMap(); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, BeansObservables.valueFactory("name"), String.class); >+ >+ // Make sure null values are handled gracefully. >+ masterMap.put(null, null); >+ assertEquals(1, mdom.size()); >+ assertNull(mdom.get(null)); >+ } >+ >+ public void testDetailObservableValuesAreDisposed() { >+ final Map detailObservables = new HashMap(); >+ IObservableFactory detailValueFactory = new IObservableFactory() { >+ public IObservable createObservable(Object target) { >+ WritableValue detailObservable = new WritableValue(); >+ // Remember the created observables. >+ detailObservables.put(target, detailObservable); >+ return detailObservable; >+ } >+ }; >+ >+ WritableMap masterMap = new WritableMap(); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, detailValueFactory, null); >+ >+ Object master1 = new Object(); >+ Object master2 = new Object(); >+ masterMap.put(master1, master1); >+ masterMap.put(master2, master2); >+ >+ // Attach a listener in order to ensure that all detail observables are >+ // actually created. >+ MapChangeEventTracker.observe(mdom); >+ >+ assertEquals(mdom.size(), detailObservables.size()); >+ >+ // No detail observables should be disposed yet. >+ assertFalse(((WritableValue) detailObservables.get(master1)) >+ .isDisposed()); >+ assertFalse(((WritableValue) detailObservables.get(master2)) >+ .isDisposed()); >+ >+ // Only the detail observable for the removed master should be disposed. >+ masterMap.remove(master2); >+ assertFalse(((WritableValue) detailObservables.get(master1)) >+ .isDisposed()); >+ assertTrue(((WritableValue) detailObservables.get(master2)) >+ .isDisposed()); >+ >+ // After disposing the detail map, all detail observables should be >+ // disposed. >+ mdom.dispose(); >+ assertTrue(((WritableValue) detailObservables.get(master1)) >+ .isDisposed()); >+ assertTrue(((WritableValue) detailObservables.get(master2)) >+ .isDisposed()); >+ } >+ >+ public void testDisposeOnMasterDisposed() { >+ WritableMap masterMap = new WritableMap(); >+ MapDetailValueObservableMap mdom = new MapDetailValueObservableMap( >+ masterMap, BeansObservables.valueFactory("name"), String.class); >+ >+ // Initially, nothing should be disposed. >+ assertFalse(masterMap.isDisposed()); >+ assertFalse(mdom.isDisposed()); >+ >+ // Upon disposing the master list, the detail list should be disposed as >+ // well. >+ masterMap.dispose(); >+ assertTrue(masterMap.isDisposed()); >+ assertTrue(mdom.isDisposed()); >+ } >+} >Index: src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/SetDetailValueObservableMapTest.java >=================================================================== >RCS file: src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/SetDetailValueObservableMapTest.java >diff -N src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/SetDetailValueObservableMapTest.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/SetDetailValueObservableMapTest.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,311 @@ >+/******************************************************************************* >+ * Copyright (c) 2010 Ovidio Mallo and others. >+ * All rights reserved. This program and the accompanying materials >+ * are made available under the terms of the Eclipse Public License v1.0 >+ * which accompanies this distribution, and is available at >+ * http://www.eclipse.org/legal/epl-v10.html >+ * >+ * Contributors: >+ * Ovidio Mallo - initial API and implementation (bug 305367) >+ ******************************************************************************/ >+ >+package org.eclipse.core.tests.internal.databinding.observable.masterdetail; >+ >+import java.util.HashMap; >+import java.util.Map; >+ >+import junit.framework.Test; >+import junit.framework.TestSuite; >+ >+import org.eclipse.core.databinding.beans.BeansObservables; >+import org.eclipse.core.databinding.observable.IObservable; >+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; >+import org.eclipse.core.databinding.observable.set.WritableSet; >+import org.eclipse.core.databinding.observable.value.WritableValue; >+import org.eclipse.core.internal.databinding.observable.masterdetail.SetDetailValueObservableMap; >+import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker; >+import org.eclipse.jface.examples.databinding.model.SimplePerson; >+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase; >+ >+/** >+ * @since 1.3 >+ */ >+public class SetDetailValueObservableMapTest extends >+ AbstractDefaultRealmTestCase { >+ >+ public static Test suite() { >+ TestSuite suite = new TestSuite(SetDetailValueObservableMapTest.class >+ .getName()); >+ suite.addTestSuite(SetDetailValueObservableMapTest.class); >+ return suite; >+ } >+ >+ public void testGetValueType() { >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ new WritableSet(), BeansObservables.valueFactory("name"), >+ String.class); >+ >+ assertSame(String.class, sdom.getValueType()); >+ } >+ >+ public void testGetObserved() { >+ WritableSet masterKeySet = new WritableSet(); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ // The observed object is the master key set. >+ assertSame(masterKeySet, sdom.getObserved()); >+ } >+ >+ public void testMasterSetInitiallyNotEmpty() { >+ WritableSet masterKeySet = new WritableSet(); >+ SimplePerson person = new SimplePerson(); >+ person.setName("name"); >+ masterKeySet.add(person); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ // Make sure that a non-empty master key set is initialized correctly. >+ assertEquals(masterKeySet.size(), sdom.size()); >+ assertEquals(person.getName(), sdom.get(person)); >+ } >+ >+ public void testAddRemove() { >+ WritableSet masterKeySet = new WritableSet(); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ // Initially, the detail map is empty. >+ assertTrue(sdom.isEmpty()); >+ >+ // Add a first person and check that its name is in the detail list. >+ SimplePerson p1 = new SimplePerson(); >+ p1.setName("name1"); >+ masterKeySet.add(p1); >+ assertEquals(masterKeySet.size(), sdom.size()); >+ assertEquals(p1.getName(), sdom.get(p1)); >+ >+ // Add a second person and check that it's name is in the detail list. >+ SimplePerson p2 = new SimplePerson(); >+ p2.setName("name2"); >+ masterKeySet.add(p2); >+ assertEquals(masterKeySet.size(), sdom.size()); >+ assertEquals(p2.getName(), sdom.get(p2)); >+ >+ // Remove the first person from the master list and check that we still >+ // have the name of the second person in the detail list. >+ masterKeySet.remove(p1); >+ assertEquals(masterKeySet.size(), sdom.size()); >+ assertEquals(p2.getName(), sdom.get(p2)); >+ >+ // Remove the second person as well. >+ masterKeySet.remove(p2); >+ assertTrue(sdom.isEmpty()); >+ } >+ >+ public void testChangeDetail() { >+ WritableSet masterKeySet = new WritableSet(); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ // Change the detail attribute explicitly. >+ SimplePerson p1 = new SimplePerson(); >+ p1.setName("name1"); >+ masterKeySet.add(p1); >+ assertEquals(p1.getName(), sdom.get(p1)); >+ p1.setName("name2"); >+ assertEquals(p1.getName(), sdom.get(p1)); >+ >+ // Change the detail attribute by changing the master. >+ SimplePerson p2 = new SimplePerson(); >+ p2.setName("name3"); >+ masterKeySet.add(p2); >+ assertEquals(p2.getName(), sdom.get(p2)); >+ } >+ >+ public void testPut() { >+ WritableSet masterKeySet = new WritableSet(); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ // Change the detail attribute explicitly. >+ SimplePerson person = new SimplePerson(); >+ person.setName("name1"); >+ masterKeySet.add(person); >+ assertEquals(person.getName(), sdom.get(person)); >+ >+ // Set a new name on the detail map. >+ sdom.put(person, "name2"); >+ // Check that the name has been propagated to the master. >+ assertEquals("name2", person.getName()); >+ assertEquals(person.getName(), sdom.get(person)); >+ } >+ >+ public void testContainsValue() { >+ WritableSet masterKeySet = new WritableSet(); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ // Add a person with a given name. >+ SimplePerson person = new SimplePerson(); >+ person.setName("name"); >+ masterKeySet.add(person); >+ >+ // Make sure the name of the person is contained. >+ assertTrue(sdom.containsValue(person.getName())); >+ >+ // Remove the person and make sure that it's name cannot be found >+ // anymore. >+ masterKeySet.remove(person); >+ assertFalse(sdom.containsValue(person.getName())); >+ } >+ >+ public void testRemove() { >+ WritableSet masterKeySet = new WritableSet(); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ // Add two person objects to the map. >+ SimplePerson p1 = new SimplePerson(); >+ SimplePerson p2 = new SimplePerson(); >+ masterKeySet.add(p1); >+ masterKeySet.add(p2); >+ >+ // Initially, both person objects should be contained in the detail map. >+ assertTrue(sdom.containsKey(p1)); >+ assertTrue(sdom.containsKey(p2)); >+ >+ // Remove one person and check that it is not contained anymore. >+ sdom.remove(p1); >+ assertFalse(sdom.containsKey(p1)); >+ assertTrue(sdom.containsKey(p2)); >+ >+ // Trying to remove a non-existent is allowed but has no effect. >+ sdom.remove(p1); >+ assertFalse(sdom.containsKey(p1)); >+ assertTrue(sdom.containsKey(p2)); >+ } >+ >+ public void testDetailObservableChangeEvent() { >+ WritableSet masterKeySet = new WritableSet(); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ MapChangeEventTracker changeTracker = MapChangeEventTracker >+ .observe(sdom); >+ >+ SimplePerson person = new SimplePerson(); >+ person.setName("old name"); >+ >+ // Initially, we should not have received any event. >+ assertEquals(0, changeTracker.count); >+ >+ // Add the person and check that we receive an addition event on the >+ // correct index and with the correct value. >+ masterKeySet.add(person); >+ assertEquals(1, changeTracker.count); >+ assertEquals(1, changeTracker.event.diff.getAddedKeys().size()); >+ assertEquals(0, changeTracker.event.diff.getRemovedKeys().size()); >+ assertEquals(0, changeTracker.event.diff.getChangedKeys().size()); >+ assertSame(person, changeTracker.event.diff.getAddedKeys().iterator() >+ .next()); >+ assertNull(changeTracker.event.diff.getOldValue(person)); >+ assertEquals("old name", changeTracker.event.diff.getNewValue(person)); >+ >+ // Change the detail property and check that we receive a replace >+ person.setName("new name"); >+ assertEquals(2, changeTracker.count); >+ assertEquals(0, changeTracker.event.diff.getAddedKeys().size()); >+ assertEquals(0, changeTracker.event.diff.getRemovedKeys().size()); >+ assertEquals(1, changeTracker.event.diff.getChangedKeys().size()); >+ assertSame(person, changeTracker.event.diff.getChangedKeys().iterator() >+ .next()); >+ assertEquals("old name", changeTracker.event.diff.getOldValue(person)); >+ assertEquals("new name", changeTracker.event.diff.getNewValue(person)); >+ } >+ >+ public void testMasterNull() { >+ WritableSet masterKeySet = new WritableSet(); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ // Make sure null values are handled gracefully. >+ masterKeySet.add(null); >+ assertEquals(1, sdom.size()); >+ assertNull(sdom.get(null)); >+ } >+ >+ public void testDetailObservableValuesAreDisposed() { >+ final Map detailObservables = new HashMap(); >+ IObservableFactory detailValueFactory = new IObservableFactory() { >+ public IObservable createObservable(Object target) { >+ WritableValue detailObservable = new WritableValue(); >+ // Remember the created observables. >+ detailObservables.put(target, detailObservable); >+ return detailObservable; >+ } >+ }; >+ >+ WritableSet masterKeySet = new WritableSet(); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, detailValueFactory, null); >+ >+ Object master1 = new Object(); >+ Object master2 = new Object(); >+ masterKeySet.add(master1); >+ masterKeySet.add(master2); >+ >+ // Attach a listener in order to ensure that all detail observables are >+ // actually created. >+ MapChangeEventTracker.observe(sdom); >+ >+ assertEquals(sdom.size(), detailObservables.size()); >+ >+ // No detail observables should be disposed yet. >+ assertFalse(((WritableValue) detailObservables.get(master1)) >+ .isDisposed()); >+ assertFalse(((WritableValue) detailObservables.get(master2)) >+ .isDisposed()); >+ >+ // Only the detail observable for the removed master should be disposed. >+ masterKeySet.remove(master2); >+ assertFalse(((WritableValue) detailObservables.get(master1)) >+ .isDisposed()); >+ assertTrue(((WritableValue) detailObservables.get(master2)) >+ .isDisposed()); >+ >+ // After disposing the detail map, all detail observables should be >+ // disposed. >+ sdom.dispose(); >+ assertTrue(((WritableValue) detailObservables.get(master1)) >+ .isDisposed()); >+ assertTrue(((WritableValue) detailObservables.get(master2)) >+ .isDisposed()); >+ } >+ >+ public void testDisposeOnMasterDisposed() { >+ WritableSet masterKeySet = new WritableSet(); >+ SetDetailValueObservableMap sdom = new SetDetailValueObservableMap( >+ masterKeySet, BeansObservables.valueFactory("name"), >+ String.class); >+ >+ // Initially, nothing should be disposed. >+ assertFalse(masterKeySet.isDisposed()); >+ assertFalse(sdom.isDisposed()); >+ >+ // Upon disposing the master list, the detail list should be disposed as >+ // well. >+ masterKeySet.dispose(); >+ assertTrue(masterKeySet.isDisposed()); >+ assertTrue(sdom.isDisposed()); >+ } >+} >Index: src/org/eclipse/jface/tests/databinding/BindingTestSuite.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/BindingTestSuite.java,v >retrieving revision 1.122 >diff -u -r1.122 BindingTestSuite.java >--- src/org/eclipse/jface/tests/databinding/BindingTestSuite.java 1 Jun 2010 19:22:39 -0000 1.122 >+++ src/org/eclipse/jface/tests/databinding/BindingTestSuite.java 1 Jul 2010 17:46:46 -0000 >@@ -17,7 +17,7 @@ > * 246103, 249992, 256150, 256543, 262269, 175735, 262946, > * 255734, 263693, 169876, 266038, 268336, 270461, 271720, > * 283204, 281723, 283428 >- * Ovidio Mallo - bugs 237163, 235195, 299619 >+ * Ovidio Mallo - bugs 237163, 235195, 299619, 305367 > *******************************************************************************/ > package org.eclipse.jface.tests.databinding; > >@@ -140,6 +140,9 @@ > import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableMapTest; > import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableSetTest; > import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableValueTest; >+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.ListDetailValueObservableListTest; >+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.MapDetailValueObservableMapTest; >+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.SetDetailValueObservableMapTest; > import org.eclipse.core.tests.internal.databinding.property.value.ListSimpleValueObservableListTest; > import org.eclipse.core.tests.internal.databinding.property.value.MapSimpleValueObservableMapTest; > import org.eclipse.core.tests.internal.databinding.property.value.SetSimpleValueObservableMapTest; >@@ -380,6 +383,9 @@ > addTestSuite(DetailObservableMapTest.class); > addTest(DetailObservableSetTest.suite()); > addTest(DetailObservableValueTest.suite()); >+ addTest(ListDetailValueObservableListTest.suite()); >+ addTest(MapDetailValueObservableMapTest.suite()); >+ addTest(SetDetailValueObservableMapTest.suite()); > > // org.eclipse.core.tests.internal.databinding.property.value > addTestSuite(MapSimpleValueObservableMapTest.class);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 305367
:
161682
|
173246
|
173532
|
179205
|
185014
|
185795