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 113165 Details for
Bug 194734
[Databinding] Property-based observables
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read
this important communication.
[patch]
Patch with just core IProperty API
clipboard.txt (text/plain), 204.57 KB, created by
Matthew Hall
on 2008-09-22 13:26:24 EDT
(
hide
)
Description:
Patch with just core IProperty API
Filename:
MIME Type:
Creator:
Matthew Hall
Created:
2008-09-22 13:26:24 EDT
Size:
204.57 KB
patch
obsolete
>### Eclipse Workspace Patch 1.0 >#P org.eclipse.core.databinding >Index: META-INF/MANIFEST.MF >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/META-INF/MANIFEST.MF,v >retrieving revision 1.15 >diff -u -r1.15 MANIFEST.MF >--- META-INF/MANIFEST.MF 3 Jul 2008 23:47:30 -0000 1.15 >+++ META-INF/MANIFEST.MF 22 Sep 2008 17:25:46 -0000 >@@ -14,6 +14,8 @@ > org.eclipse.core.databinding.observable.masterdetail, > org.eclipse.core.databinding.observable.set;x-internal:=false, > org.eclipse.core.databinding.observable.value;x-internal:=false, >+ org.eclipse.core.databinding.property, >+ org.eclipse.core.databinding.property.masterdetail, > org.eclipse.core.databinding.util, > org.eclipse.core.databinding.validation;x-internal:=false, > org.eclipse.core.internal.databinding;x-friends:="org.eclipse.core.databinding.beans", >@@ -21,6 +23,8 @@ > org.eclipse.core.internal.databinding.observable;x-internal:=true, > org.eclipse.core.internal.databinding.observable.masterdetail;x-friends:="org.eclipse.jface.tests.databinding", > org.eclipse.core.internal.databinding.observable.tree;x-friends:="org.eclipse.jface.databinding,org.eclipse.jface.tests.databinding", >+ org.eclipse.core.internal.databinding.property;x-friends:="org.eclipse.jface.databinding,org.eclipse.jface.tests.databinding", >+ org.eclipse.core.internal.databinding.property.masterdetail;x-friends:="org.eclipse.jface.databinding,org.eclipse.jface.tests.databinding", > org.eclipse.core.internal.databinding.validation;x-friends:="org.eclipse.jface.tests.databinding" > Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)" > Import-Package-Comment: see http://wiki.eclipse.org/ >Index: src/org/eclipse/core/databinding/property/IListProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/IListProperty.java >diff -N src/org/eclipse/core/databinding/property/IListProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/IListProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,163 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.Collection; >+import java.util.List; >+ >+/** >+ * Interface for list-typed properties. >+ * >+ * @since 1.2 >+ * @noimplement This interface is not intended to be implemented by clients. >+ */ >+public interface IListProperty extends ICollectionProperty { >+ /** >+ * Returns a List with the current contents of the source's list property >+ * >+ * @param source >+ * the property source >+ * @return a List with the current contents of the source's list property >+ */ >+ List getList(Object source); >+ >+ /** >+ * Inserts all elements in the specified collection into the source's list >+ * property at the specified index. >+ * >+ * @param source >+ * the property source >+ * @param index >+ * the insertion index >+ * @param c >+ * the collection of elements to add >+ * @return whether the source's list property was changed >+ */ >+ boolean addAll(Object source, int index, Collection c); >+ >+ /** >+ * Returns the element at the specified position in the source's list >+ * property >+ * >+ * @param source >+ * the property source >+ * @param index >+ * the element position >+ * @return the element at the given position in the source's list property >+ */ >+ Object get(Object source, int index); >+ >+ /** >+ * Replaces the element at the specified position in the source's list >+ * property with the given element. >+ * >+ * @param source >+ * the property source >+ * @param index >+ * the element position >+ * @param element >+ * the replacement element >+ * @return the element previously at the specified position in the source's >+ * list property >+ */ >+ Object set(Object source, int index, Object element); >+ >+ /** >+ * Moves the element at the specified old position in the source's list >+ * property to the specified new position >+ * >+ * @param source >+ * the property source >+ * @param oldIndex >+ * the old element position >+ * @param newIndex >+ * the new element position >+ * @return the element that was moved >+ */ >+ Object move(Object source, int oldIndex, int newIndex); >+ >+ /** >+ * Inserts the element into the source's list property at the specified >+ * position >+ * >+ * @param source >+ * the property source >+ * @param index >+ * the insertion index >+ * @param element >+ * the element to insert >+ */ >+ void add(Object source, int index, Object element); >+ >+ /** >+ * Removes the element from the source's list property which is located at >+ * the specified position >+ * >+ * @param source >+ * the property source >+ * @param index >+ * the index of the element to remove >+ * @return the element that was removed from the source's list property >+ */ >+ Object remove(Object source, int index); >+ >+ /** >+ * Returns the index of the first location of the given element in the >+ * source's list property, or -1 if the list does not contain the element. >+ * >+ * @param source >+ * the property source >+ * @param o >+ * the element >+ * @return the index of the first location of the given element in the >+ * source's list property, or -1 if the list does not contain the >+ * element >+ */ >+ int indexOf(Object source, Object o); >+ >+ /** >+ * Returns the index of the last location of the given element in the >+ * source's list property, or -1 if the list does not contain the given >+ * element. >+ * >+ * @param source >+ * @param o >+ * @return the index of the last location of the given element in the >+ * source's list property, or -1 if the list does not contain the >+ * element >+ */ >+ int lastIndexOf(Object source, Object o); >+ >+ /** >+ * Adds the given list property change listener to the list of listeners for >+ * the given source. >+ * >+ * @param source >+ * the property source >+ * @param listener >+ * the listener >+ */ >+ public void addListChangeListener(Object source, >+ IListPropertyChangeListener listener); >+ >+ /** >+ * Removes the given list property change listener from the list of >+ * listeners for the given source. >+ * >+ * @param source >+ * the property source >+ * @param listener >+ * the listener >+ */ >+ public void removeListChangeListener(Object source, >+ IListPropertyChangeListener listener); >+} >Index: src/org/eclipse/core/databinding/property/BasicMapProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/BasicMapProperty.java >diff -N src/org/eclipse/core/databinding/property/BasicMapProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/BasicMapProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,146 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 IBM Corporation 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: >+ * IBM Corporation - initial API and implementation >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.Collections; >+import java.util.HashMap; >+import java.util.HashSet; >+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.map.MapDiff; >+import org.eclipse.core.internal.databinding.property.PropertyUpdateHelper; >+ >+/** >+ * Abstract map property implementation for properties where the map can be >+ * completely replaced in a single atomic operation. >+ * <p> >+ * For example, a map-typed bean property Customer.phoneNumbers can be modified >+ * by calling Customer.setPhoneNumbers(Map phoneNumbers). >+ * >+ * @since 1.2 >+ */ >+public abstract class BasicMapProperty extends MapProperty { >+ private PropertyUpdateHelper updateHelper = new PropertyUpdateHelper(); >+ >+ /** >+ * Returns whether this property is currently being updated on the source. >+ * Implementors should query this value to avoid unnecessary calculations, >+ * such as computing a diff. >+ * >+ * @param source >+ * the property source >+ * @return whether this property is currently being updated on the source. >+ */ >+ protected boolean isUpdating(Object source) { >+ return updateHelper.isUpdating(source); >+ } >+ >+ /** >+ * Sets the map property on the source to the specified map, then fires a >+ * map change using the specified diff. >+ * >+ * @param source >+ * the property source >+ * @param map >+ * the new map >+ * @param diff >+ * the diff to be fired after the map property has been set. If >+ * null, the diff will be computed. >+ */ >+ protected void setMap(Object source, Map map, MapDiff diff) { >+ if (diff == null) { >+ diff = Diffs.computeMapDiff(getMap(source), map); >+ } >+ >+ updateHelper.setUpdating(source, true); >+ try { >+ doSetMap(source, map); >+ } finally { >+ updateHelper.setUpdating(source, false); >+ } >+ >+ if (hasListeners(source)) { >+ fireMapChange(source, diff); >+ } >+ } >+ >+ public void clear(Object source) { >+ setMap(source, new HashMap(), Diffs.createMapDiffRemoveAll(new HashMap( >+ getMap(source)))); >+ } >+ >+ public Object put(Object source, Object key, Object value) { >+ Map map = new HashMap(getMap(source)); >+ boolean addition = !map.containsKey(key); >+ Object result = map.put(key, value); >+ MapDiff diff; >+ if (addition) { >+ diff = Diffs.createMapDiffSingleAdd(key, value); >+ } else { >+ diff = Diffs.createMapDiffSingleChange(key, result, value); >+ } >+ setMap(source, map, diff); >+ return result; >+ } >+ >+ public void putAll(Object source, Map t) { >+ Map map = new HashMap(getMap(source)); >+ Set addedKeys = new HashSet(); >+ Set changedKeys = new HashSet(); >+ Map oldValues = new HashMap(); >+ Map newValues = new HashMap(); >+ for (Iterator it = t.entrySet().iterator(); it.hasNext();) { >+ Map.Entry entry = (Map.Entry) it.next(); >+ Object key = entry.getKey(); >+ Object newValue = entry.getValue(); >+ boolean addition = !map.containsKey(key); >+ Object oldValue = map.put(key, newValue); >+ if (addition) { >+ addedKeys.add(key); >+ } else { >+ changedKeys.add(key); >+ oldValues.put(key, oldValue); >+ } >+ newValues.put(key, newValue); >+ } >+ setMap(source, map, Diffs.createMapDiff(addedKeys, >+ Collections.EMPTY_SET, changedKeys, oldValues, newValues)); >+ } >+ >+ public Object remove(Object source, Object key) { >+ Map map = getMap(source); >+ if (map.containsKey(key)) { >+ map = new HashMap(map); >+ Object result = map.remove(key); >+ setMap(source, map, Diffs.createMapDiffSingleRemove(key, result)); >+ return result; >+ } >+ return null; >+ } >+ >+ public int size(Object source) { >+ return getMap(source).size(); >+ } >+ >+ protected abstract void doSetMap(Object source, Map map); >+ >+ public synchronized void dispose() { >+ if (updateHelper != null) { >+ updateHelper.dispose(); >+ updateHelper = null; >+ } >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/databinding/property/ISetProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/ISetProperty.java >diff -N src/org/eclipse/core/databinding/property/ISetProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/ISetProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,55 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.Set; >+ >+/** >+ * Interface for set-typed properties >+ * >+ * @since 1.2 >+ * @noimplement This interface is not intended to be implemented by clients. >+ */ >+public interface ISetProperty extends ICollectionProperty { >+ /** >+ * Returns a Set with the current contents of the source's set property >+ * >+ * @param source >+ * the property source >+ * @return a Set with the current contents of the source's set property >+ */ >+ public Set getSet(Object source); >+ >+ /** >+ * Adds the given set property change listener to the list of listeners for >+ * the given source. >+ * >+ * @param source >+ * the property source >+ * @param listener >+ * the listener >+ */ >+ public void addSetChangeListener(Object source, >+ ISetPropertyChangeListener listener); >+ >+ /** >+ * Removes the given set property change listener from the list of listeners >+ * for the given source. >+ * >+ * @param source >+ * the property source >+ * @param listener >+ * the listener >+ */ >+ public void removeSetChangeListener(Object source, >+ ISetPropertyChangeListener listener); >+} >Index: src/org/eclipse/core/databinding/property/ValuePropertyChangeEvent.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/ValuePropertyChangeEvent.java >diff -N src/org/eclipse/core/databinding/property/ValuePropertyChangeEvent.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/ValuePropertyChangeEvent.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,58 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import org.eclipse.core.databinding.observable.value.ValueDiff; >+import org.eclipse.core.runtime.Assert; >+ >+/** >+ * Value change event describing a change of a value property on a particular >+ * property source. >+ * >+ * @since 1.2 >+ */ >+public class ValuePropertyChangeEvent extends PropertyChangeEvent { >+ private static final long serialVersionUID = 1L; >+ >+ /** >+ * The value property that changed >+ */ >+ public final IValueProperty property; >+ >+ /** >+ * ValueDiff with the old and new values of the property. >+ */ >+ public final ValueDiff diff; >+ >+ /** >+ * Constructs a ValuePropertyChangeEvent with the given attributes >+ * >+ * @param source >+ * the property source >+ * @param property >+ * the property that changed on the source >+ * @param diff >+ * a ValueDiff describing the changes to the value property >+ */ >+ public ValuePropertyChangeEvent(Object source, IValueProperty property, >+ ValueDiff diff) { >+ super(source); >+ this.property = property; >+ Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$ >+ this.diff = diff; >+ } >+ >+ void dispatch(IPropertyChangeListener listener) { >+ ((IValuePropertyChangeListener) listener) >+ .handleValuePropertyChange(this); >+ } >+} >Index: src/org/eclipse/core/databinding/property/MapProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/MapProperty.java >diff -N src/org/eclipse/core/databinding/property/MapProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/MapProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,72 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import org.eclipse.core.databinding.observable.map.MapDiff; >+ >+/** >+ * Abstract implementation of IMapProperty >+ * >+ * @since 1.2 >+ */ >+public abstract class MapProperty extends Property implements IMapProperty { >+ public boolean containsKey(Object source, Object key) { >+ return getMap(source).containsKey(key); >+ } >+ >+ public boolean containsValue(Object source, Object value) { >+ return getMap(source).containsValue(value); >+ } >+ >+ public boolean equals(Object source, Object o) { >+ return getMap(source).equals(o); >+ } >+ >+ public Object get(Object source, Object key) { >+ return getMap(source).get(key); >+ } >+ >+ public int hashCode(Object source) { >+ return getMap(source).hashCode(); >+ } >+ >+ public boolean isEmpty(Object source) { >+ return getMap(source).isEmpty(); >+ } >+ >+ public int size(Object source) { >+ return getMap(source).size(); >+ } >+ >+ public final void addMapChangeListener(Object source, >+ IMapPropertyChangeListener listener) { >+ getChangeSupport().addListener(source, listener); >+ } >+ >+ public final void removeMapChangeListener(Object source, >+ IMapPropertyChangeListener listener) { >+ getChangeSupport().removeListener(source, listener); >+ } >+ >+ /** >+ * Fires a MapPropertyChangeEvent with the given source and diff >+ * >+ * @param source >+ * the property source >+ * @param diff >+ * the map diff >+ */ >+ protected final void fireMapChange(Object source, MapDiff diff) { >+ getChangeSupport().firePropertyChange( >+ new MapPropertyChangeEvent(source, this, diff)); >+ } >+} >Index: src/org/eclipse/core/databinding/property/ListProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/ListProperty.java >diff -N src/org/eclipse/core/databinding/property/ListProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/ListProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,63 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.Collection; >+ >+import org.eclipse.core.databinding.observable.list.ListDiff; >+ >+/** >+ * Abstract implementation of IListProperty. >+ * >+ * @since 1.2 >+ */ >+public abstract class ListProperty extends CollectionProperty implements >+ IListProperty { >+ Collection getCollection(Object source) { >+ return getList(source); >+ } >+ >+ public Object get(Object source, int index) { >+ return getList(source).get(index); >+ } >+ >+ public int indexOf(Object source, Object o) { >+ return getList(source).indexOf(o); >+ } >+ >+ public int lastIndexOf(Object source, Object o) { >+ return getList(source).lastIndexOf(o); >+ } >+ >+ public final void addListChangeListener(Object source, >+ IListPropertyChangeListener listener) { >+ getChangeSupport().addListener(source, listener); >+ } >+ >+ public final void removeListChangeListener(Object source, >+ IListPropertyChangeListener listener) { >+ getChangeSupport().removeListener(source, listener); >+ } >+ >+ /** >+ * Fires a ListPropertyChangeEvent with the given source and diff >+ * >+ * @param source >+ * the property source >+ * @param diff >+ * the list diff >+ */ >+ protected void fireListChange(Object source, ListDiff diff) { >+ getChangeSupport().firePropertyChange( >+ new ListPropertyChangeEvent(source, this, diff)); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/PropertyObservableMap.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/PropertyObservableMap.java >diff -N src/org/eclipse/core/internal/databinding/property/PropertyObservableMap.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/PropertyObservableMap.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,177 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property; >+ >+import java.util.Collection; >+import java.util.Collections; >+import java.util.Map; >+import java.util.Set; >+ >+import org.eclipse.core.databinding.observable.ObservableTracker; >+import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.map.AbstractObservableMap; >+import org.eclipse.core.databinding.property.IMapProperty; >+import org.eclipse.core.databinding.property.IMapPropertyChangeListener; >+import org.eclipse.core.databinding.property.IProperty; >+import org.eclipse.core.databinding.property.IPropertyObservable; >+import org.eclipse.core.databinding.property.MapPropertyChangeEvent; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class PropertyObservableMap extends AbstractObservableMap implements >+ IPropertyObservable { >+ private Object source; >+ private IMapProperty property; >+ >+ private volatile boolean updating = false; >+ >+ private boolean disposed = false; >+ >+ private transient volatile int modCount = 0; >+ >+ private IMapPropertyChangeListener listener = new IMapPropertyChangeListener() { >+ public void handleMapPropertyChange(final MapPropertyChangeEvent event) { >+ if (!disposed && !updating) { >+ getRealm().exec(new Runnable() { >+ public void run() { >+ getRealm().exec(new Runnable() { >+ public void run() { >+ modCount++; >+ fireMapChange(event.diff); >+ } >+ }); >+ } >+ }); >+ } >+ } >+ }; >+ >+ /** >+ * @param realm >+ * @param source >+ * @param property >+ */ >+ public PropertyObservableMap(Realm realm, Object source, >+ IMapProperty property) { >+ super(realm); >+ this.source = source; >+ this.property = property; >+ } >+ >+ private void getterCalled() { >+ ObservableTracker.getterCalled(this); >+ } >+ >+ protected void firstListenerAdded() { >+ if (!disposed) { >+ property.addMapChangeListener(source, listener); >+ } >+ } >+ >+ protected void lastListenerRemoved() { >+ if (!disposed) { >+ property.removeMapChangeListener(source, listener); >+ } >+ } >+ >+ public boolean containsKey(Object key) { >+ getterCalled(); >+ return property.containsKey(source, key); >+ } >+ >+ public boolean containsValue(Object value) { >+ getterCalled(); >+ return property.containsValue(source, value); >+ } >+ >+ public Set entrySet() { >+ getterCalled(); >+ // unmodifiable for now >+ return Collections.unmodifiableSet(property.getMap(source).entrySet()); >+ } >+ >+ public Object get(Object key) { >+ getterCalled(); >+ return property.get(source, key); >+ } >+ >+ public boolean isEmpty() { >+ getterCalled(); >+ return property.isEmpty(source); >+ } >+ >+ public Set keySet() { >+ getterCalled(); >+ return Collections.unmodifiableSet(property.getMap(source).keySet()); >+ } >+ >+ public Object put(Object key, Object value) { >+ checkRealm(); >+ return property.put(source, key, value); >+ } >+ >+ public void putAll(Map m) { >+ checkRealm(); >+ property.putAll(source, m); >+ } >+ >+ public Object remove(Object key) { >+ checkRealm(); >+ return property.remove(source, key); >+ } >+ >+ public int size() { >+ getterCalled(); >+ return property.size(source); >+ } >+ >+ public Collection values() { >+ getterCalled(); >+ return Collections.unmodifiableCollection(property.getMap(source) >+ .values()); >+ } >+ >+ public void clear() { >+ getterCalled(); >+ property.clear(source); >+ } >+ >+ public boolean equals(Object o) { >+ getterCalled(); >+ return property.equals(source, o); >+ } >+ >+ public int hashCode() { >+ getterCalled(); >+ return property.hashCode(source); >+ } >+ >+ public Object getObserved() { >+ return source; >+ } >+ >+ public IProperty getProperty() { >+ return property; >+ } >+ >+ public synchronized void dispose() { >+ if (!disposed) { >+ disposed = true; >+ property.removeMapChangeListener(source, listener); >+ property = null; >+ source = null; >+ } >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/databinding/property/SetPropertyChangeEvent.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/SetPropertyChangeEvent.java >diff -N src/org/eclipse/core/databinding/property/SetPropertyChangeEvent.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/SetPropertyChangeEvent.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,57 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import org.eclipse.core.databinding.observable.set.SetDiff; >+import org.eclipse.core.runtime.Assert; >+ >+/** >+ * Set change event describing an incremental change of a set property on a >+ * particular property source. >+ * >+ * @since 1.2 >+ */ >+public class SetPropertyChangeEvent extends PropertyChangeEvent { >+ private static final long serialVersionUID = 1L; >+ >+ /** >+ * The set property that changed >+ */ >+ public final ISetProperty property; >+ >+ /** >+ * SetDiff enumerating the added and removed elements in the set. >+ */ >+ public final SetDiff diff; >+ >+ /** >+ * Constructs a SetPropertyChangeEvent with the given attributes >+ * >+ * @param source >+ * the property source >+ * @param property >+ * the property that changed on the source >+ * @param diff >+ * a SetDiff describing the changes to the set property >+ */ >+ public SetPropertyChangeEvent(Object source, ISetProperty property, >+ SetDiff diff) { >+ super(source); >+ this.property = property; >+ Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$ >+ this.diff = diff; >+ } >+ >+ void dispatch(IPropertyChangeListener listener) { >+ ((ISetPropertyChangeListener) listener).handleSetPropertyChange(this); >+ } >+} >Index: src/org/eclipse/core/databinding/property/IMapProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/IMapProperty.java >diff -N src/org/eclipse/core/databinding/property/IMapProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/IMapProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,177 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.Map; >+ >+/** >+ * Interface for map-typed properties >+ * >+ * @since 1.2 >+ * @noimplement This interface is not intended to be implemented by clients. >+ */ >+public interface IMapProperty extends IProperty { >+ /** >+ * Returns a Map with the current contents of the source's map property >+ * >+ * @param source >+ * the property source >+ * @return a Map with the current contents of the source's map property >+ */ >+ Map getMap(Object source); >+ >+ /** >+ * Returns the size of the source's map property >+ * >+ * @param source >+ * the property source >+ * @return the size of the source's map property >+ */ >+ int size(Object source); >+ >+ /** >+ * Returns whether the source's map property is empty >+ * >+ * @param source >+ * the property source >+ * @return whether the source's map property is empty >+ */ >+ boolean isEmpty(Object source); >+ >+ /** >+ * Returns whether the specified key is contained in the key set of the >+ * source's map property >+ * >+ * @param source >+ * the property source >+ * @param key >+ * the key >+ * @return whether the specified key is contained in the key set of the >+ * source's map property >+ */ >+ boolean containsKey(Object source, Object key); >+ >+ /** >+ * Returns whether the specified value is contains in the values collection >+ * of the source's map property >+ * >+ * @param source >+ * the property source >+ * @param value >+ * the value >+ * @return whether the specified value is contains in the values collection >+ * of the source's map property >+ */ >+ boolean containsValue(Object source, Object value); >+ >+ /** >+ * Returns the value associated with the specified key in the source's map >+ * property >+ * >+ * @param source >+ * the property source >+ * @param key >+ * the key >+ * @return the value associated with the specified key in the source's map >+ * property >+ */ >+ Object get(Object source, Object key); >+ >+ /** >+ * Associates the specified value with the specified key in the source's map >+ * property >+ * >+ * @param source >+ * the property source >+ * @param key >+ * the key >+ * @param value >+ * the value >+ * @return the value that was previously associated with the given key in >+ * the source's map property >+ */ >+ Object put(Object source, Object key, Object value); >+ >+ /** >+ * Removes the mapping for the specified key from the source's map property >+ * >+ * @param source >+ * the property source >+ * @param key >+ * the key >+ * @return the value that was previously associated with the specified key >+ * in the source's map property, or null if no such mapping exists >+ */ >+ Object remove(Object source, Object key); >+ >+ /** >+ * Adds all mappings in the specified map to the source's map property. >+ * >+ * @param source >+ * the property source >+ * @param t >+ * the map >+ */ >+ void putAll(Object source, Map t); >+ >+ /** >+ * Removes all mapping from the source's map property >+ * >+ * @param source >+ * the property source >+ */ >+ void clear(Object source); >+ >+ /** >+ * Returns whether the source's map property is equal to the argument >+ * >+ * @param source >+ * the property source >+ * @param o >+ * the object to test for equality >+ * @return whether the source's map property is equal to the argument >+ */ >+ boolean equals(Object source, Object o); >+ >+ /** >+ * Returns the hash code of the source's map property >+ * >+ * @param source >+ * the property source >+ * @return the hash code of the source's map property >+ */ >+ int hashCode(Object source); >+ >+ /** >+ * Adds the given map property change listener to the list of listeners for >+ * the given source. >+ * >+ * @param source >+ * the property source >+ * @param listener >+ * the listener >+ */ >+ public void addMapChangeListener(Object source, >+ IMapPropertyChangeListener listener); >+ >+ /** >+ * Removes the given map property change listener from the list of listeners >+ * for the given source. >+ * >+ * @param source >+ * the property source >+ * @param listener >+ * the listener >+ */ >+ public void removeMapChangeListener(Object source, >+ IMapPropertyChangeListener listener); >+} >Index: src/org/eclipse/core/databinding/property/IValueProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/IValueProperty.java >diff -N src/org/eclipse/core/databinding/property/IValueProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/IValueProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,71 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+ >+/** >+ * Interface for value-typed properties >+ * >+ * @since 1.2 >+ * @noimplement This interface is not intended to be implemented by clients. >+ */ >+public interface IValueProperty extends IProperty { >+ /** >+ * Returns the source's value property >+ * >+ * @param source >+ * the property source >+ * @return the current value of the source's value property >+ */ >+ public Object getValue(Object source); >+ >+ /** >+ * Sets the source's value property to the specified value >+ * >+ * @param source >+ * the property source >+ * @param value >+ * the new value >+ */ >+ public void setValue(Object source, Object value); >+ >+ /** >+ * Returns the value type of the property, or <code>null</code> if untyped. >+ * >+ * @return the value type of the property, or <code>null</code> if untyped. >+ */ >+ public Object getValueType(); >+ >+ /** >+ * Adds the given value property change listener to the list of listeners >+ * for the given source. >+ * >+ * @param source >+ * the property source >+ * @param listener >+ * the listener >+ */ >+ public void addValueChangeListener(Object source, >+ IValuePropertyChangeListener listener); >+ >+ /** >+ * Removes the given list property change listener from the list of >+ * listeners for the given source. >+ * >+ * @param source >+ * the property source >+ * @param listener >+ * the listener >+ */ >+ public void removeValueChangeListener(Object source, >+ IValuePropertyChangeListener listener); >+} >Index: src/org/eclipse/core/databinding/property/IMapPropertyChangeListener.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/IMapPropertyChangeListener.java >diff -N src/org/eclipse/core/databinding/property/IMapPropertyChangeListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/IMapPropertyChangeListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,27 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+/** >+ * Listener for changes to map properties on a property source >+ * >+ * @since 1.2 >+ */ >+public interface IMapPropertyChangeListener extends IPropertyChangeListener { >+ /** >+ * Handle a change to a map property on a specific property source. >+ * >+ * @param event >+ * an event describing the map change that occured. >+ */ >+ public void handleMapPropertyChange(MapPropertyChangeEvent event); >+} >Index: src/org/eclipse/core/databinding/property/ValueProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/ValueProperty.java >diff -N src/org/eclipse/core/databinding/property/ValueProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/ValueProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,44 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import org.eclipse.core.databinding.observable.value.ValueDiff; >+ >+/** >+ * Abstract implementation of IValueProperty. >+ * >+ * @since 1.2 >+ */ >+public abstract class ValueProperty extends Property implements IValueProperty { >+ public final void addValueChangeListener(Object source, >+ IValuePropertyChangeListener listener) { >+ getChangeSupport().addListener(source, listener); >+ } >+ >+ public final void removeValueChangeListener(Object source, >+ IValuePropertyChangeListener listener) { >+ getChangeSupport().removeListener(source, listener); >+ } >+ >+ /** >+ * Fires a ValuePropertyChangeEvent with the given source and diff >+ * >+ * @param source >+ * the property source >+ * @param diff >+ * the value diff >+ */ >+ protected void fireValueChange(Object source, ValueDiff diff) { >+ getChangeSupport().firePropertyChange( >+ new ValuePropertyChangeEvent(source, this, diff)); >+ } >+} >Index: src/org/eclipse/core/databinding/property/IValuePropertyChangeListener.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/IValuePropertyChangeListener.java >diff -N src/org/eclipse/core/databinding/property/IValuePropertyChangeListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/IValuePropertyChangeListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,27 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+/** >+ * Listener for changes to value properties on a property source >+ * >+ * @since 1.2 >+ */ >+public interface IValuePropertyChangeListener extends IPropertyChangeListener { >+ /** >+ * Handle a change to a value property on a specific property source. >+ * >+ * @param event >+ * an event describing the value change that occured. >+ */ >+ public void handleValuePropertyChange(ValuePropertyChangeEvent event); >+} >Index: src/org/eclipse/core/databinding/property/BasicListProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/BasicListProperty.java >diff -N src/org/eclipse/core/databinding/property/BasicListProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/BasicListProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,244 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 IBM Corporation 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: >+ * IBM Corporation - initial API and implementation >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.ArrayList; >+import java.util.Collection; >+import java.util.Iterator; >+import java.util.List; >+import java.util.ListIterator; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.list.ListDiff; >+import org.eclipse.core.databinding.observable.list.ListDiffEntry; >+import org.eclipse.core.internal.databinding.property.PropertyUpdateHelper; >+ >+/** >+ * Abstract IListProperty implementation for properties where the list can be >+ * completely replaced in a single atomic operation. >+ * <p> >+ * For example, a list-typed bean property Customer.invoices can be modified by >+ * calling Customer.setInvoices(List invoices). >+ * >+ * @since 1.2 >+ */ >+public abstract class BasicListProperty extends ListProperty { >+ private PropertyUpdateHelper updateHelper = new PropertyUpdateHelper(); >+ >+ /** >+ * Returns whether this property is currently being updated on the source. >+ * Implementors should query this value to avoid unnecessary calculations, >+ * such as computing a diff. >+ * >+ * @param source >+ * the property source >+ * @return whether this property is currently being updated on the source. >+ */ >+ protected boolean isUpdating(Object source) { >+ return updateHelper.isUpdating(source); >+ } >+ >+ /** >+ * Sets the list property on the source to the specified list, then fires a >+ * list change using the specified diff. >+ * >+ * @param source >+ * the property source >+ * @param list >+ * the new list >+ * @param diff >+ * the diff to be fired after the list property has been set. If >+ * null, the diff will be computed. >+ */ >+ protected void setList(Object source, List list, ListDiff diff) { >+ if (diff == null) { >+ diff = Diffs.computeListDiff(getList(source), list); >+ } >+ >+ updateHelper.setUpdating(source, true); >+ try { >+ doSetList(source, list); >+ } finally { >+ updateHelper.setUpdating(source, false); >+ } >+ >+ if (hasListeners(source)) { >+ fireListChange(source, diff); >+ } >+ } >+ >+ protected void fireListChange(Object source, ListDiff diff) { >+ if (!isUpdating(source)) >+ super.fireListChange(source, diff); >+ } >+ >+ /** >+ * Sets the list property on the source to the specified list. >+ * >+ * @param source >+ * the property source >+ * @param list >+ * the new list >+ */ >+ protected abstract void doSetList(Object source, List list); >+ >+ public boolean add(Object source, Object o) { >+ add(source, size(source), o); >+ return true; >+ } >+ >+ public void add(Object source, int index, Object element) { >+ List list = new ArrayList(getList(source)); >+ list.add(index, element); >+ setList(source, list, Diffs.createListDiff(Diffs.createListDiffEntry( >+ index, true, element))); >+ } >+ >+ public boolean addAll(Object source, Collection c) { >+ if (c.isEmpty()) >+ return false; >+ addAll(source, size(source), c); >+ return true; >+ } >+ >+ public boolean addAll(Object source, int index, Collection c) { >+ if (c.isEmpty()) { >+ return false; >+ } >+ >+ List list = new ArrayList(getList(source)); >+ List entries = new ArrayList(); >+ int i = index; >+ for (Iterator it = c.iterator(); it.hasNext(); i++) { >+ Object o = it.next(); >+ list.add(i, o); >+ entries.add(Diffs.createListDiffEntry(i, true, o)); >+ } >+ boolean changed = !entries.isEmpty(); >+ if (changed) { >+ ListDiffEntry[] ea = (ListDiffEntry[]) entries >+ .toArray(new ListDiffEntry[entries.size()]); >+ setList(source, list, Diffs.createListDiff(ea)); >+ } >+ return changed; >+ } >+ >+ public void clear(Object source) { >+ if (isEmpty(source)) >+ return; >+ List list = getList(source); >+ ListDiffEntry[] entries = new ListDiffEntry[list.size()]; >+ int i = 0; >+ for (Iterator it = getList(source).iterator(); it.hasNext(); i++) { >+ entries[i] = Diffs.createListDiffEntry(0, false, it.next()); >+ } >+ setList(source, new ArrayList(), Diffs.createListDiff(entries)); >+ } >+ >+ public Object move(Object source, int oldIndex, int newIndex) { >+ if (oldIndex == newIndex) >+ return get(source, oldIndex); >+ List list = new ArrayList(getList(source)); >+ Object result = list.remove(oldIndex); >+ list.add(newIndex, result); >+ setList(source, list, Diffs.createListDiff(Diffs.createListDiffEntry( >+ oldIndex, false, result), Diffs.createListDiffEntry(newIndex, >+ true, result))); >+ return result; >+ } >+ >+ public boolean remove(Object source, Object o) { >+ int i = indexOf(source, o); >+ if (i == -1) >+ return false; >+ remove(source, i); >+ return true; >+ } >+ >+ public Object remove(Object source, int index) { >+ List list = new ArrayList(getList(source)); >+ Object result = list.remove(index); >+ setList(source, list, Diffs.createListDiff(Diffs.createListDiffEntry( >+ index, false, result))); >+ return result; >+ } >+ >+ public boolean removeAll(Object source, Collection c) { >+ if (isEmpty(source)) { >+ return false; >+ } >+ if (c.isEmpty()) { >+ return false; >+ } >+ List list = new ArrayList(getList(source)); >+ List entries = new ArrayList(); >+ for (ListIterator it = list.listIterator(); it.hasNext();) { >+ Object o = it.next(); >+ if (c.contains(o)) { >+ entries.add(Diffs.createListDiffEntry(it.previousIndex(), >+ false, o)); >+ it.remove(); >+ } >+ } >+ boolean changed = !entries.isEmpty(); >+ if (changed) { >+ ListDiffEntry[] ea = (ListDiffEntry[]) entries >+ .toArray(new ListDiffEntry[entries.size()]); >+ setList(source, list, Diffs.createListDiff(ea)); >+ } >+ return changed; >+ } >+ >+ public boolean retainAll(Object source, Collection c) { >+ if (isEmpty(source)) { >+ return false; >+ } >+ if (c.isEmpty()) { >+ clear(source); >+ return true; >+ } >+ List list = new ArrayList(getList(source)); >+ List entries = new ArrayList(); >+ for (ListIterator it = list.listIterator(); it.hasNext();) { >+ Object o = it.next(); >+ if (!c.contains(o)) { >+ entries.add(Diffs.createListDiffEntry(it.previousIndex(), >+ false, o)); >+ it.remove(); >+ } >+ } >+ boolean changed = !entries.isEmpty(); >+ if (changed) { >+ ListDiffEntry[] ea = (ListDiffEntry[]) entries >+ .toArray(new ListDiffEntry[entries.size()]); >+ setList(source, list, Diffs.createListDiff(ea)); >+ } >+ return changed; >+ } >+ >+ public Object set(Object source, int index, Object element) { >+ List list = new ArrayList(getList(source)); >+ Object result = list.set(index, element); >+ setList(source, list, Diffs.createListDiff(Diffs.createListDiffEntry( >+ index, false, result), Diffs.createListDiffEntry(index, true, >+ element))); >+ return result; >+ } >+ >+ public synchronized void dispose() { >+ if (updateHelper != null) { >+ updateHelper.dispose(); >+ updateHelper = null; >+ } >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/databinding/property/masterdetail/MasterDetailProperties.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/masterdetail/MasterDetailProperties.java >diff -N src/org/eclipse/core/databinding/property/masterdetail/MasterDetailProperties.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/masterdetail/MasterDetailProperties.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,200 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property.masterdetail; >+ >+import org.eclipse.core.databinding.property.IListProperty; >+import org.eclipse.core.databinding.property.IMapProperty; >+import org.eclipse.core.databinding.property.ISetProperty; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.internal.databinding.property.masterdetail.ListPropertyDetailValueList; >+import org.eclipse.core.internal.databinding.property.masterdetail.MapPropertyDetailValueMap; >+import org.eclipse.core.internal.databinding.property.masterdetail.SetPropertyDetailValueMap; >+import org.eclipse.core.internal.databinding.property.masterdetail.ValuePropertyDetailList; >+import org.eclipse.core.internal.databinding.property.masterdetail.ValuePropertyDetailMap; >+import org.eclipse.core.internal.databinding.property.masterdetail.ValuePropertyDetailSet; >+import org.eclipse.core.internal.databinding.property.masterdetail.ValuePropertyDetailValue; >+ >+/** >+ * A factory for combining properties to create nested properties. >+ * <p> >+ * Example: Suppose class <code>A</code> has a property <code>b</code> of type >+ * <code>B</code>, and that class <code>B</code> has a property <code>c</code> >+ * of type <code>C</code>: >+ * >+ * <pre> >+ * A a = new A(); >+ * B b = a.getB(); >+ * IValueProperty ab = BeanProperties.valueProperty(A.class, "b"); >+ * assertTrue(ab.getValue(a) == b); >+ * >+ * IValueProperty bc = BeanProperties.valueProperty(B.class, "c"); >+ * C c = b.getC(); >+ * assertTrue(bc.getValue(b) == c); >+ * </pre> >+ * >+ * Using MasterDetailProperties, the <code>ab</code> and <code>bc</code> >+ * properties may be combined to form a nested <code>abc</code> property: >+ * >+ * <pre> >+ * IValueProperty abc = MasterDetailProperties.detailValue(ab, bc) >+ * assertTrue(abc.getValue(a) == c); >+ * </pre> >+ * >+ * @since 1.2 >+ */ >+public class MasterDetailProperties { >+ // Properties of IValueProperty master properties >+ >+ /** >+ * Returns the nested combination of the master value and detail value >+ * properties. Value modifications made through the returned property are >+ * delegated to the detail property, using the value of the master property >+ * as the source. >+ * >+ * @param masterValue >+ * the master property >+ * @param detailValue >+ * the detail property >+ * @return the nested combination of the master and detail properties >+ */ >+ public static IValueProperty detailValue(IValueProperty masterValue, >+ IValueProperty detailValue) { >+ return new ValuePropertyDetailValue(masterValue, detailValue); >+ } >+ >+ /** >+ * Returns the nested combination of the master value and detail list >+ * properties. List modifications made through the returned property are >+ * delegated to the detail property, using the value of the master property >+ * as the source. >+ * >+ * @param masterValue >+ * the master property >+ * @param detailList >+ * the detail property >+ * @return the nested combination of the master value and detail list >+ * properties >+ */ >+ public static IListProperty detailList(IValueProperty masterValue, >+ IListProperty detailList) { >+ return new ValuePropertyDetailList(masterValue, detailList); >+ } >+ >+ /** >+ * Returns the nested combination of the master value and detail set >+ * properties. Set modifications made through the returned property are >+ * delegated to the detail property, using the value of the master property >+ * as the source. >+ * >+ * @param masterValue >+ * the master property >+ * @param detailSet >+ * the detail property >+ * @return the nested combination of the master value and detail set >+ * properties >+ */ >+ public static ISetProperty detailSet(IValueProperty masterValue, >+ ISetProperty detailSet) { >+ return new ValuePropertyDetailSet(masterValue, detailSet); >+ } >+ >+ /** >+ * Returns the nested combination of the master value and detail map >+ * properties. Map modifications made through the returned property are >+ * delegated to the detail property, using the value of the master property >+ * as the source. >+ * >+ * @param masterValue >+ * the master property >+ * @param detailMap >+ * the detail property >+ * @return the nested combination of the master value and detial map >+ * properties >+ */ >+ public static IMapProperty detailMap(IValueProperty masterValue, >+ IMapProperty detailMap) { >+ return new ValuePropertyDetailMap(masterValue, detailMap); >+ } >+ >+ // Properties of IListProperty master properties >+ >+ /** >+ * Returns the nested combination of the master list and detail value >+ * properties. Note that because this property is a projection of value >+ * properties over a list, the only modification supported is through the >+ * {@link IValueProperty#setValue(Object, Object)} method. Modifications >+ * made through the returned property are delegated to the detail property, >+ * using the corresponding list element from the master property as the >+ * source. >+ * >+ * @param masterList >+ * the master property >+ * @param detailValue >+ * the detail property >+ * @return the nested combination of the master list and detail value >+ * properties >+ */ >+ public static IListProperty detailValues(IListProperty masterList, >+ IValueProperty detailValue) { >+ return new ListPropertyDetailValueList(masterList, detailValue); >+ } >+ >+ // Properties of ISetProperty master properties >+ >+ /** >+ * Returns the nested combination of the master set and detail value >+ * properties. Note that because this property is a projection of value >+ * properties over a set, the only modifications supported are through the >+ * {@link IMapProperty#put(Object, Object, Object)} and >+ * {@link IMapProperty#putAll(Object, java.util.Map)} methods. In the latter >+ * case, this property does not put entries for keys not already in the >+ * master key set. Modifications made through the returned property are >+ * delegated to the detail property, using the corresponding set element >+ * from the master property as the source. >+ * >+ * @param masterKeySet >+ * the master property >+ * @param detailValues >+ * the detail property >+ * @return the nested combination of the master set and detail value >+ * properties >+ */ >+ public static IMapProperty detailValues(ISetProperty masterKeySet, >+ IValueProperty detailValues) { >+ return new SetPropertyDetailValueMap(masterKeySet, detailValues); >+ } >+ >+ // Properties of IMapProperty master properties >+ >+ /** >+ * Returns the nested combination of the master map and detail value >+ * properties. Note that because this property is a projection of value >+ * properties over a values collection, the only modifications supported are >+ * through the {@link IMapProperty#put(Object, Object, Object)} and >+ * {@link IMapProperty#putAll(Object, java.util.Map)} methods. In the latter >+ * case, this property does not entries for keys not already contained in >+ * the master map's key set. Modifications made through the returned >+ * property are delegated to the detail property, using the corresponding >+ * entry value from the master property as the source. >+ * >+ * @param masterMap >+ * the master property >+ * @param detailValues >+ * the detail property >+ * @return the nested combination of the master map and detail value >+ * properties. >+ */ >+ public static IMapProperty detailValues(IMapProperty masterMap, >+ IValueProperty detailValues) { >+ return new MapPropertyDetailValueMap(masterMap, detailValues); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailValue.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailValue.java >diff -N src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailValue.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailValue.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,123 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property.masterdetail; >+ >+import java.util.HashMap; >+import java.util.Map; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.value.ValueDiff; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.ValueProperty; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+ >+/** >+ * @since 1.2 >+ * >+ */ >+public class ValuePropertyDetailValue extends ValueProperty implements >+ IValueProperty { >+ private IValueProperty masterProperty; >+ private IValueProperty detailProperty; >+ >+ private Map sourceToDetailListener = new HashMap(); >+ >+ private IValuePropertyChangeListener masterListener = new MasterPropertyListener(); >+ >+ private class MasterPropertyListener implements >+ IValuePropertyChangeListener { >+ public void handleValuePropertyChange(ValuePropertyChangeEvent event) { >+ Object oldSource = event.diff.getOldValue(); >+ Object newSource = event.diff.getNewValue(); >+ >+ Object oldValue = detailProperty.getValue(oldSource); >+ Object newValue = detailProperty.getValue(newSource); >+ >+ ValueDiff diff = Diffs.createValueDiff(oldValue, newValue); >+ >+ Object source = event.getSource(); >+ >+ removeDetailPropertyListener(source); >+ addDetailPropertyListener(source); >+ >+ fireValueChange(source, diff); >+ } >+ } >+ >+ private class DetailPropertyListener implements >+ IValuePropertyChangeListener { >+ private Object source; >+ private Object masterValue; >+ >+ DetailPropertyListener(Object source, Object masterValue) { >+ this.source = source; >+ this.masterValue = masterValue; >+ } >+ >+ public void handleValuePropertyChange(ValuePropertyChangeEvent event) { >+ fireValueChange(source, event.diff); >+ } >+ } >+ >+ /** >+ * @param masterProperty >+ * @param detailProperty >+ */ >+ public ValuePropertyDetailValue(IValueProperty masterProperty, >+ IValueProperty detailProperty) { >+ this.masterProperty = masterProperty; >+ this.detailProperty = detailProperty; >+ } >+ >+ protected void addListenerTo(Object source) { >+ masterProperty.addValueChangeListener(source, masterListener); >+ addDetailPropertyListener(source); >+ } >+ >+ protected void removeListenerFrom(Object source) { >+ masterProperty.removeValueChangeListener(source, masterListener); >+ removeDetailPropertyListener(source); >+ } >+ >+ private void addDetailPropertyListener(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ DetailPropertyListener detailListener = new DetailPropertyListener( >+ source, masterValue); >+ detailProperty.addValueChangeListener(masterValue, detailListener); >+ sourceToDetailListener.put(source, detailListener); >+ } >+ >+ private void removeDetailPropertyListener(Object source) { >+ DetailPropertyListener detailListener = (DetailPropertyListener) sourceToDetailListener >+ .remove(source); >+ if (detailListener != null) { >+ detailProperty.removeValueChangeListener( >+ detailListener.masterValue, detailListener); >+ } >+ sourceToDetailListener.remove(source); >+ } >+ >+ public Object getValue(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.getValue(masterValue); >+ } >+ >+ public Object getValueType() { >+ return detailProperty.getValueType(); >+ } >+ >+ public void setValue(Object source, Object value) { >+ Object masterValue = masterProperty.getValue(source); >+ detailProperty.setValue(masterValue, value); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/MapValuePropertyObservableMap.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/MapValuePropertyObservableMap.java >diff -N src/org/eclipse/core/internal/databinding/property/MapValuePropertyObservableMap.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/MapValuePropertyObservableMap.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,323 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property; >+ >+import java.util.AbstractSet; >+import java.util.HashMap; >+import java.util.HashSet; >+import java.util.IdentityHashMap; >+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.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.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+import org.eclipse.core.internal.databinding.Util; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class MapValuePropertyObservableMap extends AbstractObservableMap >+ implements IObserving { >+ private IObservableMap map; >+ private IValueProperty property; >+ >+ private Map keyToMasterValueListener; >+ >+ private boolean updating = false; >+ private boolean disposed = false; >+ >+ private IMapChangeListener mapListener = new IMapChangeListener() { >+ public void handleMapChange(final MapChangeEvent event) { >+ if (!updating && !disposed) { >+ getRealm().exec(new Runnable() { >+ public void run() { >+ Map oldValues = new HashMap(); >+ Map newValues = new HashMap(); >+ >+ Set addedKeys = event.diff.getAddedKeys(); >+ for (Iterator it = addedKeys.iterator(); it.hasNext();) { >+ Object key = it.next(); >+ Object newSource = event.diff.getNewValue(key); >+ Object newValue = property.getValue(newSource); >+ newValues.put(key, newValue); >+ addPropertySourceListener(key, newSource); >+ } >+ >+ Set removedKeys = event.diff.getRemovedKeys(); >+ for (Iterator it = removedKeys.iterator(); it.hasNext();) { >+ Object key = it.next(); >+ Object oldSource = event.diff.getOldValue(key); >+ Object oldValue = property.getValue(oldSource); >+ oldValues.put(key, oldValue); >+ removePropertySourceListener(key, oldSource); >+ } >+ >+ Set changedKeys = new HashSet(event.diff >+ .getChangedKeys()); >+ for (Iterator it = changedKeys.iterator(); it.hasNext();) { >+ Object key = it.next(); >+ >+ Object oldSource = event.diff.getOldValue(key); >+ Object newSource = event.diff.getNewValue(key); >+ >+ Object oldValue = property.getValue(oldSource); >+ Object newValue = property.getValue(newSource); >+ >+ if (Util.equals(oldValue, newValue)) { >+ it.remove(); >+ } else { >+ oldValues.put(key, oldValue); >+ newValues.put(key, newValue); >+ } >+ >+ removePropertySourceListener(key, oldSource); >+ addPropertySourceListener(key, newSource); >+ } >+ >+ fireMapChange(Diffs.createMapDiff(addedKeys, >+ removedKeys, changedKeys, oldValues, newValues)); >+ } >+ }); >+ } >+ } >+ }; >+ >+ private IStaleListener staleListener = new IStaleListener() { >+ public void handleStale(StaleEvent staleEvent) { >+ fireStale(); >+ } >+ }; >+ >+ /** >+ * @param map >+ * @param valueProperty >+ */ >+ public MapValuePropertyObservableMap(IObservableMap map, >+ IValueProperty valueProperty) { >+ super(map.getRealm()); >+ this.map = map; >+ this.property = valueProperty; >+ >+ this.keyToMasterValueListener = new IdentityHashMap(); >+ } >+ >+ private class ValuePropertySourceChangeListener implements >+ IValuePropertyChangeListener { >+ private final Object key; >+ >+ public ValuePropertySourceChangeListener(Object key) { >+ this.key = key; >+ } >+ >+ public void handleValuePropertyChange(ValuePropertyChangeEvent event) { >+ Object oldSource = event.diff.getOldValue(); >+ Object oldValue = property.getValue(oldSource); >+ property.removeValueChangeListener(oldSource, this); >+ >+ Object newSource = event.diff.getNewValue(); >+ Object newValue = property.getValue(newSource); >+ property.addValueChangeListener(newSource, this); >+ >+ if (!Util.equals(oldValue, newValue)) >+ fireMapChange(Diffs.createMapDiffSingleChange(key, oldValue, >+ newValue)); >+ } >+ } >+ >+ protected void firstListenerAdded() { >+ if (!disposed) { >+ map.addMapChangeListener(mapListener); >+ map.addStaleListener(staleListener); >+ for (Iterator iterator = map.entrySet().iterator(); iterator >+ .hasNext();) { >+ Map.Entry entry = (Map.Entry) iterator.next(); >+ Object key = entry.getKey(); >+ Object masterValue = entry.getValue(); >+ >+ addPropertySourceListener(key, masterValue); >+ } >+ } >+ } >+ >+ protected void lastListenerRemoved() { >+ if (!disposed) { >+ map.removeMapChangeListener(mapListener); >+ map.removeStaleListener(staleListener); >+ for (Iterator iterator = map.entrySet().iterator(); iterator >+ .hasNext();) { >+ Map.Entry entry = (Map.Entry) iterator.next(); >+ Object key = entry.getKey(); >+ Object propertySource = entry.getValue(); >+ >+ removePropertySourceListener(key, propertySource); >+ } >+ } >+ } >+ >+ private void addPropertySourceListener(Object key, Object propertySource) { >+ IValuePropertyChangeListener propertyListener = new ValuePropertySourceChangeListener( >+ key); >+ property.addValueChangeListener(propertySource, propertyListener); >+ keyToMasterValueListener.put(key, propertyListener); >+ } >+ >+ private void removePropertySourceListener(Object key, Object propertySource) { >+ IValuePropertyChangeListener propertyListener = (IValuePropertyChangeListener) keyToMasterValueListener >+ .remove(key); >+ if (propertyListener != null) { >+ property >+ .removeValueChangeListener(propertySource, propertyListener); >+ } >+ } >+ >+ protected Object doGet(Object key) { >+ if (!map.containsKey(key)) >+ return null; >+ return property.getValue(map.get(key)); >+ } >+ >+ protected Object doPut(Object key, Object value) { >+ if (!map.containsKey(key)) >+ return null; >+ Object source = map.get(key); >+ >+ Object oldValue = property.getValue(source); >+ >+ updating = true; >+ try { >+ property.setValue(source, value); >+ } finally { >+ updating = false; >+ } >+ >+ Object newValue = property.getValue(source); >+ >+ if (!Util.equals(oldValue, newValue)) { >+ fireMapChange(Diffs.createMapDiffSingleChange(key, oldValue, >+ newValue)); >+ } >+ >+ return oldValue; >+ } >+ >+ private Set entrySet; >+ >+ public Set entrySet() { >+ getterCalled(); >+ if (entrySet == null) >+ entrySet = new EntrySet(); >+ return entrySet; >+ } >+ >+ class EntrySet extends AbstractSet { >+ public Iterator iterator() { >+ return new Iterator() { >+ Iterator it = map.entrySet().iterator(); >+ >+ public boolean hasNext() { >+ getterCalled(); >+ return it.hasNext(); >+ } >+ >+ public Object next() { >+ getterCalled(); >+ Map.Entry next = (Map.Entry) it.next(); >+ return new MapEntry(next.getKey()); >+ } >+ >+ public void remove() { >+ it.remove(); >+ } >+ }; >+ } >+ >+ public int size() { >+ return map.size(); >+ } >+ } >+ >+ class MapEntry implements Map.Entry { >+ private Object key; >+ >+ MapEntry(Object key) { >+ this.key = key; >+ } >+ >+ public Object getKey() { >+ getterCalled(); >+ return key; >+ } >+ >+ public Object getValue() { >+ getterCalled(); >+ return get(key); >+ } >+ >+ public Object setValue(Object value) { >+ return put(key, value); >+ } >+ >+ public boolean equals(Object o) { >+ 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() { >+ getterCalled(); >+ Object value = getValue(); >+ return (key == null ? 0 : key.hashCode()) >+ ^ (value == null ? 0 : value.hashCode()); >+ } >+ } >+ >+ public boolean isStale() { >+ getterCalled(); >+ return map.isStale(); >+ } >+ >+ private void getterCalled() { >+ ObservableTracker.getterCalled(this); >+ } >+ >+ public Object getObserved() { >+ return map; >+ } >+ >+ public synchronized void dispose() { >+ if (!disposed) { >+ disposed = true; >+ property = null; >+ } >+ >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/masterdetail/SetPropertyDetailValueMap.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/masterdetail/SetPropertyDetailValueMap.java >diff -N src/org/eclipse/core/internal/databinding/property/masterdetail/SetPropertyDetailValueMap.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/masterdetail/SetPropertyDetailValueMap.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,217 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property.masterdetail; >+ >+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.map.MapDiff; >+import org.eclipse.core.databinding.property.ISetProperty; >+import org.eclipse.core.databinding.property.ISetPropertyChangeListener; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.MapProperty; >+import org.eclipse.core.databinding.property.SetPropertyChangeEvent; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class SetPropertyDetailValueMap extends MapProperty { >+ private final ISetProperty masterProperty; >+ private final IValueProperty detailProperty; >+ >+ private Map sourceToMasterElementToDetailListener = new HashMap(); >+ >+ private ISetPropertyChangeListener masterListener = new MasterPropertyListener(); >+ >+ private class MasterPropertyListener implements ISetPropertyChangeListener { >+ public void handleSetPropertyChange(final SetPropertyChangeEvent event) { >+ Object source = event.getSource(); >+ >+ MapDiff diff; >+ if (event.diff == null) { >+ diff = null; >+ } else { >+ final Set removals = event.diff.getRemovals(); >+ final Set additions = event.diff.getAdditions(); >+ >+ diff = new MapDiff() { >+ public Set getAddedKeys() { >+ return additions; >+ } >+ >+ public Set getChangedKeys() { >+ return Collections.EMPTY_SET; >+ } >+ >+ public Object getNewValue(Object key) { >+ return getValue(key); >+ } >+ >+ public Object getOldValue(Object key) { >+ return getValue(key); >+ } >+ >+ private Object getValue(Object key) { >+ return detailProperty.getValue(key); >+ } >+ >+ public Set getRemovedKeys() { >+ return removals; >+ } >+ }; >+ >+ for (Iterator it = removals.iterator(); it.hasNext();) { >+ removeElementPropertyListener(source, it.next()); >+ } >+ for (Iterator it = additions.iterator(); it.hasNext();) { >+ addElementPropertyListener(source, it.next()); >+ } >+ } >+ >+ fireMapChange(source, diff); >+ } >+ >+ private void addElementPropertyListener(Object source, Object element) { >+ Map elementToDetailListener = (Map) sourceToMasterElementToDetailListener >+ .get(source); >+ if (elementToDetailListener == null) { >+ sourceToMasterElementToDetailListener.put(source, >+ elementToDetailListener = new HashMap()); >+ } >+ if (!elementToDetailListener.containsKey(element)) { >+ DetailPropertyListener detailListener = new DetailPropertyListener( >+ source, element); >+ detailProperty.addValueChangeListener(element, detailListener); >+ elementToDetailListener.put(element, detailListener); >+ } >+ } >+ >+ private void removeElementPropertyListener(Object source, Object element) { >+ Map elementToDetailListener = (Map) sourceToMasterElementToDetailListener >+ .get(source); >+ if (elementToDetailListener != null >+ && elementToDetailListener.containsKey(element)) { >+ DetailPropertyListener detailListener = (DetailPropertyListener) elementToDetailListener >+ .remove(element); >+ detailProperty.removeValueChangeListener(element, >+ detailListener); >+ } >+ } >+ } >+ >+ private class DetailPropertyListener implements >+ IValuePropertyChangeListener { >+ private Object source; >+ private Object masterValue; >+ >+ DetailPropertyListener(Object source, Object masterValue) { >+ this.source = source; >+ this.masterValue = masterValue; >+ } >+ >+ public void handleValuePropertyChange(ValuePropertyChangeEvent event) { >+ MapDiff diff; >+ if (event.diff == null) { >+ diff = null; >+ } else { >+ diff = Diffs.createMapDiffSingleChange(masterValue, event.diff >+ .getOldValue(), event.diff.getNewValue()); >+ } >+ fireMapChange(source, diff); >+ } >+ } >+ >+ /** >+ * @param masterProperty >+ * @param detailProperty >+ */ >+ public SetPropertyDetailValueMap(ISetProperty masterProperty, >+ IValueProperty detailProperty) { >+ this.masterProperty = masterProperty; >+ this.detailProperty = detailProperty; >+ } >+ >+ protected void addListenerTo(Object source) { >+ masterProperty.addSetChangeListener(source, masterListener); >+ >+ Map masterElementToDetailListener = new HashMap(); >+ for (Iterator it = masterProperty.getSet(source).iterator(); it >+ .hasNext();) { >+ Object masterElement = it.next(); >+ DetailPropertyListener detailListener = new DetailPropertyListener( >+ source, masterElement); >+ detailProperty >+ .addValueChangeListener(masterElement, detailListener); >+ masterElementToDetailListener.put(masterElement, detailListener); >+ } >+ >+ sourceToMasterElementToDetailListener.put(source, >+ masterElementToDetailListener); >+ } >+ >+ protected void removeListenerFrom(Object source) { >+ masterProperty.removeSetChangeListener(source, masterListener); >+ Map masterElementToDetailListener = (Map) sourceToMasterElementToDetailListener >+ .remove(source); >+ if (masterElementToDetailListener != null) { >+ for (Iterator it = masterElementToDetailListener.entrySet() >+ .iterator(); it.hasNext();) { >+ Map.Entry entry = (Map.Entry) it.next(); >+ detailProperty.removeValueChangeListener(entry.getKey(), >+ (DetailPropertyListener) entry.getValue()); >+ } >+ } >+ } >+ >+ public Map getMap(Object source) { >+ Map result = new HashMap(); >+ for (Iterator it = masterProperty.getSet(source).iterator(); it >+ .hasNext();) { >+ Object element = it.next(); >+ result.put(element, detailProperty.getValue(element)); >+ } >+ return result; >+ } >+ >+ public void clear(Object source) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public Object put(Object source, Object key, Object value) { >+ if (!masterProperty.contains(source, key)) >+ return null; >+ Object result = detailProperty.getValue(key); >+ detailProperty.setValue(key, value); >+ return result; >+ } >+ >+ public void putAll(Object source, Map t) { >+ Set masterSet = masterProperty.getSet(source); >+ for (Iterator it = t.entrySet().iterator(); it.hasNext();) { >+ Map.Entry entry = (Map.Entry) it.next(); >+ if (masterSet.contains(entry.getKey())) { >+ detailProperty.setValue(entry.getKey(), entry.getValue()); >+ } >+ } >+ } >+ >+ public Object remove(Object source, Object key) { >+ throw new UnsupportedOperationException(); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailList.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailList.java >diff -N src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailList.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailList.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,173 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property.masterdetail; >+ >+import java.util.Collection; >+import java.util.HashMap; >+import java.util.List; >+import java.util.Map; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.property.IListProperty; >+import org.eclipse.core.databinding.property.IListPropertyChangeListener; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.ListProperty; >+import org.eclipse.core.databinding.property.ListPropertyChangeEvent; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class ValuePropertyDetailList extends ListProperty { >+ private final IValueProperty masterProperty; >+ private final IListProperty detailProperty; >+ >+ private Map sourceToDetailListener = new HashMap(); >+ >+ private IValuePropertyChangeListener masterListener = new MasterPropertyListener(); >+ >+ private class MasterPropertyListener implements >+ IValuePropertyChangeListener { >+ public void handleValuePropertyChange(ValuePropertyChangeEvent event) { >+ Object oldSource = event.diff.getOldValue(); >+ Object newSource = event.diff.getNewValue(); >+ >+ List oldList = detailProperty.getList(oldSource); >+ List newList = detailProperty.getList(newSource); >+ >+ Object source = event.getSource(); >+ >+ removeDetailPropertyListener(source); >+ addDetailPropertyListener(source); >+ >+ fireListChange(source, Diffs.computeListDiff(oldList, newList)); >+ } >+ } >+ >+ private class DetailPropertyListener implements IListPropertyChangeListener { >+ private Object source; >+ private Object masterValue; >+ >+ DetailPropertyListener(Object source, Object masterValue) { >+ this.source = source; >+ this.masterValue = masterValue; >+ } >+ >+ public void handleListPropertyChange(ListPropertyChangeEvent event) { >+ fireListChange(source, event.diff); >+ } >+ } >+ >+ /** >+ * @param masterProperty >+ * @param detailProperty >+ */ >+ public ValuePropertyDetailList(IValueProperty masterProperty, >+ IListProperty detailProperty) { >+ this.masterProperty = masterProperty; >+ this.detailProperty = detailProperty; >+ } >+ >+ protected void addListenerTo(Object source) { >+ masterProperty.addValueChangeListener(source, masterListener); >+ addDetailPropertyListener(source); >+ } >+ >+ protected void removeListenerFrom(Object source) { >+ masterProperty.removeValueChangeListener(source, masterListener); >+ removeDetailPropertyListener(source); >+ } >+ >+ private void addDetailPropertyListener(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ DetailPropertyListener detailListener = new DetailPropertyListener( >+ source, masterValue); >+ detailProperty.addListChangeListener(masterValue, detailListener); >+ sourceToDetailListener.put(source, detailListener); >+ } >+ >+ private void removeDetailPropertyListener(Object source) { >+ DetailPropertyListener detailListener = (DetailPropertyListener) sourceToDetailListener >+ .remove(source); >+ if (detailListener != null) { >+ detailProperty.removeListChangeListener(detailListener.masterValue, >+ detailListener); >+ } >+ sourceToDetailListener.remove(source); >+ } >+ >+ public List getList(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.getList(masterValue); >+ } >+ >+ public boolean add(Object source, Object o) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.add(masterValue, o); >+ } >+ >+ public boolean addAll(Object source, Collection c) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.addAll(masterValue, c); >+ } >+ >+ public void clear(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ detailProperty.clear(masterValue); >+ } >+ >+ public Object getElementType() { >+ return detailProperty.getElementType(); >+ } >+ >+ public boolean remove(Object source, Object o) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.remove(masterValue, o); >+ } >+ >+ public boolean removeAll(Object source, Collection c) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.removeAll(masterValue, c); >+ } >+ >+ public boolean retainAll(Object source, Collection c) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.retainAll(masterValue, c); >+ } >+ >+ public void add(Object source, int index, Object element) { >+ Object masterValue = masterProperty.getValue(source); >+ detailProperty.add(masterValue, index, element); >+ } >+ >+ public boolean addAll(Object source, int index, Collection c) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.addAll(masterValue, index, c); >+ } >+ >+ public Object move(Object source, int oldIndex, int newIndex) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.move(masterValue, oldIndex, newIndex); >+ } >+ >+ public Object remove(Object source, int index) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.remove(masterValue, index); >+ } >+ >+ public Object set(Object source, int index, Object element) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.set(masterValue, index, element); >+ } >+} >Index: src/org/eclipse/core/databinding/property/SetProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/SetProperty.java >diff -N src/org/eclipse/core/databinding/property/SetProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/SetProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,51 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.Collection; >+ >+import org.eclipse.core.databinding.observable.set.SetDiff; >+ >+/** >+ * Abstract implementation of ISetProperty >+ * >+ * @since 1.2 >+ */ >+public abstract class SetProperty extends CollectionProperty implements >+ ISetProperty { >+ Collection getCollection(Object source) { >+ return getSet(source); >+ } >+ >+ public final void addSetChangeListener(Object source, >+ ISetPropertyChangeListener listener) { >+ getChangeSupport().addListener(source, listener); >+ } >+ >+ public final void removeSetChangeListener(Object source, >+ ISetPropertyChangeListener listener) { >+ getChangeSupport().removeListener(source, listener); >+ } >+ >+ /** >+ * Fires a SetPropertyChangeEvent with the given source and diff >+ * >+ * @param source >+ * the property source >+ * @param diff >+ * the set diff >+ */ >+ protected void fireSetChange(Object source, SetDiff diff) { >+ getChangeSupport().firePropertyChange( >+ new SetPropertyChangeEvent(source, this, diff)); >+ } >+} >Index: src/org/eclipse/core/databinding/property/IProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/IProperty.java >diff -N src/org/eclipse/core/databinding/property/IProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/IProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,25 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+/** >+ * Interface for observing a property of a source object. >+ * >+ * @since 1.2 >+ * @noimplement This interface is not intended to be implemented by clients. >+ */ >+public interface IProperty { >+ /** >+ * Disposes the property, removing all property listeners on source objects. >+ */ >+ public void dispose(); >+} >Index: src/org/eclipse/core/internal/databinding/property/masterdetail/ListPropertyDetailValueList.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/masterdetail/ListPropertyDetailValueList.java >diff -N src/org/eclipse/core/internal/databinding/property/masterdetail/ListPropertyDetailValueList.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/masterdetail/ListPropertyDetailValueList.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,259 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property.masterdetail; >+ >+import java.util.ArrayList; >+import java.util.Collection; >+import java.util.HashMap; >+import java.util.HashSet; >+import java.util.Iterator; >+import java.util.List; >+import java.util.ListIterator; >+import java.util.Map; >+import java.util.Set; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.list.ListDiff; >+import org.eclipse.core.databinding.observable.list.ListDiffEntry; >+import org.eclipse.core.databinding.property.IListProperty; >+import org.eclipse.core.databinding.property.IListPropertyChangeListener; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.ListProperty; >+import org.eclipse.core.databinding.property.ListPropertyChangeEvent; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+import org.eclipse.core.internal.databinding.Util; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class ListPropertyDetailValueList extends ListProperty { >+ private final IListProperty masterProperty; >+ private final IValueProperty detailProperty; >+ >+ private Map sourceToMasterElementToDetailListener = new HashMap(); >+ >+ private IListPropertyChangeListener masterListener = new MasterPropertyListener(); >+ >+ private class MasterPropertyListener implements IListPropertyChangeListener { >+ public void handleListPropertyChange(ListPropertyChangeEvent event) { >+ Object source = event.getSource(); >+ ListDiffEntry[] masterEntries = event.diff.getDifferences(); >+ ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length]; >+ Set masterElementsAdded = new HashSet(); >+ Set masterElementsRemoved = new HashSet(); >+ for (int i = 0; i < masterEntries.length; i++) { >+ ListDiffEntry masterDifference = masterEntries[i]; >+ int index = masterDifference.getPosition(); >+ boolean addition = masterDifference.isAddition(); >+ Object masterElement = masterDifference.getElement(); >+ Object elementDetailValue = detailProperty >+ .getValue(masterElement); >+ detailEntries[i] = Diffs.createListDiffEntry(index, addition, >+ elementDetailValue); >+ if (addition) >+ masterElementsAdded.add(masterElement); >+ else >+ masterElementsRemoved.add(masterElement); >+ } >+ >+ ListDiff diff = Diffs.createListDiff(detailEntries); >+ >+ for (Iterator it = masterElementsRemoved.iterator(); it.hasNext();) { >+ Object element = it.next(); >+ if (!masterProperty.contains(source, element)) >+ removeElementPropertyListener(source, element); >+ } >+ for (Iterator it = masterElementsAdded.iterator(); it.hasNext();) { >+ Object element = it.next(); >+ if (masterProperty.contains(source, element)) >+ addElementPropertyListener(source, element); >+ } >+ >+ fireListChange(source, diff); >+ } >+ >+ private void addElementPropertyListener(Object source, Object element) { >+ Map elementToDetailListener = (Map) sourceToMasterElementToDetailListener >+ .get(source); >+ if (elementToDetailListener == null) { >+ sourceToMasterElementToDetailListener.put(source, >+ elementToDetailListener = new HashMap()); >+ } >+ if (!elementToDetailListener.containsKey(element)) { >+ DetailPropertyListener detailListener = new DetailPropertyListener( >+ source, element); >+ detailProperty.addValueChangeListener(element, detailListener); >+ elementToDetailListener.put(element, detailListener); >+ } >+ } >+ >+ private void removeElementPropertyListener(Object source, Object element) { >+ Map elementToDetailListener = (Map) sourceToMasterElementToDetailListener >+ .get(source); >+ if (elementToDetailListener != null >+ && elementToDetailListener.containsKey(element)) { >+ DetailPropertyListener detailListener = (DetailPropertyListener) elementToDetailListener >+ .remove(element); >+ detailProperty.removeValueChangeListener(element, >+ detailListener); >+ } >+ } >+ } >+ >+ private class DetailPropertyListener implements >+ IValuePropertyChangeListener { >+ private Object source; >+ private Object masterElement; >+ >+ DetailPropertyListener(Object source, Object masterElement) { >+ this.source = source; >+ this.masterElement = masterElement; >+ } >+ >+ private int[] findIndices() { >+ List indices = new ArrayList(); >+ >+ List list = masterProperty.getList(source); >+ for (ListIterator it = list.listIterator(); it.hasNext();) { >+ if (Util.equals(masterElement, it.next())) >+ indices.add(new Integer(it.previousIndex())); >+ } >+ >+ int[] result = new int[indices.size()]; >+ for (int i = 0; i < result.length; i++) { >+ result[i] = ((Integer) indices.get(i)).intValue(); >+ } >+ return result; >+ } >+ >+ public void handleValuePropertyChange(ValuePropertyChangeEvent event) { >+ int[] indices = findIndices(); >+ Object oldValue = event.diff.getOldValue(); >+ Object newValue = event.diff.getNewValue(); >+ ListDiffEntry[] entries = new ListDiffEntry[indices.length * 2]; >+ for (int i = 0; i < indices.length; i++) { >+ int index = indices[i]; >+ entries[i * 2] = Diffs.createListDiffEntry(index, false, >+ oldValue); >+ entries[i * 2 + 1] = Diffs.createListDiffEntry(index, true, >+ newValue); >+ } >+ >+ ListDiff diff = Diffs.createListDiff(entries); >+ fireListChange(source, diff); >+ } >+ } >+ >+ /** >+ * @param masterProperty >+ * @param detailProperty >+ */ >+ public ListPropertyDetailValueList(IListProperty masterProperty, >+ IValueProperty detailProperty) { >+ this.masterProperty = masterProperty; >+ this.detailProperty = detailProperty; >+ } >+ >+ protected void addListenerTo(Object source) { >+ masterProperty.addListChangeListener(source, masterListener); >+ >+ Map masterElementToDetailListener = new HashMap(); >+ for (Iterator it = masterProperty.getList(source).iterator(); it >+ .hasNext();) { >+ Object masterElement = it.next(); >+ DetailPropertyListener detailListener = new DetailPropertyListener( >+ source, masterElement); >+ detailProperty >+ .addValueChangeListener(masterElement, detailListener); >+ masterElementToDetailListener.put(masterElement, detailListener); >+ } >+ >+ sourceToMasterElementToDetailListener.put(source, >+ masterElementToDetailListener); >+ } >+ >+ protected void removeListenerFrom(Object source) { >+ masterProperty.removeListChangeListener(source, masterListener); >+ Map masterElementToDetailListener = (Map) sourceToMasterElementToDetailListener >+ .remove(source); >+ if (masterElementToDetailListener != null) { >+ for (Iterator it = masterElementToDetailListener.entrySet() >+ .iterator(); it.hasNext();) { >+ Map.Entry entry = (Map.Entry) it.next(); >+ detailProperty.removeValueChangeListener(entry.getKey(), >+ (DetailPropertyListener) entry.getValue()); >+ } >+ } >+ } >+ >+ public List getList(Object source) { >+ List result = new ArrayList(); >+ for (Iterator it = masterProperty.getList(source).iterator(); it >+ .hasNext();) { >+ result.add(detailProperty.getValue(it.next())); >+ } >+ return result; >+ } >+ >+ public boolean add(Object source, Object o) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean addAll(Object source, Collection c) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public void clear(Object source) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public Object getElementType() { >+ return detailProperty.getValueType(); >+ } >+ >+ public boolean remove(Object source, Object o) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean removeAll(Object source, Collection c) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean retainAll(Object source, Collection c) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public void add(Object source, int index, Object element) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean addAll(Object source, int index, Collection c) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public Object move(Object source, int oldIndex, int newIndex) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public Object remove(Object source, int index) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public Object set(Object source, int index, Object element) { >+ Object masterElement = masterProperty.get(source, index); >+ Object result = detailProperty.getValue(masterElement); >+ detailProperty.setValue(masterElement, element); >+ return result; >+ } >+} >Index: src/org/eclipse/core/databinding/property/IPropertyObservable.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/IPropertyObservable.java >diff -N src/org/eclipse/core/databinding/property/IPropertyObservable.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/IPropertyObservable.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,28 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 IBM Corporation 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: >+ * IBM Corporation - initial API and implementation >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import org.eclipse.core.databinding.observable.IObserving; >+ >+/** >+ * Provides access to the details of property observables >+ * >+ * @since 1.2 >+ */ >+public interface IPropertyObservable extends IObserving { >+ /** >+ * Returns the property being observed >+ * >+ * @return the property being observed >+ */ >+ IProperty getProperty(); >+} >Index: src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailMap.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailMap.java >diff -N src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailMap.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailMap.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,133 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property.masterdetail; >+ >+import java.util.HashMap; >+import java.util.Map; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.map.MapDiff; >+import org.eclipse.core.databinding.property.IMapProperty; >+import org.eclipse.core.databinding.property.IMapPropertyChangeListener; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.MapProperty; >+import org.eclipse.core.databinding.property.MapPropertyChangeEvent; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class ValuePropertyDetailMap extends MapProperty { >+ private final IValueProperty masterProperty; >+ private final IMapProperty detailProperty; >+ >+ private Map sourceToDetailListener = new HashMap(); >+ >+ private IValuePropertyChangeListener masterListener = new MasterValueListener(); >+ >+ private class MasterValueListener implements IValuePropertyChangeListener { >+ public void handleValuePropertyChange(ValuePropertyChangeEvent event) { >+ Object oldSource = event.diff.getOldValue(); >+ Object newSource = event.diff.getNewValue(); >+ >+ Map oldMap = detailProperty.getMap(oldSource); >+ Map newMap = detailProperty.getMap(newSource); >+ MapDiff diff = Diffs.computeMapDiff(oldMap, newMap); >+ >+ Object source = event.getSource(); >+ >+ removeDetailPropertyListener(source); >+ addDetailPropertyListener(source); >+ >+ fireMapChange(source, diff); >+ } >+ } >+ >+ private class DetailPropertyListener implements IMapPropertyChangeListener { >+ private Object source; >+ private Object masterValue; >+ >+ DetailPropertyListener(Object source, Object masterValue) { >+ this.source = source; >+ this.masterValue = masterValue; >+ } >+ >+ public void handleMapPropertyChange(MapPropertyChangeEvent event) { >+ fireMapChange(source, event.diff); >+ } >+ } >+ >+ /** >+ * @param masterProperty >+ * @param detailProperty >+ */ >+ public ValuePropertyDetailMap(IValueProperty masterProperty, >+ IMapProperty detailProperty) { >+ this.masterProperty = masterProperty; >+ this.detailProperty = detailProperty; >+ } >+ >+ protected void addListenerTo(Object source) { >+ masterProperty.addValueChangeListener(source, masterListener); >+ addDetailPropertyListener(source); >+ } >+ >+ protected void removeListenerFrom(Object source) { >+ masterProperty.removeValueChangeListener(source, masterListener); >+ removeDetailPropertyListener(source); >+ } >+ >+ private void addDetailPropertyListener(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ DetailPropertyListener detailListener = new DetailPropertyListener( >+ source, masterValue); >+ detailProperty.addMapChangeListener(masterValue, detailListener); >+ sourceToDetailListener.put(source, detailListener); >+ } >+ >+ private void removeDetailPropertyListener(Object source) { >+ DetailPropertyListener detailListener = (DetailPropertyListener) sourceToDetailListener >+ .remove(source); >+ if (detailListener != null) { >+ detailProperty.removeMapChangeListener(detailListener.masterValue, >+ detailListener); >+ } >+ sourceToDetailListener.remove(source); >+ } >+ >+ public Map getMap(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.getMap(masterValue); >+ } >+ >+ public void clear(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ detailProperty.clear(masterValue); >+ } >+ >+ public Object put(Object source, Object key, Object value) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.put(masterValue, key, value); >+ } >+ >+ public void putAll(Object source, Map t) { >+ Object masterValue = masterProperty.getValue(source); >+ detailProperty.putAll(masterValue, t); >+ } >+ >+ public Object remove(Object source, Object key) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.remove(masterValue, key); >+ } >+} >Index: src/org/eclipse/core/databinding/property/ISetPropertyChangeListener.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/ISetPropertyChangeListener.java >diff -N src/org/eclipse/core/databinding/property/ISetPropertyChangeListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/ISetPropertyChangeListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,27 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+/** >+ * Listener for changes to set properties on a property source >+ * >+ * @since 1.2 >+ */ >+public interface ISetPropertyChangeListener extends IPropertyChangeListener { >+ /** >+ * Handle a change to a set property on a specific property source. >+ * >+ * @param event >+ * an event describing the set change that occured. >+ */ >+ public void handleSetPropertyChange(SetPropertyChangeEvent event); >+} >Index: src/org/eclipse/core/databinding/property/BasicSetProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/BasicSetProperty.java >diff -N src/org/eclipse/core/databinding/property/BasicSetProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/BasicSetProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,186 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 IBM Corporation 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: >+ * IBM Corporation - initial API and implementation >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.Collection; >+import java.util.Collections; >+import java.util.HashSet; >+import java.util.Iterator; >+import java.util.Set; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.set.SetDiff; >+import org.eclipse.core.internal.databinding.property.PropertyUpdateHelper; >+ >+/** >+ * Abstract set property implementation for properties where the set can be >+ * completely replaced in a single atomic operation. >+ * <p> >+ * For example, a set-typed bean property Customer.addresses can be modified by >+ * calling Customer.setAddresses(List addresses). >+ * >+ * @since 1.2 >+ */ >+public abstract class BasicSetProperty extends SetProperty { >+ private PropertyUpdateHelper updateHelper = new PropertyUpdateHelper(); >+ >+ /** >+ * Returns whether this property is currently being updated on the source. >+ * Implementors should query this value to avoid unnecessary calculations, >+ * such as computing a diff. >+ * >+ * @param source >+ * the property source >+ * @return whether this property is currently being updated on the source. >+ */ >+ protected boolean isUpdating(Object source) { >+ return updateHelper.isUpdating(source); >+ } >+ >+ /** >+ * Sets the set property on the source to the specified set, then fires a >+ * set change using the specified diff. >+ * >+ * @param source >+ * the property source >+ * @param set >+ * the new set >+ * @param diff >+ * the diff to be fired after the set property has been set. If >+ * null, the diff will be computed. >+ */ >+ protected void setSet(Object source, Set set, SetDiff diff) { >+ if (diff == null) { >+ diff = Diffs.computeSetDiff(getSet(source), set); >+ } >+ >+ updateHelper.setUpdating(source, true); >+ try { >+ doSetSet(source, set); >+ } finally { >+ updateHelper.setUpdating(source, false); >+ } >+ >+ if (hasListeners(source)) { >+ fireSetChange(source, diff); >+ } >+ } >+ >+ protected void fireSetChange(Object source, SetDiff diff) { >+ if (!isUpdating(source)) >+ super.fireSetChange(source, diff); >+ } >+ >+ /** >+ * Sets the set property on the source to the specified set. >+ * >+ * @param source >+ * the property source >+ * @param set >+ * the new set >+ */ >+ protected abstract void doSetSet(Object source, Set set); >+ >+ public boolean add(Object source, Object o) { >+ Set set = getSet(source); >+ if (!set.contains(o)) { >+ set = new HashSet(set); >+ boolean added = set.add(o); >+ if (added) >+ setSet(source, set, Diffs.createSetDiff(Collections >+ .singleton(o), Collections.EMPTY_SET)); >+ return added; >+ } >+ return false; >+ } >+ >+ public boolean addAll(Object source, Collection c) { >+ Set set = getSet(source); >+ Set additions = new HashSet(); >+ for (Iterator it = c.iterator(); it.hasNext();) { >+ Object o = it.next(); >+ if (!set.contains(o)) { >+ additions.add(o); >+ } >+ } >+ boolean changed = !additions.isEmpty(); >+ if (changed) { >+ set = new HashSet(set); >+ set.addAll(additions); >+ setSet(source, set, Diffs.createSetDiff(additions, >+ Collections.EMPTY_SET)); >+ } >+ return changed; >+ } >+ >+ public void clear(Object source) { >+ setSet(source, new HashSet(), Diffs.createSetDiff( >+ Collections.EMPTY_SET, getSet(source))); >+ } >+ >+ public boolean remove(Object source, Object o) { >+ Set set = getSet(source); >+ if (set.contains(o)) { >+ set = new HashSet(set); >+ boolean removed = set.remove(o); >+ if (removed) >+ setSet(source, set, Diffs.createSetDiff(Collections.EMPTY_SET, >+ Collections.singleton(o))); >+ return removed; >+ } >+ return false; >+ } >+ >+ public boolean removeAll(Object source, Collection c) { >+ Set set = new HashSet(getSet(source)); >+ Set removals = new HashSet(); >+ for (Iterator it = set.iterator(); it.hasNext();) { >+ Object o = it.next(); >+ if (c.contains(o)) { >+ removals.add(o); >+ it.remove(); >+ } >+ } >+ boolean changed = !removals.isEmpty(); >+ if (changed) { >+ setSet(source, set, Diffs.createSetDiff(Collections.EMPTY_SET, >+ removals)); >+ } >+ return changed; >+ } >+ >+ public boolean retainAll(Object source, Collection c) { >+ Set set = new HashSet(getSet(source)); >+ Set removals = new HashSet(); >+ for (Iterator it = set.iterator(); it.hasNext();) { >+ Object o = it.next(); >+ if (!c.contains(o)) { >+ removals.add(o); >+ it.remove(); >+ } >+ } >+ boolean changed = !removals.isEmpty(); >+ if (changed) { >+ setSet(source, set, Diffs.createSetDiff(Collections.EMPTY_SET, >+ removals)); >+ } >+ return changed; >+ } >+ >+ public synchronized void dispose() { >+ if (updateHelper != null) { >+ updateHelper.dispose(); >+ updateHelper = null; >+ } >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/databinding/property/MapPropertyChangeEvent.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/MapPropertyChangeEvent.java >diff -N src/org/eclipse/core/databinding/property/MapPropertyChangeEvent.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/MapPropertyChangeEvent.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,57 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import org.eclipse.core.databinding.observable.map.MapDiff; >+import org.eclipse.core.runtime.Assert; >+ >+/** >+ * Map change event describing an incremental change of a map property on a >+ * particular property source. >+ * >+ * @since 1.2 >+ */ >+public class MapPropertyChangeEvent extends PropertyChangeEvent { >+ private static final long serialVersionUID = 1L; >+ >+ /** >+ * The map property that changed >+ */ >+ public final IMapProperty property; >+ >+ /** >+ * MapDiff enumerating the added, changed, and removed entries in the map. >+ */ >+ public final MapDiff diff; >+ >+ /** >+ * Constructs a MapPropertyChangeEvent with the given attributes >+ * >+ * @param source >+ * the property source >+ * @param property >+ * the property that changed on the source >+ * @param diff >+ * a MapDiff describing the changes to the map property >+ */ >+ public MapPropertyChangeEvent(Object source, IMapProperty property, >+ MapDiff diff) { >+ super(source); >+ this.property = property; >+ Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$ >+ this.diff = diff; >+ } >+ >+ void dispatch(IPropertyChangeListener listener) { >+ ((IMapPropertyChangeListener) listener).handleMapPropertyChange(this); >+ } >+} >Index: src/org/eclipse/core/databinding/property/PropertyChangeEvent.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/PropertyChangeEvent.java >diff -N src/org/eclipse/core/databinding/property/PropertyChangeEvent.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/PropertyChangeEvent.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,24 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.EventObject; >+ >+abstract class PropertyChangeEvent extends EventObject { >+ private static final long serialVersionUID = 1L; >+ >+ PropertyChangeEvent(Object source) { >+ super(source); >+ } >+ >+ abstract void dispatch(IPropertyChangeListener listener); >+} >Index: src/org/eclipse/core/internal/databinding/property/masterdetail/MapPropertyDetailValueMap.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/masterdetail/MapPropertyDetailValueMap.java >diff -N src/org/eclipse/core/internal/databinding/property/masterdetail/MapPropertyDetailValueMap.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/masterdetail/MapPropertyDetailValueMap.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,223 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property.masterdetail; >+ >+import java.util.HashMap; >+import java.util.HashSet; >+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.map.MapDiff; >+import org.eclipse.core.databinding.property.IMapProperty; >+import org.eclipse.core.databinding.property.IMapPropertyChangeListener; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.MapProperty; >+import org.eclipse.core.databinding.property.MapPropertyChangeEvent; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+import org.eclipse.core.internal.databinding.Util; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class MapPropertyDetailValueMap extends MapProperty { >+ private final IMapProperty masterProperty; >+ private final IValueProperty detailProperty; >+ >+ private Map sourceToKeyToDetailListener = new HashMap(); >+ >+ private IMapPropertyChangeListener masterListener = new MasterPropertyListener(); >+ >+ private class MasterPropertyListener implements IMapPropertyChangeListener { >+ public void handleMapPropertyChange(final MapPropertyChangeEvent event) { >+ Object source = event.getSource(); >+ >+ Map oldValues = new HashMap(); >+ Map newValues = new HashMap(); >+ >+ Set addedKeys = event.diff.getAddedKeys(); >+ for (Iterator it = addedKeys.iterator(); it.hasNext();) { >+ Object key = it.next(); >+ Object newMasterValue = event.diff.getNewValue(key); >+ Object newDetailValue = detailProperty.getValue(newMasterValue); >+ newValues.put(key, newDetailValue); >+ addPropertySourceListener(source, key, newMasterValue); >+ } >+ >+ Set removedKeys = event.diff.getRemovedKeys(); >+ for (Iterator it = removedKeys.iterator(); it.hasNext();) { >+ Object key = it.next(); >+ Object oldMasterValue = event.diff.getOldValue(key); >+ Object oldDetailValue = detailProperty.getValue(oldMasterValue); >+ oldValues.put(key, oldDetailValue); >+ removePropertySourceListener(source, key, oldMasterValue); >+ } >+ >+ Set changedKeys = new HashSet(event.diff.getChangedKeys()); >+ for (Iterator it = changedKeys.iterator(); it.hasNext();) { >+ Object key = it.next(); >+ >+ Object oldMasterValue = event.diff.getOldValue(key); >+ Object newMasterValue = event.diff.getNewValue(key); >+ >+ Object oldDetailValue = detailProperty.getValue(oldMasterValue); >+ Object newDetailValue = detailProperty.getValue(newMasterValue); >+ >+ if (Util.equals(oldDetailValue, newDetailValue)) { >+ it.remove(); >+ } else { >+ oldValues.put(key, oldDetailValue); >+ newValues.put(key, newDetailValue); >+ } >+ >+ removePropertySourceListener(source, key, oldMasterValue); >+ addPropertySourceListener(source, key, newMasterValue); >+ } >+ >+ MapDiff diff = Diffs.createMapDiff(addedKeys, removedKeys, >+ changedKeys, oldValues, newValues); >+ >+ fireMapChange(source, diff); >+ } >+ >+ private void addPropertySourceListener(Object source, Object key, >+ Object masterValue) { >+ Map keyToDetailListener = (Map) sourceToKeyToDetailListener >+ .get(source); >+ if (keyToDetailListener == null) { >+ sourceToKeyToDetailListener.put(source, >+ keyToDetailListener = new HashMap()); >+ } >+ if (!keyToDetailListener.containsKey(key)) { >+ DetailPropertyListener detailListener = new DetailPropertyListener( >+ source, key); >+ detailProperty.addValueChangeListener(masterValue, >+ detailListener); >+ keyToDetailListener.put(key, detailListener); >+ } >+ } >+ >+ private void removePropertySourceListener(Object source, Object key, >+ Object masterValue) { >+ Map keyToDetailListener = (Map) sourceToKeyToDetailListener >+ .get(source); >+ if (keyToDetailListener != null >+ && keyToDetailListener.containsKey(key)) { >+ DetailPropertyListener detailListener = (DetailPropertyListener) keyToDetailListener >+ .remove(key); >+ detailProperty.removeValueChangeListener(masterValue, >+ detailListener); >+ } >+ } >+ } >+ >+ private class DetailPropertyListener implements >+ IValuePropertyChangeListener { >+ private Object source; >+ private Object key; >+ >+ DetailPropertyListener(Object source, Object key) { >+ this.source = source; >+ this.key = key; >+ } >+ >+ public void handleValuePropertyChange(ValuePropertyChangeEvent event) { >+ fireMapChange(source, Diffs.createMapDiffSingleChange(key, >+ event.diff.getOldValue(), event.diff.getNewValue())); >+ } >+ } >+ >+ /** >+ * @param masterProperty >+ * @param detailProperty >+ */ >+ public MapPropertyDetailValueMap(IMapProperty masterProperty, >+ IValueProperty detailProperty) { >+ this.masterProperty = masterProperty; >+ this.detailProperty = detailProperty; >+ } >+ >+ protected void addListenerTo(Object source) { >+ masterProperty.addMapChangeListener(source, masterListener); >+ >+ Map keyToDetailListener = new HashMap(); >+ for (Iterator it = masterProperty.getMap(source).entrySet().iterator(); it >+ .hasNext();) { >+ Map.Entry entry = (Map.Entry) it.next(); >+ Object key = entry.getKey(); >+ Object masterValue = entry.getValue(); >+ DetailPropertyListener detailListener = new DetailPropertyListener( >+ source, key); >+ detailProperty.addValueChangeListener(masterValue, detailListener); >+ keyToDetailListener.put(masterValue, detailListener); >+ } >+ >+ sourceToKeyToDetailListener.put(source, keyToDetailListener); >+ } >+ >+ protected void removeListenerFrom(Object source) { >+ masterProperty.removeMapChangeListener(source, masterListener); >+ Map masterElementToDetailListener = (Map) sourceToKeyToDetailListener >+ .remove(source); >+ if (masterElementToDetailListener != null) { >+ for (Iterator it = masterElementToDetailListener.entrySet() >+ .iterator(); it.hasNext();) { >+ Map.Entry entry = (Map.Entry) it.next(); >+ detailProperty.removeValueChangeListener(entry.getKey(), >+ (DetailPropertyListener) entry.getValue()); >+ } >+ } >+ } >+ >+ public Map getMap(Object source) { >+ Map result = new HashMap(); >+ for (Iterator it = masterProperty.getMap(source).entrySet().iterator(); it >+ .hasNext();) { >+ Map.Entry entry = (Map.Entry) it.next(); >+ result.put(entry.getKey(), detailProperty >+ .getValue(entry.getValue())); >+ } >+ return result; >+ } >+ >+ public void clear(Object source) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public Object put(Object source, Object key, Object value) { >+ if (!masterProperty.containsKey(source, key)) >+ return null; >+ Object masterValue = masterProperty.get(source, key); >+ >+ Object result = detailProperty.getValue(masterValue); >+ detailProperty.setValue(masterValue, value); >+ return result; >+ } >+ >+ public void putAll(Object source, Map t) { >+ for (Iterator it = t.entrySet().iterator(); it.hasNext();) { >+ Map.Entry entry = (Map.Entry) it.next(); >+ Object masterKey = entry.getKey(); >+ Object detailValue = entry.getValue(); >+ if (masterProperty.getMap(source).containsKey(masterKey)) { >+ detailProperty.setValue(masterKey, detailValue); >+ } >+ } >+ } >+ >+ public Object remove(Object source, Object key) { >+ throw new UnsupportedOperationException(); >+ } >+} >Index: src/org/eclipse/core/databinding/property/ICollectionProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/ICollectionProperty.java >diff -N src/org/eclipse/core/databinding/property/ICollectionProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/ICollectionProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,186 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.Collection; >+ >+/** >+ * Interface for collection-typed properties. >+ * >+ * @since 1.2 >+ * @noimplement This interface is not intended to be implemented by clients. >+ */ >+public interface ICollectionProperty extends IProperty { >+ /** >+ * Returns the size of the source's collection property >+ * >+ * @param source >+ * the property source >+ * @return the size of the source's collection property >+ */ >+ int size(Object source); >+ >+ /** >+ * Returns whether the source's collection property is empty >+ * >+ * @param source >+ * the property source >+ * @return whether the source's collection property is empty >+ */ >+ boolean isEmpty(Object source); >+ >+ /** >+ * Returns whether the source's collection property contains the given >+ * element. >+ * >+ * @param source >+ * the property source >+ * @param o >+ * the element >+ * @return whether the source's collection property contains the given >+ * element. >+ */ >+ boolean contains(Object source, Object o); >+ >+ /** >+ * Returns an array of all elements in the source's collection property >+ * >+ * @param source >+ * the property source >+ * @return an array of all elements in the source's collection property >+ */ >+ Object[] toArray(Object source); >+ >+ /** >+ * Returns an array of all elements in the source's collection property >+ * >+ * @param source >+ * the property source >+ * @param array >+ * the array into which the elements will be copied. If the array >+ * is not large enough to hold all elements, the elements will be >+ * returned in a new array of the same runtime type. >+ * @return an array of all elements in the source's collection property >+ */ >+ Object[] toArray(Object source, Object[] array); >+ >+ /** >+ * Adds the element to the source's collection property >+ * >+ * @param source >+ * the property source >+ * @param o >+ * the element to add >+ * @return whether the element was added to the source's collection property >+ */ >+ boolean add(Object source, Object o); >+ >+ /** >+ * Removes the element from the source's collection property >+ * >+ * @param source >+ * the property source >+ * @param o >+ * the element to remove >+ * @return whether the element was removed from the source's collection >+ * property >+ */ >+ boolean remove(Object source, Object o); >+ >+ /** >+ * Returns whether the source's collection property contains all elements in >+ * the given collection >+ * >+ * @param source >+ * the property source >+ * @param c >+ * the collection of elements to test for >+ * @return whether the source's collection property contains all elements in >+ * the given collection >+ */ >+ boolean containsAll(Object source, Collection c); >+ >+ /** >+ * Adds all elements in the specified collection to the source's collection >+ * property. >+ * >+ * @param source >+ * the property source >+ * @param c >+ * the collection of elements to add. >+ * @return whether the source's collection property was changed >+ */ >+ boolean addAll(Object source, Collection c); >+ >+ /** >+ * Removes all elements from the source's collection property which are >+ * contained in the specified collection. >+ * >+ * @param source >+ * the property source >+ * @param c >+ * the collection of elements to be removed >+ * @return whether the source's collection property was changed >+ */ >+ boolean removeAll(Object source, Collection c); >+ >+ /** >+ * Removes all elements from the source's collection property which are not >+ * contained in the specified collection. >+ * >+ * @param source >+ * the property source >+ * @param c >+ * the collection of elements to retain >+ * @return whether the source's collection property was changed >+ */ >+ boolean retainAll(Object source, Collection c); >+ >+ /** >+ * Removes all elements from the source's collection property. >+ * >+ * @param source >+ * the property source >+ */ >+ void clear(Object source); >+ >+ /** >+ * Returns whether the source's collection property is equal to the >+ * argument. >+ * >+ * @param source >+ * the property source >+ * @param o >+ * the object to test for equality to the source's collection >+ * property >+ * @return whether the source's collection property is equal to the argument >+ */ >+ boolean equals(Object source, Object o); >+ >+ /** >+ * Returns the hash code of the source's collection property. >+ * >+ * @param source >+ * the property source >+ * @return the hash code of the source's collection property >+ */ >+ int hashCode(Object source); >+ >+ /** >+ * Returns the type of the elements in the collection or <code>null</code> >+ * if untyped >+ * >+ * @return the type of the elements in the collection or <code>null</code> >+ * if untyped >+ */ >+ Object getElementType(); >+} >Index: src/org/eclipse/core/databinding/property/ListPropertyChangeEvent.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/ListPropertyChangeEvent.java >diff -N src/org/eclipse/core/databinding/property/ListPropertyChangeEvent.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/ListPropertyChangeEvent.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,57 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import org.eclipse.core.databinding.observable.list.ListDiff; >+import org.eclipse.core.runtime.Assert; >+ >+/** >+ * List change event describing an incremental change of a list property on a >+ * particular property source. >+ * >+ * @since 1.2 >+ */ >+public class ListPropertyChangeEvent extends PropertyChangeEvent { >+ private static final long serialVersionUID = 1L; >+ >+ /** >+ * The list property that changed >+ */ >+ public final IListProperty property; >+ >+ /** >+ * ListDiff enumerating the added and removed elements in the list. >+ */ >+ public final ListDiff diff; >+ >+ /** >+ * Constructs a ListPropertyChangeEvent with the given attributes >+ * >+ * @param source >+ * the property source >+ * @param property >+ * the property that changed on the source >+ * @param diff >+ * a ListDiff describing the changes to the list property >+ */ >+ public ListPropertyChangeEvent(Object source, IListProperty property, >+ ListDiff diff) { >+ super(source); >+ this.property = property; >+ Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$ >+ this.diff = diff; >+ } >+ >+ void dispatch(IPropertyChangeListener listener) { >+ ((IListPropertyChangeListener) listener).handleListPropertyChange(this); >+ } >+} >Index: src/org/eclipse/core/databinding/property/PropertyChangeSupport.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/PropertyChangeSupport.java >diff -N src/org/eclipse/core/databinding/property/PropertyChangeSupport.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/PropertyChangeSupport.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,118 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.Collections; >+import java.util.IdentityHashMap; >+import java.util.Iterator; >+import java.util.Map; >+ >+import org.eclipse.core.runtime.ListenerList; >+ >+class PropertyChangeSupport { >+ private Property property; >+ private Map changeListeners; >+ >+ PropertyChangeSupport(Property property) { >+ this.property = property; >+ this.changeListeners = null; >+ } >+ >+ protected final void addListener(Object source, >+ IPropertyChangeListener listener) { >+ boolean hadListeners = hasListeners(source); >+ >+ if (changeListeners == null) { >+ synchronized (this) { >+ if (changeListeners == null) { >+ changeListeners = Collections >+ .synchronizedMap(new IdentityHashMap()); >+ } >+ } >+ } >+ >+ ListenerList listeners; >+ synchronized (changeListeners) { >+ if (changeListeners.containsKey(source)) { >+ listeners = (ListenerList) changeListeners.get(source); >+ } else { >+ changeListeners.put(source, listeners = new ListenerList()); >+ } >+ } >+ >+ synchronized (listeners) { >+ listeners.add(listener); >+ } >+ >+ if (!hadListeners) >+ property.addListenerTo(source); >+ } >+ >+ /** >+ * Returns whether the change support has listeners registered for the given >+ * property source >+ * >+ * @param source >+ * the property source >+ * @return whether the change support has listeners registered for the given >+ * property source >+ */ >+ public boolean hasListeners(Object source) { >+ return changeListeners != null && changeListeners.containsKey(source); >+ } >+ >+ protected final void removeListener(Object source, >+ IPropertyChangeListener listener) { >+ if (changeListeners != null) { >+ ListenerList listeners = (ListenerList) changeListeners.get(source); >+ if (listeners != null) { >+ listeners.remove(listener); >+ if (listeners.isEmpty()) { >+ synchronized (listeners) { >+ if (listeners.isEmpty()) { >+ changeListeners.remove(source); >+ property.removeListenerFrom(source); >+ } >+ } >+ } >+ } >+ } >+ } >+ >+ protected final void firePropertyChange(PropertyChangeEvent event) { >+ ListenerList listenerList; >+ synchronized (this) { >+ if (changeListeners == null) >+ return; >+ listenerList = (ListenerList) changeListeners >+ .get(event.getSource()); >+ } >+ if (listenerList != null) { >+ Object[] listeners = listenerList.getListeners(); >+ for (int i = 0; i < listeners.length; i++) { >+ event.dispatch((IPropertyChangeListener) listeners[i]); >+ } >+ } >+ } >+ >+ void dispose() { >+ if (changeListeners != null) { >+ for (Iterator iterator = changeListeners.keySet().iterator(); iterator >+ .hasNext();) { >+ Object source = iterator.next(); >+ property.removeListenerFrom(source); >+ iterator.remove(); >+ } >+ changeListeners = null; >+ } >+ } >+} >Index: src/org/eclipse/core/databinding/property/PropertyObservables.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/PropertyObservables.java >diff -N src/org/eclipse/core/databinding/property/PropertyObservables.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/PropertyObservables.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,371 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+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.internal.databinding.property.ListValuePropertyObservableList; >+import org.eclipse.core.internal.databinding.property.MapValuePropertyObservableMap; >+import org.eclipse.core.internal.databinding.property.PropertyObservableList; >+import org.eclipse.core.internal.databinding.property.PropertyObservableMap; >+import org.eclipse.core.internal.databinding.property.PropertyObservableSet; >+import org.eclipse.core.internal.databinding.property.PropertyObservableValue; >+import org.eclipse.core.internal.databinding.property.SetValuePropertyObservableMap; >+ >+/** >+ * A factory for creating observables observing properties of source objects >+ * >+ * @since 1.2 >+ */ >+public class PropertyObservables { >+ /** >+ * Returns an observable value on the default realm that tracks the given >+ * property of the source object. >+ * >+ * @param source >+ * the property value >+ * @param property >+ * the value property to observe >+ * @return an observable value on the default realm that tracks the given >+ * property of the source object. >+ * @since 1.2 >+ */ >+ public static IObservableValue observeValue(Object source, >+ IValueProperty property) { >+ return observeValue(Realm.getDefault(), source, property); >+ } >+ >+ /** >+ * Returns an observable value on the given realm that tracks the given >+ * property of the source object >+ * >+ * @param realm >+ * the realm >+ * @param source >+ * the property source >+ * @param property >+ * the value property observe >+ * @return an observable value that tracks the given property of the source >+ * object >+ * @since 1.2 >+ */ >+ public static IObservableValue observeValue(Realm realm, Object source, >+ IValueProperty property) { >+ return new PropertyObservableValue(realm, source, property); >+ } >+ >+ /** >+ * Returns a factory for creating observable values on the default realm >+ * tracking the given property of a particular source object >+ * >+ * @param property >+ * the property to observe >+ * @return a factory for creating observable values on the default realm >+ * tracking the given property of a particular source object >+ */ >+ public static IObservableFactory valueFactory(IValueProperty property) { >+ return valueFactory(Realm.getDefault(), property); >+ } >+ >+ /** >+ * Returns a factory for creating observable values on the given realm >+ * tracking the given property of a particular source object >+ * >+ * @param realm >+ * the realm >+ * @param property >+ * the property to observe >+ * @return a factory for creating observable values on the given realm >+ * tracking the given property of a particular source object >+ */ >+ public static IObservableFactory valueFactory(final Realm realm, >+ final IValueProperty property) { >+ return new IObservableFactory() { >+ public IObservable createObservable(Object target) { >+ return observeValue(realm, target, property); >+ } >+ }; >+ } >+ >+ /** >+ * Returns an observable value on the master observable's realm which tracks >+ * the given property of the current value of <code>master</code>. >+ * >+ * @param master >+ * the master observable >+ * @param property >+ * the property to observe >+ * @return an observable value which tracks the given property of the >+ * current value of <code>master</code>. >+ */ >+ public static IObservableValue observeDetailValue(IObservableValue master, >+ IValueProperty property) { >+ return MasterDetailObservables.detailValue(master, valueFactory(master >+ .getRealm(), property), property.getValueType()); >+ } >+ >+ /** >+ * Returns an observable set on the default realm that tracks the given >+ * property of the source object >+ * >+ * @param source >+ * the property source >+ * @param property >+ * the property to observe >+ * @return an observable set on the default realm that tracks the given >+ * property of the source object >+ */ >+ public static IObservableSet observeSet(Object source, ISetProperty property) { >+ return observeSet(Realm.getDefault(), source, property); >+ } >+ >+ /** >+ * Returns an observable set on the given realm that tracks the given >+ * property of the source object >+ * >+ * @param realm >+ * the realm >+ * @param source >+ * the property source >+ * @param property >+ * the property to observe >+ * @return an observable set on the given realm that tracks the given >+ * property of the source object >+ */ >+ public static IObservableSet observeSet(Realm realm, Object source, >+ ISetProperty property) { >+ return new PropertyObservableSet(realm, source, property); >+ } >+ >+ /** >+ * Returns a factory for creating observable sets on the default realm >+ * tracking the given property of a particular source object >+ * >+ * @param property >+ * the property to be observed >+ * @return a factory for creating observable sets on the default realm >+ * tracking the given property of a particular source object >+ */ >+ public static IObservableFactory setFactory(ISetProperty property) { >+ return setFactory(Realm.getDefault(), property); >+ } >+ >+ /** >+ * Returns a factory for creating obervable sets on the given realm tracking >+ * the given property of a particular source object >+ * >+ * @param realm >+ * the realm >+ * @param property >+ * the property to be observed >+ * @return a factory for creating obervable sets on the given realm tracking >+ * the given property of a particular source object >+ */ >+ public static IObservableFactory setFactory(final Realm realm, >+ final ISetProperty property) { >+ return new IObservableFactory() { >+ public IObservable createObservable(Object target) { >+ return observeSet(realm, target, property); >+ } >+ }; >+ } >+ >+ /** >+ * Returns an observable set on the master observable's realm which tracks >+ * the given property of the current value of <code>master</code>. >+ * >+ * @param master >+ * the master observable >+ * @param property >+ * the property to observe >+ * @return an observable set on the given realm which tracks the given >+ * property of the current value of <code>master</code>. >+ */ >+ public static IObservableSet observeDetailSet(IObservableValue master, >+ ISetProperty property) { >+ return MasterDetailObservables.detailSet(master, setFactory(master >+ .getRealm(), property), property.getElementType()); >+ } >+ >+ /** >+ * Returns an observable list on the default realm that tracks the given >+ * property of the source object >+ * >+ * @param source >+ * the property source >+ * @param property >+ * the property to observe >+ * @return an observable list on the default realm that tracks the given >+ * property of the source object >+ */ >+ public static IObservableList observeList(Object source, >+ IListProperty property) { >+ return observeList(Realm.getDefault(), source, property); >+ } >+ >+ /** >+ * Returns an observable list on the given realm that tracks the given >+ * property of the source object >+ * >+ * @param realm >+ * the realm >+ * @param source >+ * the property source >+ * @param property >+ * the property to observe >+ * @return an observable list on the given realm that tracks the given >+ * property of the source object >+ */ >+ public static IObservableList observeList(Realm realm, Object source, >+ IListProperty property) { >+ return new PropertyObservableList(realm, source, property); >+ } >+ >+ /** >+ * Returns a factory for creating observable lists on the default realm >+ * tracking the given property of a particular source object >+ * >+ * @param property >+ * the property to be observed >+ * @return an observable value factory on >+ */ >+ public static IObservableFactory listFactory(IListProperty property) { >+ return listFactory(Realm.getDefault(), property); >+ } >+ >+ /** >+ * Returns a factory for creating obervable lists on the given realm >+ * tracking the given property of a particular source object >+ * >+ * @param realm >+ * the realm >+ * @param property >+ * the property to be observed >+ * @return an observable value factory on >+ */ >+ public static IObservableFactory listFactory(final Realm realm, >+ final IListProperty property) { >+ return new IObservableFactory() { >+ public IObservable createObservable(Object target) { >+ return observeList(realm, target, property); >+ } >+ }; >+ } >+ >+ /** >+ * Returns an observable list on the master observable's realm which tracks >+ * the given property of the current value of <code>master</code>. >+ * >+ * @param master >+ * the master observable >+ * @param property >+ * the property to observe >+ * @return an observable list on the given realm which tracks the given >+ * property of the current value of <code>master</code>. >+ */ >+ public static IObservableList observeDetailList(IObservableValue master, >+ IListProperty property) { >+ return MasterDetailObservables.detailList(master, listFactory(master >+ .getRealm(), property), property.getElementType()); >+ } >+ >+ /** >+ * Returns an observable list on the master observable's realm which tracks >+ * the given property of each element of <code>master</code>. >+ * >+ * @param master >+ * the master observable >+ * @param property >+ * the property to observe on each element in the master >+ * observable >+ * @return an observable >+ */ >+ public static IObservableList observeDetailValues(IObservableList master, >+ IValueProperty property) { >+ return new ListValuePropertyObservableList(master, property); >+ } >+ >+ /** >+ * Returns an observable map on the default realm which tracks the given >+ * property of the source object >+ * >+ * @param source >+ * the property source >+ * @param property >+ * the property to observe >+ * @return an observable map on the default realm which tracks the given >+ * property of the source object >+ */ >+ public static IObservableMap observeMap(Object source, IMapProperty property) { >+ return observeMap(Realm.getDefault(), source, property); >+ } >+ >+ /** >+ * Returns an observable map on the given realm which tracks the given >+ * property of the source object >+ * >+ * @param realm >+ * the realm >+ * @param source >+ * the property source >+ * @param property >+ * the property to observe >+ * @return an observable map on the given realm which tracks the given >+ * property of the source object >+ */ >+ public static IObservableMap observeMap(Realm realm, Object source, >+ IMapProperty property) { >+ return new PropertyObservableMap(realm, source, property); >+ } >+ >+ /** >+ * Returns an observable map on the master observable's realm tracking the >+ * current values of the given property for the elements in the given set. >+ * >+ * @param keySet >+ * the master observable >+ * @param valueProperty >+ * the property to observe on each element of the master >+ * observable >+ * @return an observable map that tracks the current value of the given >+ * property for the elements in the given set. >+ */ >+ public static IObservableMap observeDetailValues(IObservableSet keySet, >+ IValueProperty valueProperty) { >+ return new SetValuePropertyObservableMap(keySet, valueProperty); >+ } >+ >+ /** >+ * Returns an observable map on the master observable's realm which tracks >+ * the current values of the given property for the entry values in the >+ * given map. >+ * >+ * @param map >+ * the master observable >+ * @param valueProperty >+ * the property to observe on each entry value in the master >+ * observable >+ * @return an observable map on the master observable's realm which tracks >+ * the current value of the given property for the elements in the >+ * given map's values collection >+ */ >+ public static IObservableMap observeDetailValues(IObservableMap map, >+ IValueProperty valueProperty) { >+ return new MapValuePropertyObservableMap(map, valueProperty); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailSet.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailSet.java >diff -N src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailSet.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailSet.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,150 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property.masterdetail; >+ >+import java.util.Collection; >+import java.util.HashMap; >+import java.util.Map; >+import java.util.Set; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.set.SetDiff; >+import org.eclipse.core.databinding.property.ISetProperty; >+import org.eclipse.core.databinding.property.ISetPropertyChangeListener; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.SetProperty; >+import org.eclipse.core.databinding.property.SetPropertyChangeEvent; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class ValuePropertyDetailSet extends SetProperty { >+ private final IValueProperty masterProperty; >+ private final ISetProperty detailProperty; >+ >+ private Map sourceToDetailListener = new HashMap(); >+ >+ private IValuePropertyChangeListener masterListener = new MasterPropertyListener(); >+ >+ private class MasterPropertyListener implements >+ IValuePropertyChangeListener { >+ public void handleValuePropertyChange(ValuePropertyChangeEvent event) { >+ Object oldSource = event.diff.getOldValue(); >+ Object newSource = event.diff.getNewValue(); >+ >+ Set oldSet = detailProperty.getSet(oldSource); >+ Set newSet = detailProperty.getSet(newSource); >+ SetDiff diff = Diffs.computeSetDiff(oldSet, newSet); >+ >+ Object source = event.getSource(); >+ >+ removeDetailPropertyListener(source); >+ addDetailPropertyListener(source); >+ >+ fireSetChange(source, diff); >+ } >+ } >+ >+ private class DetailPropertyListener implements ISetPropertyChangeListener { >+ private Object source; >+ private Object masterValue; >+ >+ DetailPropertyListener(Object source, Object masterValue) { >+ this.source = source; >+ this.masterValue = masterValue; >+ } >+ >+ public void handleSetPropertyChange(SetPropertyChangeEvent event) { >+ fireSetChange(source, event.diff); >+ } >+ } >+ >+ /** >+ * @param masterProperty >+ * @param detailProperty >+ */ >+ public ValuePropertyDetailSet(IValueProperty masterProperty, >+ ISetProperty detailProperty) { >+ this.masterProperty = masterProperty; >+ this.detailProperty = detailProperty; >+ } >+ >+ protected void addListenerTo(Object source) { >+ masterProperty.addValueChangeListener(source, masterListener); >+ addDetailPropertyListener(source); >+ } >+ >+ protected void removeListenerFrom(Object source) { >+ masterProperty.removeValueChangeListener(source, masterListener); >+ removeDetailPropertyListener(source); >+ } >+ >+ private void addDetailPropertyListener(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ DetailPropertyListener detailListener = new DetailPropertyListener( >+ source, masterValue); >+ detailProperty.addSetChangeListener(masterValue, detailListener); >+ sourceToDetailListener.put(source, detailListener); >+ } >+ >+ private void removeDetailPropertyListener(Object source) { >+ DetailPropertyListener detailListener = (DetailPropertyListener) sourceToDetailListener >+ .remove(source); >+ if (detailListener != null) { >+ detailProperty.removeSetChangeListener(detailListener.masterValue, >+ detailListener); >+ } >+ sourceToDetailListener.remove(source); >+ } >+ >+ public Set getSet(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.getSet(masterValue); >+ } >+ >+ public boolean add(Object source, Object o) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.add(masterValue, o); >+ } >+ >+ public boolean addAll(Object source, Collection c) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.addAll(masterValue, c); >+ } >+ >+ public void clear(Object source) { >+ Object masterValue = masterProperty.getValue(source); >+ detailProperty.clear(masterValue); >+ } >+ >+ public Object getElementType() { >+ return detailProperty.getElementType(); >+ } >+ >+ public boolean remove(Object source, Object o) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.remove(masterValue, o); >+ } >+ >+ public boolean removeAll(Object source, Collection c) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.removeAll(masterValue, c); >+ } >+ >+ public boolean retainAll(Object source, Collection c) { >+ Object masterValue = masterProperty.getValue(source); >+ return detailProperty.retainAll(masterValue, c); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/PropertyObservableList.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/PropertyObservableList.java >diff -N src/org/eclipse/core/internal/databinding/property/PropertyObservableList.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/PropertyObservableList.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,374 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property; >+ >+import java.util.ArrayList; >+import java.util.Collection; >+import java.util.Collections; >+import java.util.ConcurrentModificationException; >+import java.util.Iterator; >+import java.util.List; >+import java.util.ListIterator; >+ >+import org.eclipse.core.databinding.observable.ObservableTracker; >+import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.list.AbstractObservableList; >+import org.eclipse.core.databinding.property.IListProperty; >+import org.eclipse.core.databinding.property.IListPropertyChangeListener; >+import org.eclipse.core.databinding.property.IProperty; >+import org.eclipse.core.databinding.property.IPropertyObservable; >+import org.eclipse.core.databinding.property.ListPropertyChangeEvent; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class PropertyObservableList extends AbstractObservableList implements >+ IPropertyObservable { >+ private Object source; >+ private IListProperty property; >+ >+ private volatile boolean updating = false; >+ >+ private boolean disposed = false; >+ >+ private transient volatile int modCount = 0; >+ >+ private IListPropertyChangeListener listener = new IListPropertyChangeListener() { >+ public void handleListPropertyChange(final ListPropertyChangeEvent event) { >+ if (!disposed && !updating) { >+ getRealm().exec(new Runnable() { >+ public void run() { >+ getRealm().exec(new Runnable() { >+ public void run() { >+ modCount++; >+ fireListChange(event.diff); >+ } >+ }); >+ } >+ }); >+ } >+ } >+ }; >+ >+ /** >+ * @param realm >+ * @param source >+ * @param property >+ */ >+ public PropertyObservableList(Realm realm, Object source, >+ IListProperty property) { >+ super(realm); >+ this.source = source; >+ this.property = property; >+ } >+ >+ protected void firstListenerAdded() { >+ if (!disposed) { >+ property.addListChangeListener(source, listener); >+ } >+ } >+ >+ protected void lastListenerRemoved() { >+ if (!disposed) { >+ property.removeListChangeListener(source, listener); >+ } >+ } >+ >+ private void getterCalled() { >+ ObservableTracker.getterCalled(this); >+ } >+ >+ public Object getElementType() { >+ return property.getElementType(); >+ } >+ >+ // Queries >+ >+ protected int doGetSize() { >+ return property.size(source); >+ } >+ >+ public boolean contains(Object o) { >+ getterCalled(); >+ return property.contains(source, o); >+ } >+ >+ public boolean containsAll(Collection c) { >+ getterCalled(); >+ return property.containsAll(source, c); >+ } >+ >+ public Object get(int index) { >+ getterCalled(); >+ return property.get(source, index); >+ } >+ >+ public int indexOf(Object o) { >+ getterCalled(); >+ return property.indexOf(source, o); >+ } >+ >+ public boolean isEmpty() { >+ getterCalled(); >+ return property.isEmpty(source); >+ } >+ >+ public int lastIndexOf(Object o) { >+ getterCalled(); >+ return property.lastIndexOf(source, o); >+ } >+ >+ public Object[] toArray() { >+ getterCalled(); >+ return property.toArray(source); >+ } >+ >+ public Object[] toArray(Object[] a) { >+ getterCalled(); >+ return property.toArray(source, a); >+ } >+ >+ // Single change operations >+ >+ public boolean add(Object o) { >+ checkRealm(); >+ return property.add(source, o); >+ } >+ >+ public Iterator iterator() { >+ getterCalled(); >+ return new Iterator() { >+ int lastReturned = -1; >+ int expectedModCount = modCount; >+ ListIterator delegate = new ArrayList(property.getList(source)) >+ .listIterator(); >+ >+ public boolean hasNext() { >+ getterCalled(); >+ checkForComodification(); >+ return delegate.hasNext(); >+ } >+ >+ public Object next() { >+ getterCalled(); >+ checkForComodification(); >+ Object next = delegate.next(); >+ lastReturned = delegate.previousIndex(); >+ return next; >+ } >+ >+ public void remove() { >+ checkRealm(); >+ checkForComodification(); >+ if (lastReturned == -1) >+ throw new IllegalStateException(); >+ >+ delegate.remove(); // stay in sync >+ >+ property.remove(source, lastReturned); >+ >+ lastReturned = -1; >+ expectedModCount = modCount; >+ } >+ >+ private void checkForComodification() { >+ if (expectedModCount != modCount) >+ throw new ConcurrentModificationException(); >+ } >+ }; >+ } >+ >+ public Object move(int oldIndex, int newIndex) { >+ getterCalled(); >+ return property.move(source, oldIndex, newIndex); >+ } >+ >+ public boolean remove(Object o) { >+ getterCalled(); >+ return property.remove(source, o); >+ } >+ >+ public void add(int index, Object o) { >+ getterCalled(); >+ property.add(source, index, o); >+ } >+ >+ public ListIterator listIterator() { >+ return listIterator(0); >+ } >+ >+ public ListIterator listIterator(final int index) { >+ getterCalled(); >+ return new ListIterator() { >+ int lastReturned = -1; >+ int expectedModCount = modCount; >+ ListIterator delegate = new ArrayList(property.getList(source)) >+ .listIterator(index); >+ >+ public boolean hasNext() { >+ getterCalled(); >+ checkForComodification(); >+ return delegate.hasNext(); >+ } >+ >+ public int nextIndex() { >+ getterCalled(); >+ checkForComodification(); >+ return delegate.nextIndex(); >+ } >+ >+ public Object next() { >+ getterCalled(); >+ checkForComodification(); >+ Object next = delegate.next(); >+ lastReturned = delegate.previousIndex(); >+ return next; >+ } >+ >+ public boolean hasPrevious() { >+ getterCalled(); >+ checkForComodification(); >+ return delegate.hasPrevious(); >+ } >+ >+ public int previousIndex() { >+ getterCalled(); >+ checkForComodification(); >+ return delegate.previousIndex(); >+ } >+ >+ public Object previous() { >+ getterCalled(); >+ checkForComodification(); >+ Object previous = delegate.previous(); >+ lastReturned = delegate.nextIndex(); >+ return previous; >+ } >+ >+ public void add(Object o) { >+ checkRealm(); >+ checkForComodification(); >+ int index = delegate.nextIndex(); >+ >+ delegate.add(o); // keep in sync >+ >+ property.add(source, index, o); >+ >+ lastReturned = -1; >+ expectedModCount = modCount; >+ } >+ >+ public void set(Object o) { >+ checkRealm(); >+ checkForComodification(); >+ >+ delegate.set(o); >+ >+ property.set(source, lastReturned, o); >+ >+ expectedModCount = modCount; >+ } >+ >+ public void remove() { >+ checkRealm(); >+ checkForComodification(); >+ if (lastReturned == -1) >+ throw new IllegalStateException(); >+ >+ delegate.remove(); // keep in sync >+ >+ property.remove(source, lastReturned); >+ >+ lastReturned = -1; >+ expectedModCount = modCount; >+ } >+ >+ private void checkForComodification() { >+ if (expectedModCount != modCount) >+ throw new ConcurrentModificationException(); >+ } >+ >+ }; >+ } >+ >+ public Object remove(int index) { >+ getterCalled(); >+ return property.remove(source, index); >+ } >+ >+ public Object set(int index, Object o) { >+ getterCalled(); >+ return property.set(source, index, o); >+ } >+ >+ public List subList(int fromIndex, int toIndex) { >+ getterCalled(); >+ return Collections.unmodifiableList(property.getList(source).subList( >+ fromIndex, toIndex)); >+ } >+ >+ // Bulk change operations >+ >+ public boolean addAll(Collection c) { >+ getterCalled(); >+ return property.addAll(source, c); >+ } >+ >+ public boolean addAll(int index, Collection c) { >+ getterCalled(); >+ return property.addAll(source, index, c); >+ } >+ >+ public boolean removeAll(Collection c) { >+ getterCalled(); >+ return property.removeAll(source, c); >+ } >+ >+ public boolean retainAll(Collection c) { >+ getterCalled(); >+ return property.retainAll(source, c); >+ } >+ >+ public void clear() { >+ getterCalled(); >+ property.clear(source); >+ } >+ >+ public boolean equals(Object o) { >+ getterCalled(); >+ return property.equals(source, o); >+ } >+ >+ public int hashCode() { >+ getterCalled(); >+ return property.hashCode(source); >+ } >+ >+ public Object getObserved() { >+ return source; >+ } >+ >+ public IProperty getProperty() { >+ return property; >+ } >+ >+ public synchronized void dispose() { >+ if (!disposed) { >+ disposed = true; >+ property.removeListChangeListener(source, listener); >+ property = null; >+ source = null; >+ } >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/PropertyUpdateHelper.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/PropertyUpdateHelper.java >diff -N src/org/eclipse/core/internal/databinding/property/PropertyUpdateHelper.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/PropertyUpdateHelper.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,65 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property; >+ >+import java.util.HashMap; >+import java.util.Map; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class PropertyUpdateHelper { >+ private ThreadLocal localMap = new ThreadLocal() { >+ protected Object initialValue() { >+ return new HashMap(); >+ } >+ }; >+ >+ /** >+ * @param source >+ * @return blah >+ */ >+ public boolean isUpdating(Object source) { >+ Map map = (Map) localMap.get(); >+ return map.containsKey(source); >+ } >+ >+ /** >+ * @param source >+ * @param state >+ */ >+ public void setUpdating(Object source, boolean state) { >+ Map map = (Map) localMap.get(); >+ Integer count = (Integer) map.get(source); >+ int newCount = (count == null ? 0 : count.intValue()) >+ + (state ? 1 : -1); >+ if (newCount > 0) >+ map.put(source, new Integer(newCount)); >+ else >+ map.remove(source); >+ } >+ >+ /** >+ * >+ */ >+ public void dispose() { >+ if (localMap != null) { >+ Map map = (Map) localMap.get(); >+ if (map != null) { >+ map.clear(); >+ localMap.set(null); >+ } >+ localMap = null; >+ } >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/ListValuePropertyObservableList.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/ListValuePropertyObservableList.java >diff -N src/org/eclipse/core/internal/databinding/property/ListValuePropertyObservableList.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/ListValuePropertyObservableList.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,379 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property; >+ >+import java.lang.reflect.Array; >+import java.util.ArrayList; >+import java.util.Collection; >+import java.util.Iterator; >+import java.util.List; >+import java.util.ListIterator; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.IObserving; >+import org.eclipse.core.databinding.observable.ObservableTracker; >+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.set.IObservableSet; >+import org.eclipse.core.databinding.observable.set.ISetChangeListener; >+import org.eclipse.core.databinding.observable.set.SetChangeEvent; >+import org.eclipse.core.databinding.observable.set.WritableSet; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+import org.eclipse.core.internal.databinding.Util; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class ListValuePropertyObservableList extends AbstractObservableList >+ implements IObserving { >+ private IObservableList masterList; >+ private IValueProperty detailProperty; >+ >+ private IObservableSet knownMasterElements; >+ >+ private boolean disposed = false; >+ >+ private IListChangeListener masterListener = new IListChangeListener() { >+ public void handleListChange(ListChangeEvent event) { >+ ListDiffEntry[] masterEntries = event.diff.getDifferences(); >+ ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length]; >+ for (int i = 0; i < masterEntries.length; i++) { >+ ListDiffEntry masterDifference = masterEntries[i]; >+ int index = masterDifference.getPosition(); >+ boolean addition = masterDifference.isAddition(); >+ Object masterElement = masterDifference.getElement(); >+ Object elementDetailValue = detailProperty >+ .getValue(masterElement); >+ detailEntries[i] = Diffs.createListDiffEntry(index, addition, >+ elementDetailValue); >+ } >+ >+ // Removes listeners from elements removed from list >+ knownMasterElements.retainAll(masterList); >+ >+ // Adds listeners to elements added to list >+ knownMasterElements.addAll(masterList); >+ >+ fireListChange(Diffs.createListDiff(detailEntries)); >+ } >+ }; >+ >+ private IValuePropertyChangeListener detailListener = new IValuePropertyChangeListener() { >+ public void handleValuePropertyChange( >+ final ValuePropertyChangeEvent event) { >+ if (!disposed) { >+ Object source = event.getSource(); >+ int[] indices = findIndices(source); >+ Object oldValue = event.diff.getOldValue(); >+ Object newValue = event.diff.getNewValue(); >+ ListDiffEntry[] entries = new ListDiffEntry[indices.length * 2]; >+ for (int i = 0; i < indices.length; i++) { >+ int index = indices[i]; >+ entries[i * 2] = Diffs.createListDiffEntry(index, false, >+ oldValue); >+ entries[i * 2 + 1] = Diffs.createListDiffEntry(index, true, >+ newValue); >+ } >+ >+ ListDiff diff = Diffs.createListDiff(entries); >+ fireListChange(diff); >+ } >+ } >+ >+ private int[] findIndices(Object masterElement) { >+ List indices = new ArrayList(); >+ >+ for (ListIterator it = masterList.listIterator(); it.hasNext();) { >+ if (Util.equals(masterElement, it.next())) >+ indices.add(new Integer(it.previousIndex())); >+ } >+ >+ int[] result = new int[indices.size()]; >+ for (int i = 0; i < result.length; i++) { >+ result[i] = ((Integer) indices.get(i)).intValue(); >+ } >+ return result; >+ } >+ }; >+ >+ /** >+ * @param masterList >+ * @param valueProperty >+ */ >+ public ListValuePropertyObservableList(IObservableList masterList, >+ IValueProperty valueProperty) { >+ super(masterList.getRealm()); >+ this.masterList = masterList; >+ this.detailProperty = valueProperty; >+ } >+ >+ protected void firstListenerAdded() { >+ knownMasterElements = new WritableSet(getRealm()); >+ knownMasterElements.addSetChangeListener(new ISetChangeListener() { >+ public void handleSetChange(SetChangeEvent event) { >+ for (Iterator it = event.diff.getRemovals().iterator(); it >+ .hasNext();) { >+ detailProperty.addValueChangeListener(it.next(), >+ detailListener); >+ } >+ for (Iterator it = event.diff.getAdditions().iterator(); it >+ .hasNext();) { >+ detailProperty.removeValueChangeListener(it.next(), >+ detailListener); >+ } >+ } >+ }); >+ knownMasterElements.addAll(masterList); >+ masterList.addListChangeListener(masterListener); >+ } >+ >+ protected void lastListenerRemoved() { >+ masterList.removeListChangeListener(masterListener); >+ if (knownMasterElements != null) { >+ knownMasterElements.clear(); >+ knownMasterElements.dispose(); >+ knownMasterElements = null; >+ } >+ } >+ >+ protected int doGetSize() { >+ getterCalled(); >+ return masterList.size(); >+ } >+ >+ private void getterCalled() { >+ ObservableTracker.getterCalled(this); >+ } >+ >+ public Object getElementType() { >+ return detailProperty.getValueType(); >+ } >+ >+ public Object get(int index) { >+ getterCalled(); >+ Object masterElement = masterList.get(index); >+ return detailProperty.getValue(masterElement); >+ } >+ >+ public boolean add(Object o) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean addAll(Collection c) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean addAll(int index, Collection c) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean contains(Object o) { >+ getterCalled(); >+ >+ Iterator it; >+ if (knownMasterElements != null) >+ it = knownMasterElements.iterator(); >+ else >+ it = masterList.iterator(); >+ for (; it.hasNext();) { >+ if (Util.equals(detailProperty.getValue(it.next()), o)) >+ return true; >+ } >+ return false; >+ } >+ >+ public boolean isEmpty() { >+ getterCalled(); >+ return masterList.isEmpty(); >+ } >+ >+ public boolean isStale() { >+ getterCalled(); >+ return masterList.isStale(); >+ } >+ >+ public Iterator iterator() { >+ getterCalled(); >+ return new Iterator() { >+ Iterator it = masterList.iterator(); >+ >+ public boolean hasNext() { >+ getterCalled(); >+ return it.hasNext(); >+ } >+ >+ public Object next() { >+ getterCalled(); >+ Object masterElement = it.next(); >+ return detailProperty.getValue(masterElement); >+ } >+ >+ public void remove() { >+ throw new UnsupportedOperationException(); >+ } >+ }; >+ } >+ >+ 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[] toArray() { >+ getterCalled(); >+ Object[] masterElements = masterList.toArray(); >+ Object[] result = new Object[masterElements.length]; >+ for (int i = 0; i < result.length; i++) { >+ result[i] = detailProperty.getValue(masterElements[i]); >+ } >+ return result; >+ } >+ >+ public Object[] toArray(Object[] a) { >+ getterCalled(); >+ Object[] masterElements = masterList.toArray(); >+ if (a.length < masterElements.length) >+ a = (Object[]) Array.newInstance(a.getClass().getComponentType(), >+ masterElements.length); >+ for (int i = 0; i < masterElements.length; i++) { >+ a[i] = detailProperty.getValue(masterElements[i]); >+ } >+ return a; >+ } >+ >+ public void add(int index, Object o) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public void clear() { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public ListIterator listIterator() { >+ return listIterator(0); >+ } >+ >+ public ListIterator listIterator(final int index) { >+ getterCalled(); >+ return new ListIterator() { >+ ListIterator it = masterList.listIterator(index); >+ Object lastReturned; >+ boolean haveIterated = false; >+ >+ public void add(Object arg0) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public boolean hasNext() { >+ getterCalled(); >+ return it.hasNext(); >+ } >+ >+ public boolean hasPrevious() { >+ getterCalled(); >+ return it.hasPrevious(); >+ } >+ >+ public Object next() { >+ getterCalled(); >+ Object result = lastReturned = detailProperty.getValue(it >+ .next()); >+ haveIterated = true; >+ return result; >+ } >+ >+ public int nextIndex() { >+ getterCalled(); >+ return it.nextIndex(); >+ } >+ >+ public Object previous() { >+ getterCalled(); >+ Object result = lastReturned = detailProperty.getValue(it >+ .previous()); >+ haveIterated = true; >+ return result; >+ } >+ >+ public int previousIndex() { >+ getterCalled(); >+ return it.previousIndex(); >+ } >+ >+ public void remove() { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public void set(Object o) { >+ checkRealm(); >+ if (!haveIterated) >+ throw new IllegalStateException(); >+ // if listeners are registered, the value property listener will >+ // cause the list change event to fire. otherwise it doesn't >+ // matter because noone is listening >+ detailProperty.setValue(lastReturned, o); >+ } >+ }; >+ } >+ >+ public Object remove(int index) { >+ throw new UnsupportedOperationException(); >+ } >+ >+ public Object set(int index, Object o) { >+ checkRealm(); >+ Object masterElement = masterList.get(index); >+ Object oldValue = detailProperty.getValue(masterElement); >+ detailProperty.setValue(masterElement, o); >+ return oldValue; >+ } >+ >+ public Object getObserved() { >+ return masterList; >+ } >+ >+ public synchronized void dispose() { >+ if (masterList != null) { >+ masterList.removeListChangeListener(masterListener); >+ masterList = null; >+ masterListener = null; >+ } >+ if (knownMasterElements != null) { >+ knownMasterElements.clear(); >+ knownMasterElements.dispose(); >+ knownMasterElements = null; >+ detailListener = null; >+ } >+ detailProperty = null; >+ disposed = true; >+ >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/PropertyObservableSet.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/PropertyObservableSet.java >diff -N src/org/eclipse/core/internal/databinding/property/PropertyObservableSet.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/PropertyObservableSet.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,197 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property; >+ >+import java.util.Collection; >+import java.util.ConcurrentModificationException; >+import java.util.HashSet; >+import java.util.Iterator; >+import java.util.Set; >+ >+import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.set.AbstractObservableSet; >+import org.eclipse.core.databinding.property.IProperty; >+import org.eclipse.core.databinding.property.IPropertyObservable; >+import org.eclipse.core.databinding.property.ISetProperty; >+import org.eclipse.core.databinding.property.ISetPropertyChangeListener; >+import org.eclipse.core.databinding.property.SetPropertyChangeEvent; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class PropertyObservableSet extends AbstractObservableSet implements >+ IPropertyObservable { >+ private Object source; >+ private ISetProperty property; >+ >+ private boolean updating = false; >+ >+ private boolean disposed = false; >+ >+ private transient volatile int modCount = 0; >+ >+ private ISetPropertyChangeListener listener = new ISetPropertyChangeListener() { >+ public void handleSetPropertyChange(final SetPropertyChangeEvent event) { >+ if (!disposed && !updating) { >+ getRealm().exec(new Runnable() { >+ public void run() { >+ getRealm().exec(new Runnable() { >+ public void run() { >+ modCount++; >+ fireSetChange(event.diff); >+ } >+ }); >+ } >+ }); >+ } >+ } >+ }; >+ >+ /** >+ * @param realm >+ * @param source >+ * @param property >+ */ >+ public PropertyObservableSet(Realm realm, Object source, >+ ISetProperty property) { >+ super(realm); >+ this.source = source; >+ this.property = property; >+ } >+ >+ protected void firstListenerAdded() { >+ if (!disposed) { >+ property.addSetChangeListener(source, listener); >+ } >+ } >+ >+ protected void lastListenerRemoved() { >+ if (!disposed) { >+ property.removeSetChangeListener(source, listener); >+ } >+ } >+ >+ protected Set getWrappedSet() { >+ return property.getSet(source); >+ } >+ >+ public Object getElementType() { >+ return property.getElementType(); >+ } >+ >+ // Queries >+ >+ protected int doGetSize() { >+ return property.size(source); >+ } >+ >+ // Single change operations >+ >+ public boolean add(Object o) { >+ checkRealm(); >+ return property.add(source, o); >+ } >+ >+ public Iterator iterator() { >+ getterCalled(); >+ return new Iterator() { >+ int expectedModCount = modCount; >+ Iterator delegate = new HashSet(property.getSet(source)).iterator(); >+ Object last = null; >+ >+ public boolean hasNext() { >+ getterCalled(); >+ checkForComodification(); >+ return delegate.hasNext(); >+ } >+ >+ public Object next() { >+ getterCalled(); >+ checkForComodification(); >+ Object next = delegate.next(); >+ last = next; >+ return next; >+ } >+ >+ public void remove() { >+ checkRealm(); >+ checkForComodification(); >+ >+ delegate.remove(); // stay in sync >+ >+ property.remove(source, last); >+ >+ last = null; >+ expectedModCount = modCount; >+ } >+ >+ private void checkForComodification() { >+ if (expectedModCount != modCount) >+ throw new ConcurrentModificationException(); >+ } >+ }; >+ } >+ >+ public boolean remove(Object o) { >+ getterCalled(); >+ return property.remove(source, o); >+ } >+ >+ // Bulk change operations >+ >+ public boolean addAll(Collection c) { >+ getterCalled(); >+ return property.addAll(source, c); >+ } >+ >+ public boolean removeAll(Collection c) { >+ getterCalled(); >+ return property.removeAll(source, c); >+ } >+ >+ public boolean retainAll(Collection c) { >+ getterCalled(); >+ return property.retainAll(source, c); >+ } >+ >+ public void clear() { >+ getterCalled(); >+ property.clear(source); >+ } >+ >+ public boolean equals(Object o) { >+ return property.equals(source, o); >+ } >+ >+ public int hashCode() { >+ return property.hashCode(source); >+ } >+ >+ public Object getObserved() { >+ return source; >+ } >+ >+ public IProperty getProperty() { >+ return property; >+ } >+ >+ public synchronized void dispose() { >+ if (!disposed) { >+ disposed = true; >+ property.removeSetChangeListener(source, listener); >+ property = null; >+ source = null; >+ } >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/databinding/property/IPropertyChangeListener.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/IPropertyChangeListener.java >diff -N src/org/eclipse/core/databinding/property/IPropertyChangeListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/IPropertyChangeListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,23 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+/** >+ * Marker interface for all listener types in the properties framework. >+ * >+ * @noimplement This interface is not intended to be implemented by clients. >+ * >+ * @since 1.2 >+ */ >+public interface IPropertyChangeListener { >+ >+} >Index: src/org/eclipse/core/databinding/property/CollectionProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/CollectionProperty.java >diff -N src/org/eclipse/core/databinding/property/CollectionProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/CollectionProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,56 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import java.util.Collection; >+ >+/** >+ * Abstract implementation of ICollectionProperty. >+ * >+ * @since 1.2 >+ */ >+public abstract class CollectionProperty extends Property implements >+ ICollectionProperty { >+ abstract Collection getCollection(Object source); >+ >+ public boolean contains(Object source, Object o) { >+ return getCollection(source).contains(o); >+ } >+ >+ public boolean containsAll(Object source, Collection c) { >+ return getCollection(source).containsAll(c); >+ } >+ >+ public boolean equals(Object source, Object o) { >+ return getCollection(source).equals(o); >+ } >+ >+ public int hashCode(Object source) { >+ return getCollection(source).hashCode(); >+ } >+ >+ public boolean isEmpty(Object source) { >+ return getCollection(source).isEmpty(); >+ } >+ >+ public int size(Object source) { >+ return getCollection(source).size(); >+ } >+ >+ public Object[] toArray(Object source, Object[] array) { >+ return getCollection(source).toArray(array); >+ } >+ >+ public Object[] toArray(Object source) { >+ return getCollection(source).toArray(); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/SetValuePropertyObservableMap.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/SetValuePropertyObservableMap.java >diff -N src/org/eclipse/core/internal/databinding/property/SetValuePropertyObservableMap.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/SetValuePropertyObservableMap.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,92 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.IObserving; >+import org.eclipse.core.databinding.observable.map.ComputedObservableMap; >+import org.eclipse.core.databinding.observable.set.IObservableSet; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+ >+/** >+ * @since 3.3 >+ * >+ */ >+public class SetValuePropertyObservableMap extends ComputedObservableMap >+ implements IObserving { >+ private IValueProperty property; >+ >+ private boolean disposed = false; >+ >+ private IValuePropertyChangeListener listener = new IValuePropertyChangeListener() { >+ public void handleValuePropertyChange( >+ final ValuePropertyChangeEvent event) { >+ if (!disposed) { >+ getRealm().exec(new Runnable() { >+ public void run() { >+ Object key = event.getSource(); >+ Object oldValue = event.diff.getOldValue(); >+ Object newValue = event.diff.getNewValue(); >+ fireMapChange(Diffs.createMapDiffSingleChange(key, >+ oldValue, newValue)); >+ } >+ }); >+ } >+ } >+ }; >+ >+ /** >+ * @param keySet >+ * @param valueProperty >+ */ >+ public SetValuePropertyObservableMap(IObservableSet keySet, >+ IValueProperty valueProperty) { >+ super(keySet); >+ this.property = valueProperty; >+ } >+ >+ protected Object doGet(Object key) { >+ return property.getValue(key); >+ } >+ >+ protected Object doPut(Object key, Object value) { >+ Object result = property.getValue(key); >+ property.setValue(key, value); >+ return result; >+ } >+ >+ protected void hookListener(Object addedKey) { >+ if (!disposed) >+ property.addValueChangeListener(addedKey, listener); >+ } >+ >+ protected void unhookListener(Object removedKey) { >+ if (!disposed) >+ property.removeValueChangeListener(removedKey, listener); >+ } >+ >+ public Object getObserved() { >+ return keySet(); >+ } >+ >+ public synchronized void dispose() { >+ super.dispose(); >+ >+ if (!disposed) { >+ disposed = true; >+ property = null; >+ listener = null; >+ } >+ } >+} >Index: src/org/eclipse/core/databinding/property/BasicValueProperty.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/BasicValueProperty.java >diff -N src/org/eclipse/core/databinding/property/BasicValueProperty.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/BasicValueProperty.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,93 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 IBM Corporation 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: >+ * IBM Corporation - initial API and implementation >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.value.ValueDiff; >+import org.eclipse.core.internal.databinding.Util; >+import org.eclipse.core.internal.databinding.property.PropertyUpdateHelper; >+ >+/** >+ * Abstract IValueProperty implementation. >+ * <p> >+ * This class automatically fires value change events when changes are initiated >+ * through a call to {@link #setValue(Object, Object)}. >+ * >+ * @since 1.2 >+ */ >+public abstract class BasicValueProperty extends ValueProperty { >+ private PropertyUpdateHelper updateHelper = new PropertyUpdateHelper(); >+ >+ /** >+ * Returns whether this property is currently being updated on the source. >+ * Implementors should query this value to avoid unnecessary calculations, >+ * such as computing a diff. >+ * >+ * @param source >+ * the property source >+ * @return whether this property is currently being updated on the source. >+ */ >+ protected boolean isUpdating(Object source) { >+ return updateHelper.isUpdating(source); >+ } >+ >+ /** >+ * Sets the value property on the source to the specified value, then fires >+ * a value change. >+ * >+ * @param source >+ * the property source >+ * @param value >+ * the new value >+ */ >+ public void setValue(Object source, Object value) { >+ Object oldValue = getValue(source); >+ >+ updateHelper.setUpdating(source, true); >+ try { >+ doSetValue(source, value); >+ } finally { >+ updateHelper.setUpdating(source, false); >+ } >+ >+ if (hasListeners(source)) { >+ Object newValue = getValue(source); >+ if (!Util.equals(oldValue, newValue)) { >+ fireValueChange(source, Diffs.createValueDiff(oldValue, >+ newValue)); >+ } >+ } >+ } >+ >+ protected void fireValueChange(Object source, ValueDiff diff) { >+ if (!isUpdating(source)) >+ super.fireValueChange(source, diff); >+ } >+ >+ /** >+ * Sets the value property on the source to the specified list. >+ * >+ * @param source >+ * the property source >+ * @param value >+ * the new value >+ */ >+ protected abstract void doSetValue(Object source, Object value); >+ >+ public synchronized void dispose() { >+ if (updateHelper != null) { >+ updateHelper.dispose(); >+ updateHelper = null; >+ } >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/internal/databinding/property/PropertyObservableValue.java >=================================================================== >RCS file: src/org/eclipse/core/internal/databinding/property/PropertyObservableValue.java >diff -N src/org/eclipse/core/internal/databinding/property/PropertyObservableValue.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/internal/databinding/property/PropertyObservableValue.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,115 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.internal.databinding.property; >+ >+import org.eclipse.core.databinding.observable.Diffs; >+import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.value.AbstractObservableValue; >+import org.eclipse.core.databinding.property.IProperty; >+import org.eclipse.core.databinding.property.IPropertyObservable; >+import org.eclipse.core.databinding.property.IValueProperty; >+import org.eclipse.core.databinding.property.IValuePropertyChangeListener; >+import org.eclipse.core.databinding.property.ValuePropertyChangeEvent; >+import org.eclipse.core.internal.databinding.Util; >+ >+/** >+ * @since 1.2 >+ * >+ */ >+public class PropertyObservableValue extends AbstractObservableValue implements >+ IPropertyObservable { >+ private Object source; >+ private IValueProperty property; >+ >+ private boolean updating = false; >+ >+ private boolean disposed = false; >+ >+ private IValuePropertyChangeListener listener = new IValuePropertyChangeListener() { >+ public void handleValuePropertyChange( >+ final ValuePropertyChangeEvent event) { >+ if (!disposed && !updating) { >+ getRealm().exec(new Runnable() { >+ public void run() { >+ fireValueChange(event.diff); >+ } >+ }); >+ } >+ } >+ }; >+ >+ /** >+ * @param realm >+ * @param source >+ * @param property >+ */ >+ public PropertyObservableValue(Realm realm, Object source, >+ IValueProperty property) { >+ super(realm); >+ this.source = source; >+ this.property = property; >+ } >+ >+ protected void firstListenerAdded() { >+ if (!disposed) { >+ property.addValueChangeListener(source, listener); >+ } >+ } >+ >+ protected void lastListenerRemoved() { >+ if (!disposed) { >+ property.removeValueChangeListener(source, listener); >+ } >+ } >+ >+ protected Object doGetValue() { >+ return property.getValue(source); >+ } >+ >+ protected void doSetValue(Object value) { >+ Object oldValue = doGetValue(); >+ >+ updating = true; >+ try { >+ property.setValue(source, value); >+ } finally { >+ updating = false; >+ } >+ >+ Object newValue = doGetValue(); >+ if (!Util.equals(oldValue, newValue)) { >+ fireValueChange(Diffs.createValueDiff(oldValue, newValue)); >+ } >+ } >+ >+ public Object getValueType() { >+ return property.getValueType(); >+ } >+ >+ public Object getObserved() { >+ return source; >+ } >+ >+ public IProperty getProperty() { >+ return property; >+ } >+ >+ public synchronized void dispose() { >+ if (!disposed) { >+ disposed = true; >+ property.removeValueChangeListener(source, listener); >+ property = null; >+ source = null; >+ } >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/core/databinding/property/IListPropertyChangeListener.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/IListPropertyChangeListener.java >diff -N src/org/eclipse/core/databinding/property/IListPropertyChangeListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/IListPropertyChangeListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,27 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+/** >+ * Listener for changes to list properties on a property source >+ * >+ * @since 1.2 >+ */ >+public interface IListPropertyChangeListener extends IPropertyChangeListener { >+ /** >+ * Handle a change to a list property on a specific property source. >+ * >+ * @param event >+ * an event describing the list change that occured. >+ */ >+ public void handleListPropertyChange(ListPropertyChangeEvent event); >+} >Index: src/org/eclipse/core/databinding/property/Property.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/property/Property.java >diff -N src/org/eclipse/core/databinding/property/Property.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/property/Property.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,78 @@ >+/******************************************************************************* >+ * Copyright (c) 2008 Matthew Hall 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: >+ * Matthew Hall - initial API and implementation (bug 194734) >+ ******************************************************************************/ >+ >+package org.eclipse.core.databinding.property; >+ >+/** >+ * Abstract implementation of IProperty >+ * >+ * @since 1.2 >+ */ >+public abstract class Property implements IProperty { >+ private PropertyChangeSupport changeSupport; >+ >+ synchronized PropertyChangeSupport getChangeSupport() { >+ if (changeSupport == null) >+ changeSupport = new PropertyChangeSupport(this); >+ return changeSupport; >+ } >+ >+ /** >+ * Adds a listener on the given property source for changes to the property. >+ * The listener should fire a property change event whenever the a property >+ * change is observed. This method is called when the first property change >+ * listener is added for the given source object. >+ * <p> >+ * This method does nothing if the source object has no listener support for >+ * the property. >+ * >+ * @param source >+ * the source object to observe for property changes. >+ * @see #removeListenerFrom(Object) >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ protected abstract void addListenerTo(Object source); >+ >+ /** >+ * Remove the listeners previous added on the property source for changes to >+ * the property. This method is called when the last property change >+ * listener is removed for the given source object. >+ * <p> >+ * This method does nothing if the source object has no listener support for >+ * the property. >+ * >+ * @param source >+ * the source object to stop observing for property changes. >+ * @see #addListenerTo(Object) >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ protected abstract void removeListenerFrom(Object source); >+ >+ /** >+ * Returns whether this property has listeners registered for the given >+ * property source >+ * >+ * @param source >+ * the property source >+ * @return whether this property has listeners registered for the given >+ * property source >+ */ >+ protected boolean hasListeners(Object source) { >+ return changeSupport != null && changeSupport.hasListeners(source); >+ } >+ >+ public synchronized void dispose() { >+ if (changeSupport != null) { >+ changeSupport.dispose(); >+ changeSupport = null; >+ } >+ } >+}
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 194734
:
106529
|
107000
|
107001
|
107041
|
107042
|
111164
|
111165
|
111335
|
111336
|
111448
|
111449
|
111640
|
111641
|
111717
|
111719
|
111845
|
111846
|
111897
|
111898
|
111993
|
111994
|
112161
|
112162
|
112268
|
112269
|
112360
|
112361
|
112370
|
112371
|
112471
|
112472
|
112606
|
112607
|
112629
|
112630
|
113149
|
113150
| 113165 |
113166
|
114858
|
114859
|
115196
|
115197
|
115284
|
115458
|
115459
|
117500
|
117501
|
117990
|
117991
|
119231
|
119232
|
119274
|
120541
|
120542
|
120651
|
120652
|
120653
|
120654
|
120914
|
120915
|
120989
|
120990
|
121020
|
121021
|
121141
|
121142
|
122228
|
122229
|
122234
|
122235
|
122288
|
122289
|
122609
|
122610
|
122613
|
122614
|
122775
|
122776
|
122813
|
122814
|
122852
|
122853
|
122864
|
122865
|
123137
|
123138