Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 194734 | Differences between
and this patch

Collapse All | Expand All

(-)META-INF/MANIFEST.MF (+4 lines)
Lines 14-19 Link Here
14
 org.eclipse.core.databinding.observable.masterdetail,
14
 org.eclipse.core.databinding.observable.masterdetail,
15
 org.eclipse.core.databinding.observable.set;x-internal:=false,
15
 org.eclipse.core.databinding.observable.set;x-internal:=false,
16
 org.eclipse.core.databinding.observable.value;x-internal:=false,
16
 org.eclipse.core.databinding.observable.value;x-internal:=false,
17
 org.eclipse.core.databinding.property,
18
 org.eclipse.core.databinding.property.masterdetail,
17
 org.eclipse.core.databinding.util,
19
 org.eclipse.core.databinding.util,
18
 org.eclipse.core.databinding.validation;x-internal:=false,
20
 org.eclipse.core.databinding.validation;x-internal:=false,
19
 org.eclipse.core.internal.databinding;x-friends:="org.eclipse.core.databinding.beans",
21
 org.eclipse.core.internal.databinding;x-friends:="org.eclipse.core.databinding.beans",
Lines 21-26 Link Here
21
 org.eclipse.core.internal.databinding.observable;x-internal:=true,
23
 org.eclipse.core.internal.databinding.observable;x-internal:=true,
22
 org.eclipse.core.internal.databinding.observable.masterdetail;x-friends:="org.eclipse.jface.tests.databinding",
24
 org.eclipse.core.internal.databinding.observable.masterdetail;x-friends:="org.eclipse.jface.tests.databinding",
23
 org.eclipse.core.internal.databinding.observable.tree;x-friends:="org.eclipse.jface.databinding,org.eclipse.jface.tests.databinding",
25
 org.eclipse.core.internal.databinding.observable.tree;x-friends:="org.eclipse.jface.databinding,org.eclipse.jface.tests.databinding",
26
 org.eclipse.core.internal.databinding.property;x-friends:="org.eclipse.jface.databinding,org.eclipse.jface.tests.databinding",
27
 org.eclipse.core.internal.databinding.property.masterdetail;x-friends:="org.eclipse.jface.databinding,org.eclipse.jface.tests.databinding",
24
 org.eclipse.core.internal.databinding.validation;x-friends:="org.eclipse.jface.tests.databinding"
28
 org.eclipse.core.internal.databinding.validation;x-friends:="org.eclipse.jface.tests.databinding"
25
Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)"
29
Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)"
26
Import-Package-Comment: see http://wiki.eclipse.org/
30
Import-Package-Comment: see http://wiki.eclipse.org/
(-)src/org/eclipse/core/databinding/property/IListProperty.java (+163 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.Collection;
15
import java.util.List;
16
17
/**
18
 * Interface for list-typed properties.
19
 * 
20
 * @since 1.2
21
 * @noimplement This interface is not intended to be implemented by clients.
22
 */
23
public interface IListProperty extends ICollectionProperty {
24
	/**
25
	 * Returns a List with the current contents of the source's list property
26
	 * 
27
	 * @param source
28
	 *            the property source
29
	 * @return a List with the current contents of the source's list property
30
	 */
31
	List getList(Object source);
32
33
	/**
34
	 * Inserts all elements in the specified collection into the source's list
35
	 * property at the specified index.
36
	 * 
37
	 * @param source
38
	 *            the property source
39
	 * @param index
40
	 *            the insertion index
41
	 * @param c
42
	 *            the collection of elements to add
43
	 * @return whether the source's list property was changed
44
	 */
45
	boolean addAll(Object source, int index, Collection c);
46
47
	/**
48
	 * Returns the element at the specified position in the source's list
49
	 * property
50
	 * 
51
	 * @param source
52
	 *            the property source
53
	 * @param index
54
	 *            the element position
55
	 * @return the element at the given position in the source's list property
56
	 */
57
	Object get(Object source, int index);
58
59
	/**
60
	 * Replaces the element at the specified position in the source's list
61
	 * property with the given element.
62
	 * 
63
	 * @param source
64
	 *            the property source
65
	 * @param index
66
	 *            the element position
67
	 * @param element
68
	 *            the replacement element
69
	 * @return the element previously at the specified position in the source's
70
	 *         list property
71
	 */
72
	Object set(Object source, int index, Object element);
73
74
	/**
75
	 * Moves the element at the specified old position in the source's list
76
	 * property to the specified new position
77
	 * 
78
	 * @param source
79
	 *            the property source
80
	 * @param oldIndex
81
	 *            the old element position
82
	 * @param newIndex
83
	 *            the new element position
84
	 * @return the element that was moved
85
	 */
86
	Object move(Object source, int oldIndex, int newIndex);
87
88
	/**
89
	 * Inserts the element into the source's list property at the specified
90
	 * position
91
	 * 
92
	 * @param source
93
	 *            the property source
94
	 * @param index
95
	 *            the insertion index
96
	 * @param element
97
	 *            the element to insert
98
	 */
99
	void add(Object source, int index, Object element);
100
101
	/**
102
	 * Removes the element from the source's list property which is located at
103
	 * the specified position
104
	 * 
105
	 * @param source
106
	 *            the property source
107
	 * @param index
108
	 *            the index of the element to remove
109
	 * @return the element that was removed from the source's list property
110
	 */
111
	Object remove(Object source, int index);
112
113
	/**
114
	 * Returns the index of the first location of the given element in the
115
	 * source's list property, or -1 if the list does not contain the element.
116
	 * 
117
	 * @param source
118
	 *            the property source
119
	 * @param o
120
	 *            the element
121
	 * @return the index of the first location of the given element in the
122
	 *         source's list property, or -1 if the list does not contain the
123
	 *         element
124
	 */
125
	int indexOf(Object source, Object o);
126
127
	/**
128
	 * Returns the index of the last location of the given element in the
129
	 * source's list property, or -1 if the list does not contain the given
130
	 * element.
131
	 * 
132
	 * @param source
133
	 * @param o
134
	 * @return the index of the last location of the given element in the
135
	 *         source's list property, or -1 if the list does not contain the
136
	 *         element
137
	 */
138
	int lastIndexOf(Object source, Object o);
139
140
	/**
141
	 * Adds the given list property change listener to the list of listeners for
142
	 * the given source.
143
	 * 
144
	 * @param source
145
	 *            the property source
146
	 * @param listener
147
	 *            the listener
148
	 */
149
	public void addListChangeListener(Object source,
150
			IListPropertyChangeListener listener);
151
152
	/**
153
	 * Removes the given list property change listener from the list of
154
	 * listeners for the given source.
155
	 * 
156
	 * @param source
157
	 *            the property source
158
	 * @param listener
159
	 *            the listener
160
	 */
161
	public void removeListChangeListener(Object source,
162
			IListPropertyChangeListener listener);
163
}
(-)src/org/eclipse/core/databinding/property/BasicMapProperty.java (+146 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.Collections;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.Iterator;
18
import java.util.Map;
19
import java.util.Set;
20
21
import org.eclipse.core.databinding.observable.Diffs;
22
import org.eclipse.core.databinding.observable.map.MapDiff;
23
import org.eclipse.core.internal.databinding.property.PropertyUpdateHelper;
24
25
/**
26
 * Abstract map property implementation for properties where the map can be
27
 * completely replaced in a single atomic operation.
28
 * <p>
29
 * For example, a map-typed bean property Customer.phoneNumbers can be modified
30
 * by calling Customer.setPhoneNumbers(Map phoneNumbers).
31
 * 
32
 * @since 1.2
33
 */
34
public abstract class BasicMapProperty extends MapProperty {
35
	private PropertyUpdateHelper updateHelper = new PropertyUpdateHelper();
36
37
	/**
38
	 * Returns whether this property is currently being updated on the source.
39
	 * Implementors should query this value to avoid unnecessary calculations,
40
	 * such as computing a diff.
41
	 * 
42
	 * @param source
43
	 *            the property source
44
	 * @return whether this property is currently being updated on the source.
45
	 */
46
	protected boolean isUpdating(Object source) {
47
		return updateHelper.isUpdating(source);
48
	}
49
50
	/**
51
	 * Sets the map property on the source to the specified map, then fires a
52
	 * map change using the specified diff.
53
	 * 
54
	 * @param source
55
	 *            the property source
56
	 * @param map
57
	 *            the new map
58
	 * @param diff
59
	 *            the diff to be fired after the map property has been set. If
60
	 *            null, the diff will be computed.
61
	 */
62
	protected void setMap(Object source, Map map, MapDiff diff) {
63
		if (diff == null) {
64
			diff = Diffs.computeMapDiff(getMap(source), map);
65
		}
66
67
		updateHelper.setUpdating(source, true);
68
		try {
69
			doSetMap(source, map);
70
		} finally {
71
			updateHelper.setUpdating(source, false);
72
		}
73
74
		if (hasListeners(source)) {
75
			fireMapChange(source, diff);
76
		}
77
	}
78
79
	public void clear(Object source) {
80
		setMap(source, new HashMap(), Diffs.createMapDiffRemoveAll(new HashMap(
81
				getMap(source))));
82
	}
83
84
	public Object put(Object source, Object key, Object value) {
85
		Map map = new HashMap(getMap(source));
86
		boolean addition = !map.containsKey(key);
87
		Object result = map.put(key, value);
88
		MapDiff diff;
89
		if (addition) {
90
			diff = Diffs.createMapDiffSingleAdd(key, value);
91
		} else {
92
			diff = Diffs.createMapDiffSingleChange(key, result, value);
93
		}
94
		setMap(source, map, diff);
95
		return result;
96
	}
97
98
	public void putAll(Object source, Map t) {
99
		Map map = new HashMap(getMap(source));
100
		Set addedKeys = new HashSet();
101
		Set changedKeys = new HashSet();
102
		Map oldValues = new HashMap();
103
		Map newValues = new HashMap();
104
		for (Iterator it = t.entrySet().iterator(); it.hasNext();) {
105
			Map.Entry entry = (Map.Entry) it.next();
106
			Object key = entry.getKey();
107
			Object newValue = entry.getValue();
108
			boolean addition = !map.containsKey(key);
109
			Object oldValue = map.put(key, newValue);
110
			if (addition) {
111
				addedKeys.add(key);
112
			} else {
113
				changedKeys.add(key);
114
				oldValues.put(key, oldValue);
115
			}
116
			newValues.put(key, newValue);
117
		}
118
		setMap(source, map, Diffs.createMapDiff(addedKeys,
119
				Collections.EMPTY_SET, changedKeys, oldValues, newValues));
120
	}
121
122
	public Object remove(Object source, Object key) {
123
		Map map = getMap(source);
124
		if (map.containsKey(key)) {
125
			map = new HashMap(map);
126
			Object result = map.remove(key);
127
			setMap(source, map, Diffs.createMapDiffSingleRemove(key, result));
128
			return result;
129
		}
130
		return null;
131
	}
132
133
	public int size(Object source) {
134
		return getMap(source).size();
135
	}
136
137
	protected abstract void doSetMap(Object source, Map map);
138
139
	public synchronized void dispose() {
140
		if (updateHelper != null) {
141
			updateHelper.dispose();
142
			updateHelper = null;
143
		}
144
		super.dispose();
145
	}
146
}
(-)src/org/eclipse/core/databinding/property/ISetProperty.java (+55 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.Set;
15
16
/**
17
 * Interface for set-typed properties
18
 * 
19
 * @since 1.2
20
 * @noimplement This interface is not intended to be implemented by clients.
21
 */
22
public interface ISetProperty extends ICollectionProperty {
23
	/**
24
	 * Returns a Set with the current contents of the source's set property
25
	 * 
26
	 * @param source
27
	 *            the property source
28
	 * @return a Set with the current contents of the source's set property
29
	 */
30
	public Set getSet(Object source);
31
32
	/**
33
	 * Adds the given set property change listener to the list of listeners for
34
	 * the given source.
35
	 * 
36
	 * @param source
37
	 *            the property source
38
	 * @param listener
39
	 *            the listener
40
	 */
41
	public void addSetChangeListener(Object source,
42
			ISetPropertyChangeListener listener);
43
44
	/**
45
	 * Removes the given set property change listener from the list of listeners
46
	 * for the given source.
47
	 * 
48
	 * @param source
49
	 *            the property source
50
	 * @param listener
51
	 *            the listener
52
	 */
53
	public void removeSetChangeListener(Object source,
54
			ISetPropertyChangeListener listener);
55
}
(-)src/org/eclipse/core/databinding/property/ValuePropertyChangeEvent.java (+58 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import org.eclipse.core.databinding.observable.value.ValueDiff;
15
import org.eclipse.core.runtime.Assert;
16
17
/**
18
 * Value change event describing a change of a value property on a particular
19
 * property source.
20
 * 
21
 * @since 1.2
22
 */
23
public class ValuePropertyChangeEvent extends PropertyChangeEvent {
24
	private static final long serialVersionUID = 1L;
25
26
	/**
27
	 * The value property that changed
28
	 */
29
	public final IValueProperty property;
30
31
	/**
32
	 * ValueDiff with the old and new values of the property.
33
	 */
34
	public final ValueDiff diff;
35
36
	/**
37
	 * Constructs a ValuePropertyChangeEvent with the given attributes
38
	 * 
39
	 * @param source
40
	 *            the property source
41
	 * @param property
42
	 *            the property that changed on the source
43
	 * @param diff
44
	 *            a ValueDiff describing the changes to the value property
45
	 */
46
	public ValuePropertyChangeEvent(Object source, IValueProperty property,
47
			ValueDiff diff) {
48
		super(source);
49
		this.property = property;
50
		Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$
51
		this.diff = diff;
52
	}
53
54
	void dispatch(IPropertyChangeListener listener) {
55
		((IValuePropertyChangeListener) listener)
56
				.handleValuePropertyChange(this);
57
	}
58
}
(-)src/org/eclipse/core/databinding/property/MapProperty.java (+72 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import org.eclipse.core.databinding.observable.map.MapDiff;
15
16
/**
17
 * Abstract implementation of IMapProperty
18
 * 
19
 * @since 1.2
20
 */
21
public abstract class MapProperty extends Property implements IMapProperty {
22
	public boolean containsKey(Object source, Object key) {
23
		return getMap(source).containsKey(key);
24
	}
25
26
	public boolean containsValue(Object source, Object value) {
27
		return getMap(source).containsValue(value);
28
	}
29
30
	public boolean equals(Object source, Object o) {
31
		return getMap(source).equals(o);
32
	}
33
34
	public Object get(Object source, Object key) {
35
		return getMap(source).get(key);
36
	}
37
38
	public int hashCode(Object source) {
39
		return getMap(source).hashCode();
40
	}
41
42
	public boolean isEmpty(Object source) {
43
		return getMap(source).isEmpty();
44
	}
45
46
	public int size(Object source) {
47
		return getMap(source).size();
48
	}
49
50
	public final void addMapChangeListener(Object source,
51
			IMapPropertyChangeListener listener) {
52
		getChangeSupport().addListener(source, listener);
53
	}
54
55
	public final void removeMapChangeListener(Object source,
56
			IMapPropertyChangeListener listener) {
57
		getChangeSupport().removeListener(source, listener);
58
	}
59
60
	/**
61
	 * Fires a MapPropertyChangeEvent with the given source and diff
62
	 * 
63
	 * @param source
64
	 *            the property source
65
	 * @param diff
66
	 *            the map diff
67
	 */
68
	protected final void fireMapChange(Object source, MapDiff diff) {
69
		getChangeSupport().firePropertyChange(
70
				new MapPropertyChangeEvent(source, this, diff));
71
	}
72
}
(-)src/org/eclipse/core/databinding/property/ListProperty.java (+63 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.Collection;
15
16
import org.eclipse.core.databinding.observable.list.ListDiff;
17
18
/**
19
 * Abstract implementation of IListProperty.
20
 * 
21
 * @since 1.2
22
 */
23
public abstract class ListProperty extends CollectionProperty implements
24
		IListProperty {
25
	Collection getCollection(Object source) {
26
		return getList(source);
27
	}
28
29
	public Object get(Object source, int index) {
30
		return getList(source).get(index);
31
	}
32
33
	public int indexOf(Object source, Object o) {
34
		return getList(source).indexOf(o);
35
	}
36
37
	public int lastIndexOf(Object source, Object o) {
38
		return getList(source).lastIndexOf(o);
39
	}
40
41
	public final void addListChangeListener(Object source,
42
			IListPropertyChangeListener listener) {
43
		getChangeSupport().addListener(source, listener);
44
	}
45
46
	public final void removeListChangeListener(Object source,
47
			IListPropertyChangeListener listener) {
48
		getChangeSupport().removeListener(source, listener);
49
	}
50
51
	/**
52
	 * Fires a ListPropertyChangeEvent with the given source and diff
53
	 * 
54
	 * @param source
55
	 *            the property source
56
	 * @param diff
57
	 *            the list diff
58
	 */
59
	protected void fireListChange(Object source, ListDiff diff) {
60
		getChangeSupport().firePropertyChange(
61
				new ListPropertyChangeEvent(source, this, diff));
62
	}
63
}
(-)src/org/eclipse/core/internal/databinding/property/PropertyObservableMap.java (+177 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property;
13
14
import java.util.Collection;
15
import java.util.Collections;
16
import java.util.Map;
17
import java.util.Set;
18
19
import org.eclipse.core.databinding.observable.ObservableTracker;
20
import org.eclipse.core.databinding.observable.Realm;
21
import org.eclipse.core.databinding.observable.map.AbstractObservableMap;
22
import org.eclipse.core.databinding.property.IMapProperty;
23
import org.eclipse.core.databinding.property.IMapPropertyChangeListener;
24
import org.eclipse.core.databinding.property.IProperty;
25
import org.eclipse.core.databinding.property.IPropertyObservable;
26
import org.eclipse.core.databinding.property.MapPropertyChangeEvent;
27
28
/**
29
 * @since 3.3
30
 * 
31
 */
32
public class PropertyObservableMap extends AbstractObservableMap implements
33
		IPropertyObservable {
34
	private Object source;
35
	private IMapProperty property;
36
37
	private volatile boolean updating = false;
38
39
	private boolean disposed = false;
40
41
	private transient volatile int modCount = 0;
42
43
	private IMapPropertyChangeListener listener = new IMapPropertyChangeListener() {
44
		public void handleMapPropertyChange(final MapPropertyChangeEvent event) {
45
			if (!disposed && !updating) {
46
				getRealm().exec(new Runnable() {
47
					public void run() {
48
						getRealm().exec(new Runnable() {
49
							public void run() {
50
								modCount++;
51
								fireMapChange(event.diff);
52
							}
53
						});
54
					}
55
				});
56
			}
57
		}
58
	};
59
60
	/**
61
	 * @param realm
62
	 * @param source
63
	 * @param property
64
	 */
65
	public PropertyObservableMap(Realm realm, Object source,
66
			IMapProperty property) {
67
		super(realm);
68
		this.source = source;
69
		this.property = property;
70
	}
71
72
	private void getterCalled() {
73
		ObservableTracker.getterCalled(this);
74
	}
75
76
	protected void firstListenerAdded() {
77
		if (!disposed) {
78
			property.addMapChangeListener(source, listener);
79
		}
80
	}
81
82
	protected void lastListenerRemoved() {
83
		if (!disposed) {
84
			property.removeMapChangeListener(source, listener);
85
		}
86
	}
87
88
	public boolean containsKey(Object key) {
89
		getterCalled();
90
		return property.containsKey(source, key);
91
	}
92
93
	public boolean containsValue(Object value) {
94
		getterCalled();
95
		return property.containsValue(source, value);
96
	}
97
98
	public Set entrySet() {
99
		getterCalled();
100
		// unmodifiable for now
101
		return Collections.unmodifiableSet(property.getMap(source).entrySet());
102
	}
103
104
	public Object get(Object key) {
105
		getterCalled();
106
		return property.get(source, key);
107
	}
108
109
	public boolean isEmpty() {
110
		getterCalled();
111
		return property.isEmpty(source);
112
	}
113
114
	public Set keySet() {
115
		getterCalled();
116
		return Collections.unmodifiableSet(property.getMap(source).keySet());
117
	}
118
119
	public Object put(Object key, Object value) {
120
		checkRealm();
121
		return property.put(source, key, value);
122
	}
123
124
	public void putAll(Map m) {
125
		checkRealm();
126
		property.putAll(source, m);
127
	}
128
129
	public Object remove(Object key) {
130
		checkRealm();
131
		return property.remove(source, key);
132
	}
133
134
	public int size() {
135
		getterCalled();
136
		return property.size(source);
137
	}
138
139
	public Collection values() {
140
		getterCalled();
141
		return Collections.unmodifiableCollection(property.getMap(source)
142
				.values());
143
	}
144
145
	public void clear() {
146
		getterCalled();
147
		property.clear(source);
148
	}
149
150
	public boolean equals(Object o) {
151
		getterCalled();
152
		return property.equals(source, o);
153
	}
154
155
	public int hashCode() {
156
		getterCalled();
157
		return property.hashCode(source);
158
	}
159
160
	public Object getObserved() {
161
		return source;
162
	}
163
164
	public IProperty getProperty() {
165
		return property;
166
	}
167
168
	public synchronized void dispose() {
169
		if (!disposed) {
170
			disposed = true;
171
			property.removeMapChangeListener(source, listener);
172
			property = null;
173
			source = null;
174
		}
175
		super.dispose();
176
	}
177
}
(-)src/org/eclipse/core/databinding/property/SetPropertyChangeEvent.java (+57 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import org.eclipse.core.databinding.observable.set.SetDiff;
15
import org.eclipse.core.runtime.Assert;
16
17
/**
18
 * Set change event describing an incremental change of a set property on a
19
 * particular property source.
20
 * 
21
 * @since 1.2
22
 */
23
public class SetPropertyChangeEvent extends PropertyChangeEvent {
24
	private static final long serialVersionUID = 1L;
25
26
	/**
27
	 * The set property that changed
28
	 */
29
	public final ISetProperty property;
30
31
	/**
32
	 * SetDiff enumerating the added and removed elements in the set.
33
	 */
34
	public final SetDiff diff;
35
36
	/**
37
	 * Constructs a SetPropertyChangeEvent with the given attributes
38
	 * 
39
	 * @param source
40
	 *            the property source
41
	 * @param property
42
	 *            the property that changed on the source
43
	 * @param diff
44
	 *            a SetDiff describing the changes to the set property
45
	 */
46
	public SetPropertyChangeEvent(Object source, ISetProperty property,
47
			SetDiff diff) {
48
		super(source);
49
		this.property = property;
50
		Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$
51
		this.diff = diff;
52
	}
53
54
	void dispatch(IPropertyChangeListener listener) {
55
		((ISetPropertyChangeListener) listener).handleSetPropertyChange(this);
56
	}
57
}
(-)src/org/eclipse/core/databinding/property/IMapProperty.java (+177 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.Map;
15
16
/**
17
 * Interface for map-typed properties
18
 * 
19
 * @since 1.2
20
 * @noimplement This interface is not intended to be implemented by clients.
21
 */
22
public interface IMapProperty extends IProperty {
23
	/**
24
	 * Returns a Map with the current contents of the source's map property
25
	 * 
26
	 * @param source
27
	 *            the property source
28
	 * @return a Map with the current contents of the source's map property
29
	 */
30
	Map getMap(Object source);
31
32
	/**
33
	 * Returns the size of the source's map property
34
	 * 
35
	 * @param source
36
	 *            the property source
37
	 * @return the size of the source's map property
38
	 */
39
	int size(Object source);
40
41
	/**
42
	 * Returns whether the source's map property is empty
43
	 * 
44
	 * @param source
45
	 *            the property source
46
	 * @return whether the source's map property is empty
47
	 */
48
	boolean isEmpty(Object source);
49
50
	/**
51
	 * Returns whether the specified key is contained in the key set of the
52
	 * source's map property
53
	 * 
54
	 * @param source
55
	 *            the property source
56
	 * @param key
57
	 *            the key
58
	 * @return whether the specified key is contained in the key set of the
59
	 *         source's map property
60
	 */
61
	boolean containsKey(Object source, Object key);
62
63
	/**
64
	 * Returns whether the specified value is contains in the values collection
65
	 * of the source's map property
66
	 * 
67
	 * @param source
68
	 *            the property source
69
	 * @param value
70
	 *            the value
71
	 * @return whether the specified value is contains in the values collection
72
	 *         of the source's map property
73
	 */
74
	boolean containsValue(Object source, Object value);
75
76
	/**
77
	 * Returns the value associated with the specified key in the source's map
78
	 * property
79
	 * 
80
	 * @param source
81
	 *            the property source
82
	 * @param key
83
	 *            the key
84
	 * @return the value associated with the specified key in the source's map
85
	 *         property
86
	 */
87
	Object get(Object source, Object key);
88
89
	/**
90
	 * Associates the specified value with the specified key in the source's map
91
	 * property
92
	 * 
93
	 * @param source
94
	 *            the property source
95
	 * @param key
96
	 *            the key
97
	 * @param value
98
	 *            the value
99
	 * @return the value that was previously associated with the given key in
100
	 *         the source's map property
101
	 */
102
	Object put(Object source, Object key, Object value);
103
104
	/**
105
	 * Removes the mapping for the specified key from the source's map property
106
	 * 
107
	 * @param source
108
	 *            the property source
109
	 * @param key
110
	 *            the key
111
	 * @return the value that was previously associated with the specified key
112
	 *         in the source's map property, or null if no such mapping exists
113
	 */
114
	Object remove(Object source, Object key);
115
116
	/**
117
	 * Adds all mappings in the specified map to the source's map property.
118
	 * 
119
	 * @param source
120
	 *            the property source
121
	 * @param t
122
	 *            the map
123
	 */
124
	void putAll(Object source, Map t);
125
126
	/**
127
	 * Removes all mapping from the source's map property
128
	 * 
129
	 * @param source
130
	 *            the property source
131
	 */
132
	void clear(Object source);
133
134
	/**
135
	 * Returns whether the source's map property is equal to the argument
136
	 * 
137
	 * @param source
138
	 *            the property source
139
	 * @param o
140
	 *            the object to test for equality
141
	 * @return whether the source's map property is equal to the argument
142
	 */
143
	boolean equals(Object source, Object o);
144
145
	/**
146
	 * Returns the hash code of the source's map property
147
	 * 
148
	 * @param source
149
	 *            the property source
150
	 * @return the hash code of the source's map property
151
	 */
152
	int hashCode(Object source);
153
154
	/**
155
	 * Adds the given map property change listener to the list of listeners for
156
	 * the given source.
157
	 * 
158
	 * @param source
159
	 *            the property source
160
	 * @param listener
161
	 *            the listener
162
	 */
163
	public void addMapChangeListener(Object source,
164
			IMapPropertyChangeListener listener);
165
166
	/**
167
	 * Removes the given map property change listener from the list of listeners
168
	 * for the given source.
169
	 * 
170
	 * @param source
171
	 *            the property source
172
	 * @param listener
173
	 *            the listener
174
	 */
175
	public void removeMapChangeListener(Object source,
176
			IMapPropertyChangeListener listener);
177
}
(-)src/org/eclipse/core/databinding/property/IValueProperty.java (+71 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
15
/**
16
 * Interface for value-typed properties
17
 * 
18
 * @since 1.2
19
 * @noimplement This interface is not intended to be implemented by clients.
20
 */
21
public interface IValueProperty extends IProperty {
22
	/**
23
	 * Returns the source's value property
24
	 * 
25
	 * @param source
26
	 *            the property source
27
	 * @return the current value of the source's value property
28
	 */
29
	public Object getValue(Object source);
30
31
	/**
32
	 * Sets the source's value property to the specified value
33
	 * 
34
	 * @param source
35
	 *            the property source
36
	 * @param value
37
	 *            the new value
38
	 */
39
	public void setValue(Object source, Object value);
40
41
	/**
42
	 * Returns the value type of the property, or <code>null</code> if untyped.
43
	 * 
44
	 * @return the value type of the property, or <code>null</code> if untyped.
45
	 */
46
	public Object getValueType();
47
48
	/**
49
	 * Adds the given value property change listener to the list of listeners
50
	 * for the given source.
51
	 * 
52
	 * @param source
53
	 *            the property source
54
	 * @param listener
55
	 *            the listener
56
	 */
57
	public void addValueChangeListener(Object source,
58
			IValuePropertyChangeListener listener);
59
60
	/**
61
	 * Removes the given list property change listener from the list of
62
	 * listeners for the given source.
63
	 * 
64
	 * @param source
65
	 *            the property source
66
	 * @param listener
67
	 *            the listener
68
	 */
69
	public void removeValueChangeListener(Object source,
70
			IValuePropertyChangeListener listener);
71
}
(-)src/org/eclipse/core/databinding/property/IMapPropertyChangeListener.java (+27 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
/**
15
 * Listener for changes to map properties on a property source
16
 * 
17
 * @since 1.2
18
 */
19
public interface IMapPropertyChangeListener extends IPropertyChangeListener {
20
	/**
21
	 * Handle a change to a map property on a specific property source.
22
	 * 
23
	 * @param event
24
	 *            an event describing the map change that occured.
25
	 */
26
	public void handleMapPropertyChange(MapPropertyChangeEvent event);
27
}
(-)src/org/eclipse/core/databinding/property/ValueProperty.java (+44 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import org.eclipse.core.databinding.observable.value.ValueDiff;
15
16
/**
17
 * Abstract implementation of IValueProperty.
18
 * 
19
 * @since 1.2
20
 */
21
public abstract class ValueProperty extends Property implements IValueProperty {
22
	public final void addValueChangeListener(Object source,
23
			IValuePropertyChangeListener listener) {
24
		getChangeSupport().addListener(source, listener);
25
	}
26
27
	public final void removeValueChangeListener(Object source,
28
			IValuePropertyChangeListener listener) {
29
		getChangeSupport().removeListener(source, listener);
30
	}
31
32
	/**
33
	 * Fires a ValuePropertyChangeEvent with the given source and diff
34
	 * 
35
	 * @param source
36
	 *            the property source
37
	 * @param diff
38
	 *            the value diff
39
	 */
40
	protected void fireValueChange(Object source, ValueDiff diff) {
41
		getChangeSupport().firePropertyChange(
42
				new ValuePropertyChangeEvent(source, this, diff));
43
	}
44
}
(-)src/org/eclipse/core/databinding/property/IValuePropertyChangeListener.java (+27 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
/**
15
 * Listener for changes to value properties on a property source
16
 * 
17
 * @since 1.2
18
 */
19
public interface IValuePropertyChangeListener extends IPropertyChangeListener {
20
	/**
21
	 * Handle a change to a value property on a specific property source.
22
	 * 
23
	 * @param event
24
	 *            an event describing the value change that occured.
25
	 */
26
	public void handleValuePropertyChange(ValuePropertyChangeEvent event);
27
}
(-)src/org/eclipse/core/databinding/property/BasicListProperty.java (+244 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.Iterator;
17
import java.util.List;
18
import java.util.ListIterator;
19
20
import org.eclipse.core.databinding.observable.Diffs;
21
import org.eclipse.core.databinding.observable.list.ListDiff;
22
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
23
import org.eclipse.core.internal.databinding.property.PropertyUpdateHelper;
24
25
/**
26
 * Abstract IListProperty implementation for properties where the list can be
27
 * completely replaced in a single atomic operation.
28
 * <p>
29
 * For example, a list-typed bean property Customer.invoices can be modified by
30
 * calling Customer.setInvoices(List invoices).
31
 * 
32
 * @since 1.2
33
 */
34
public abstract class BasicListProperty extends ListProperty {
35
	private PropertyUpdateHelper updateHelper = new PropertyUpdateHelper();
36
37
	/**
38
	 * Returns whether this property is currently being updated on the source.
39
	 * Implementors should query this value to avoid unnecessary calculations,
40
	 * such as computing a diff.
41
	 * 
42
	 * @param source
43
	 *            the property source
44
	 * @return whether this property is currently being updated on the source.
45
	 */
46
	protected boolean isUpdating(Object source) {
47
		return updateHelper.isUpdating(source);
48
	}
49
50
	/**
51
	 * Sets the list property on the source to the specified list, then fires a
52
	 * list change using the specified diff.
53
	 * 
54
	 * @param source
55
	 *            the property source
56
	 * @param list
57
	 *            the new list
58
	 * @param diff
59
	 *            the diff to be fired after the list property has been set. If
60
	 *            null, the diff will be computed.
61
	 */
62
	protected void setList(Object source, List list, ListDiff diff) {
63
		if (diff == null) {
64
			diff = Diffs.computeListDiff(getList(source), list);
65
		}
66
67
		updateHelper.setUpdating(source, true);
68
		try {
69
			doSetList(source, list);
70
		} finally {
71
			updateHelper.setUpdating(source, false);
72
		}
73
74
		if (hasListeners(source)) {
75
			fireListChange(source, diff);
76
		}
77
	}
78
79
	protected void fireListChange(Object source, ListDiff diff) {
80
		if (!isUpdating(source))
81
			super.fireListChange(source, diff);
82
	}
83
84
	/**
85
	 * Sets the list property on the source to the specified list.
86
	 * 
87
	 * @param source
88
	 *            the property source
89
	 * @param list
90
	 *            the new list
91
	 */
92
	protected abstract void doSetList(Object source, List list);
93
94
	public boolean add(Object source, Object o) {
95
		add(source, size(source), o);
96
		return true;
97
	}
98
99
	public void add(Object source, int index, Object element) {
100
		List list = new ArrayList(getList(source));
101
		list.add(index, element);
102
		setList(source, list, Diffs.createListDiff(Diffs.createListDiffEntry(
103
				index, true, element)));
104
	}
105
106
	public boolean addAll(Object source, Collection c) {
107
		if (c.isEmpty())
108
			return false;
109
		addAll(source, size(source), c);
110
		return true;
111
	}
112
113
	public boolean addAll(Object source, int index, Collection c) {
114
		if (c.isEmpty()) {
115
			return false;
116
		}
117
118
		List list = new ArrayList(getList(source));
119
		List entries = new ArrayList();
120
		int i = index;
121
		for (Iterator it = c.iterator(); it.hasNext(); i++) {
122
			Object o = it.next();
123
			list.add(i, o);
124
			entries.add(Diffs.createListDiffEntry(i, true, o));
125
		}
126
		boolean changed = !entries.isEmpty();
127
		if (changed) {
128
			ListDiffEntry[] ea = (ListDiffEntry[]) entries
129
					.toArray(new ListDiffEntry[entries.size()]);
130
			setList(source, list, Diffs.createListDiff(ea));
131
		}
132
		return changed;
133
	}
134
135
	public void clear(Object source) {
136
		if (isEmpty(source))
137
			return;
138
		List list = getList(source);
139
		ListDiffEntry[] entries = new ListDiffEntry[list.size()];
140
		int i = 0;
141
		for (Iterator it = getList(source).iterator(); it.hasNext(); i++) {
142
			entries[i] = Diffs.createListDiffEntry(0, false, it.next());
143
		}
144
		setList(source, new ArrayList(), Diffs.createListDiff(entries));
145
	}
146
147
	public Object move(Object source, int oldIndex, int newIndex) {
148
		if (oldIndex == newIndex)
149
			return get(source, oldIndex);
150
		List list = new ArrayList(getList(source));
151
		Object result = list.remove(oldIndex);
152
		list.add(newIndex, result);
153
		setList(source, list, Diffs.createListDiff(Diffs.createListDiffEntry(
154
				oldIndex, false, result), Diffs.createListDiffEntry(newIndex,
155
				true, result)));
156
		return result;
157
	}
158
159
	public boolean remove(Object source, Object o) {
160
		int i = indexOf(source, o);
161
		if (i == -1)
162
			return false;
163
		remove(source, i);
164
		return true;
165
	}
166
167
	public Object remove(Object source, int index) {
168
		List list = new ArrayList(getList(source));
169
		Object result = list.remove(index);
170
		setList(source, list, Diffs.createListDiff(Diffs.createListDiffEntry(
171
				index, false, result)));
172
		return result;
173
	}
174
175
	public boolean removeAll(Object source, Collection c) {
176
		if (isEmpty(source)) {
177
			return false;
178
		}
179
		if (c.isEmpty()) {
180
			return false;
181
		}
182
		List list = new ArrayList(getList(source));
183
		List entries = new ArrayList();
184
		for (ListIterator it = list.listIterator(); it.hasNext();) {
185
			Object o = it.next();
186
			if (c.contains(o)) {
187
				entries.add(Diffs.createListDiffEntry(it.previousIndex(),
188
						false, o));
189
				it.remove();
190
			}
191
		}
192
		boolean changed = !entries.isEmpty();
193
		if (changed) {
194
			ListDiffEntry[] ea = (ListDiffEntry[]) entries
195
					.toArray(new ListDiffEntry[entries.size()]);
196
			setList(source, list, Diffs.createListDiff(ea));
197
		}
198
		return changed;
199
	}
200
201
	public boolean retainAll(Object source, Collection c) {
202
		if (isEmpty(source)) {
203
			return false;
204
		}
205
		if (c.isEmpty()) {
206
			clear(source);
207
			return true;
208
		}
209
		List list = new ArrayList(getList(source));
210
		List entries = new ArrayList();
211
		for (ListIterator it = list.listIterator(); it.hasNext();) {
212
			Object o = it.next();
213
			if (!c.contains(o)) {
214
				entries.add(Diffs.createListDiffEntry(it.previousIndex(),
215
						false, o));
216
				it.remove();
217
			}
218
		}
219
		boolean changed = !entries.isEmpty();
220
		if (changed) {
221
			ListDiffEntry[] ea = (ListDiffEntry[]) entries
222
					.toArray(new ListDiffEntry[entries.size()]);
223
			setList(source, list, Diffs.createListDiff(ea));
224
		}
225
		return changed;
226
	}
227
228
	public Object set(Object source, int index, Object element) {
229
		List list = new ArrayList(getList(source));
230
		Object result = list.set(index, element);
231
		setList(source, list, Diffs.createListDiff(Diffs.createListDiffEntry(
232
				index, false, result), Diffs.createListDiffEntry(index, true,
233
				element)));
234
		return result;
235
	}
236
237
	public synchronized void dispose() {
238
		if (updateHelper != null) {
239
			updateHelper.dispose();
240
			updateHelper = null;
241
		}
242
		super.dispose();
243
	}
244
}
(-)src/org/eclipse/core/databinding/property/masterdetail/MasterDetailProperties.java (+200 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property.masterdetail;
13
14
import org.eclipse.core.databinding.property.IListProperty;
15
import org.eclipse.core.databinding.property.IMapProperty;
16
import org.eclipse.core.databinding.property.ISetProperty;
17
import org.eclipse.core.databinding.property.IValueProperty;
18
import org.eclipse.core.internal.databinding.property.masterdetail.ListPropertyDetailValueList;
19
import org.eclipse.core.internal.databinding.property.masterdetail.MapPropertyDetailValueMap;
20
import org.eclipse.core.internal.databinding.property.masterdetail.SetPropertyDetailValueMap;
21
import org.eclipse.core.internal.databinding.property.masterdetail.ValuePropertyDetailList;
22
import org.eclipse.core.internal.databinding.property.masterdetail.ValuePropertyDetailMap;
23
import org.eclipse.core.internal.databinding.property.masterdetail.ValuePropertyDetailSet;
24
import org.eclipse.core.internal.databinding.property.masterdetail.ValuePropertyDetailValue;
25
26
/**
27
 * A factory for combining properties to create nested properties.
28
 * <p>
29
 * Example: Suppose class <code>A</code> has a property <code>b</code> of type
30
 * <code>B</code>, and that class <code>B</code> has a property <code>c</code>
31
 * of type <code>C</code>:
32
 * 
33
 * <pre>
34
 * A a = new A();
35
 * B b = a.getB();
36
 * IValueProperty ab = BeanProperties.valueProperty(A.class, &quot;b&quot;);
37
 * assertTrue(ab.getValue(a) == b);
38
 * 
39
 * IValueProperty bc = BeanProperties.valueProperty(B.class, &quot;c&quot;);
40
 * C c = b.getC();
41
 * assertTrue(bc.getValue(b) == c);
42
 * </pre>
43
 * 
44
 * Using MasterDetailProperties, the <code>ab</code> and <code>bc</code>
45
 * properties may be combined to form a nested <code>abc</code> property:
46
 * 
47
 * <pre>
48
 * IValueProperty abc = MasterDetailProperties.detailValue(ab, bc)
49
 * assertTrue(abc.getValue(a) == c);
50
 * </pre>
51
 * 
52
 * @since 1.2
53
 */
54
public class MasterDetailProperties {
55
	// Properties of IValueProperty master properties
56
57
	/**
58
	 * Returns the nested combination of the master value and detail value
59
	 * properties. Value modifications made through the returned property are
60
	 * delegated to the detail property, using the value of the master property
61
	 * as the source.
62
	 * 
63
	 * @param masterValue
64
	 *            the master property
65
	 * @param detailValue
66
	 *            the detail property
67
	 * @return the nested combination of the master and detail properties
68
	 */
69
	public static IValueProperty detailValue(IValueProperty masterValue,
70
			IValueProperty detailValue) {
71
		return new ValuePropertyDetailValue(masterValue, detailValue);
72
	}
73
74
	/**
75
	 * Returns the nested combination of the master value and detail list
76
	 * properties. List modifications made through the returned property are
77
	 * delegated to the detail property, using the value of the master property
78
	 * as the source.
79
	 * 
80
	 * @param masterValue
81
	 *            the master property
82
	 * @param detailList
83
	 *            the detail property
84
	 * @return the nested combination of the master value and detail list
85
	 *         properties
86
	 */
87
	public static IListProperty detailList(IValueProperty masterValue,
88
			IListProperty detailList) {
89
		return new ValuePropertyDetailList(masterValue, detailList);
90
	}
91
92
	/**
93
	 * Returns the nested combination of the master value and detail set
94
	 * properties. Set modifications made through the returned property are
95
	 * delegated to the detail property, using the value of the master property
96
	 * as the source.
97
	 * 
98
	 * @param masterValue
99
	 *            the master property
100
	 * @param detailSet
101
	 *            the detail property
102
	 * @return the nested combination of the master value and detail set
103
	 *         properties
104
	 */
105
	public static ISetProperty detailSet(IValueProperty masterValue,
106
			ISetProperty detailSet) {
107
		return new ValuePropertyDetailSet(masterValue, detailSet);
108
	}
109
110
	/**
111
	 * Returns the nested combination of the master value and detail map
112
	 * properties. Map modifications made through the returned property are
113
	 * delegated to the detail property, using the value of the master property
114
	 * as the source.
115
	 * 
116
	 * @param masterValue
117
	 *            the master property
118
	 * @param detailMap
119
	 *            the detail property
120
	 * @return the nested combination of the master value and detial map
121
	 *         properties
122
	 */
123
	public static IMapProperty detailMap(IValueProperty masterValue,
124
			IMapProperty detailMap) {
125
		return new ValuePropertyDetailMap(masterValue, detailMap);
126
	}
127
128
	// Properties of IListProperty master properties
129
130
	/**
131
	 * Returns the nested combination of the master list and detail value
132
	 * properties. Note that because this property is a projection of value
133
	 * properties over a list, the only modification supported is through the
134
	 * {@link IValueProperty#setValue(Object, Object)} method. Modifications
135
	 * made through the returned property are delegated to the detail property,
136
	 * using the corresponding list element from the master property as the
137
	 * source.
138
	 * 
139
	 * @param masterList
140
	 *            the master property
141
	 * @param detailValue
142
	 *            the detail property
143
	 * @return the nested combination of the master list and detail value
144
	 *         properties
145
	 */
146
	public static IListProperty detailValues(IListProperty masterList,
147
			IValueProperty detailValue) {
148
		return new ListPropertyDetailValueList(masterList, detailValue);
149
	}
150
151
	// Properties of ISetProperty master properties
152
153
	/**
154
	 * Returns the nested combination of the master set and detail value
155
	 * properties. Note that because this property is a projection of value
156
	 * properties over a set, the only modifications supported are through the
157
	 * {@link IMapProperty#put(Object, Object, Object)} and
158
	 * {@link IMapProperty#putAll(Object, java.util.Map)} methods. In the latter
159
	 * case, this property does not put entries for keys not already in the
160
	 * master key set. Modifications made through the returned property are
161
	 * delegated to the detail property, using the corresponding set element
162
	 * from the master property as the source.
163
	 * 
164
	 * @param masterKeySet
165
	 *            the master property
166
	 * @param detailValues
167
	 *            the detail property
168
	 * @return the nested combination of the master set and detail value
169
	 *         properties
170
	 */
171
	public static IMapProperty detailValues(ISetProperty masterKeySet,
172
			IValueProperty detailValues) {
173
		return new SetPropertyDetailValueMap(masterKeySet, detailValues);
174
	}
175
176
	// Properties of IMapProperty master properties
177
178
	/**
179
	 * Returns the nested combination of the master map and detail value
180
	 * properties. Note that because this property is a projection of value
181
	 * properties over a values collection, the only modifications supported are
182
	 * through the {@link IMapProperty#put(Object, Object, Object)} and
183
	 * {@link IMapProperty#putAll(Object, java.util.Map)} methods. In the latter
184
	 * case, this property does not entries for keys not already contained in
185
	 * the master map's key set. Modifications made through the returned
186
	 * property are delegated to the detail property, using the corresponding
187
	 * entry value from the master property as the source.
188
	 * 
189
	 * @param masterMap
190
	 *            the master property
191
	 * @param detailValues
192
	 *            the detail property
193
	 * @return the nested combination of the master map and detail value
194
	 *         properties.
195
	 */
196
	public static IMapProperty detailValues(IMapProperty masterMap,
197
			IValueProperty detailValues) {
198
		return new MapPropertyDetailValueMap(masterMap, detailValues);
199
	}
200
}
(-)src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailValue.java (+123 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property.masterdetail;
13
14
import java.util.HashMap;
15
import java.util.Map;
16
17
import org.eclipse.core.databinding.observable.Diffs;
18
import org.eclipse.core.databinding.observable.value.ValueDiff;
19
import org.eclipse.core.databinding.property.IValueProperty;
20
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
21
import org.eclipse.core.databinding.property.ValueProperty;
22
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
23
24
/**
25
 * @since 1.2
26
 * 
27
 */
28
public class ValuePropertyDetailValue extends ValueProperty implements
29
		IValueProperty {
30
	private IValueProperty masterProperty;
31
	private IValueProperty detailProperty;
32
33
	private Map sourceToDetailListener = new HashMap();
34
35
	private IValuePropertyChangeListener masterListener = new MasterPropertyListener();
36
37
	private class MasterPropertyListener implements
38
			IValuePropertyChangeListener {
39
		public void handleValuePropertyChange(ValuePropertyChangeEvent event) {
40
			Object oldSource = event.diff.getOldValue();
41
			Object newSource = event.diff.getNewValue();
42
43
			Object oldValue = detailProperty.getValue(oldSource);
44
			Object newValue = detailProperty.getValue(newSource);
45
46
			ValueDiff diff = Diffs.createValueDiff(oldValue, newValue);
47
48
			Object source = event.getSource();
49
50
			removeDetailPropertyListener(source);
51
			addDetailPropertyListener(source);
52
53
			fireValueChange(source, diff);
54
		}
55
	}
56
57
	private class DetailPropertyListener implements
58
			IValuePropertyChangeListener {
59
		private Object source;
60
		private Object masterValue;
61
62
		DetailPropertyListener(Object source, Object masterValue) {
63
			this.source = source;
64
			this.masterValue = masterValue;
65
		}
66
67
		public void handleValuePropertyChange(ValuePropertyChangeEvent event) {
68
			fireValueChange(source, event.diff);
69
		}
70
	}
71
72
	/**
73
	 * @param masterProperty
74
	 * @param detailProperty
75
	 */
76
	public ValuePropertyDetailValue(IValueProperty masterProperty,
77
			IValueProperty detailProperty) {
78
		this.masterProperty = masterProperty;
79
		this.detailProperty = detailProperty;
80
	}
81
82
	protected void addListenerTo(Object source) {
83
		masterProperty.addValueChangeListener(source, masterListener);
84
		addDetailPropertyListener(source);
85
	}
86
87
	protected void removeListenerFrom(Object source) {
88
		masterProperty.removeValueChangeListener(source, masterListener);
89
		removeDetailPropertyListener(source);
90
	}
91
92
	private void addDetailPropertyListener(Object source) {
93
		Object masterValue = masterProperty.getValue(source);
94
		DetailPropertyListener detailListener = new DetailPropertyListener(
95
				source, masterValue);
96
		detailProperty.addValueChangeListener(masterValue, detailListener);
97
		sourceToDetailListener.put(source, detailListener);
98
	}
99
100
	private void removeDetailPropertyListener(Object source) {
101
		DetailPropertyListener detailListener = (DetailPropertyListener) sourceToDetailListener
102
				.remove(source);
103
		if (detailListener != null) {
104
			detailProperty.removeValueChangeListener(
105
					detailListener.masterValue, detailListener);
106
		}
107
		sourceToDetailListener.remove(source);
108
	}
109
110
	public Object getValue(Object source) {
111
		Object masterValue = masterProperty.getValue(source);
112
		return detailProperty.getValue(masterValue);
113
	}
114
115
	public Object getValueType() {
116
		return detailProperty.getValueType();
117
	}
118
119
	public void setValue(Object source, Object value) {
120
		Object masterValue = masterProperty.getValue(source);
121
		detailProperty.setValue(masterValue, value);
122
	}
123
}
(-)src/org/eclipse/core/internal/databinding/property/MapValuePropertyObservableMap.java (+323 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property;
13
14
import java.util.AbstractSet;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.IdentityHashMap;
18
import java.util.Iterator;
19
import java.util.Map;
20
import java.util.Set;
21
22
import org.eclipse.core.databinding.observable.Diffs;
23
import org.eclipse.core.databinding.observable.IObserving;
24
import org.eclipse.core.databinding.observable.IStaleListener;
25
import org.eclipse.core.databinding.observable.ObservableTracker;
26
import org.eclipse.core.databinding.observable.StaleEvent;
27
import org.eclipse.core.databinding.observable.map.AbstractObservableMap;
28
import org.eclipse.core.databinding.observable.map.IMapChangeListener;
29
import org.eclipse.core.databinding.observable.map.IObservableMap;
30
import org.eclipse.core.databinding.observable.map.MapChangeEvent;
31
import org.eclipse.core.databinding.property.IValueProperty;
32
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
33
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
34
import org.eclipse.core.internal.databinding.Util;
35
36
/**
37
 * @since 3.3
38
 * 
39
 */
40
public class MapValuePropertyObservableMap extends AbstractObservableMap
41
		implements IObserving {
42
	private IObservableMap map;
43
	private IValueProperty property;
44
45
	private Map keyToMasterValueListener;
46
47
	private boolean updating = false;
48
	private boolean disposed = false;
49
50
	private IMapChangeListener mapListener = new IMapChangeListener() {
51
		public void handleMapChange(final MapChangeEvent event) {
52
			if (!updating && !disposed) {
53
				getRealm().exec(new Runnable() {
54
					public void run() {
55
						Map oldValues = new HashMap();
56
						Map newValues = new HashMap();
57
58
						Set addedKeys = event.diff.getAddedKeys();
59
						for (Iterator it = addedKeys.iterator(); it.hasNext();) {
60
							Object key = it.next();
61
							Object newSource = event.diff.getNewValue(key);
62
							Object newValue = property.getValue(newSource);
63
							newValues.put(key, newValue);
64
							addPropertySourceListener(key, newSource);
65
						}
66
67
						Set removedKeys = event.diff.getRemovedKeys();
68
						for (Iterator it = removedKeys.iterator(); it.hasNext();) {
69
							Object key = it.next();
70
							Object oldSource = event.diff.getOldValue(key);
71
							Object oldValue = property.getValue(oldSource);
72
							oldValues.put(key, oldValue);
73
							removePropertySourceListener(key, oldSource);
74
						}
75
76
						Set changedKeys = new HashSet(event.diff
77
								.getChangedKeys());
78
						for (Iterator it = changedKeys.iterator(); it.hasNext();) {
79
							Object key = it.next();
80
81
							Object oldSource = event.diff.getOldValue(key);
82
							Object newSource = event.diff.getNewValue(key);
83
84
							Object oldValue = property.getValue(oldSource);
85
							Object newValue = property.getValue(newSource);
86
87
							if (Util.equals(oldValue, newValue)) {
88
								it.remove();
89
							} else {
90
								oldValues.put(key, oldValue);
91
								newValues.put(key, newValue);
92
							}
93
94
							removePropertySourceListener(key, oldSource);
95
							addPropertySourceListener(key, newSource);
96
						}
97
98
						fireMapChange(Diffs.createMapDiff(addedKeys,
99
								removedKeys, changedKeys, oldValues, newValues));
100
					}
101
				});
102
			}
103
		}
104
	};
105
106
	private IStaleListener staleListener = new IStaleListener() {
107
		public void handleStale(StaleEvent staleEvent) {
108
			fireStale();
109
		}
110
	};
111
112
	/**
113
	 * @param map
114
	 * @param valueProperty
115
	 */
116
	public MapValuePropertyObservableMap(IObservableMap map,
117
			IValueProperty valueProperty) {
118
		super(map.getRealm());
119
		this.map = map;
120
		this.property = valueProperty;
121
122
		this.keyToMasterValueListener = new IdentityHashMap();
123
	}
124
125
	private class ValuePropertySourceChangeListener implements
126
			IValuePropertyChangeListener {
127
		private final Object key;
128
129
		public ValuePropertySourceChangeListener(Object key) {
130
			this.key = key;
131
		}
132
133
		public void handleValuePropertyChange(ValuePropertyChangeEvent event) {
134
			Object oldSource = event.diff.getOldValue();
135
			Object oldValue = property.getValue(oldSource);
136
			property.removeValueChangeListener(oldSource, this);
137
138
			Object newSource = event.diff.getNewValue();
139
			Object newValue = property.getValue(newSource);
140
			property.addValueChangeListener(newSource, this);
141
142
			if (!Util.equals(oldValue, newValue))
143
				fireMapChange(Diffs.createMapDiffSingleChange(key, oldValue,
144
						newValue));
145
		}
146
	}
147
148
	protected void firstListenerAdded() {
149
		if (!disposed) {
150
			map.addMapChangeListener(mapListener);
151
			map.addStaleListener(staleListener);
152
			for (Iterator iterator = map.entrySet().iterator(); iterator
153
					.hasNext();) {
154
				Map.Entry entry = (Map.Entry) iterator.next();
155
				Object key = entry.getKey();
156
				Object masterValue = entry.getValue();
157
158
				addPropertySourceListener(key, masterValue);
159
			}
160
		}
161
	}
162
163
	protected void lastListenerRemoved() {
164
		if (!disposed) {
165
			map.removeMapChangeListener(mapListener);
166
			map.removeStaleListener(staleListener);
167
			for (Iterator iterator = map.entrySet().iterator(); iterator
168
					.hasNext();) {
169
				Map.Entry entry = (Map.Entry) iterator.next();
170
				Object key = entry.getKey();
171
				Object propertySource = entry.getValue();
172
173
				removePropertySourceListener(key, propertySource);
174
			}
175
		}
176
	}
177
178
	private void addPropertySourceListener(Object key, Object propertySource) {
179
		IValuePropertyChangeListener propertyListener = new ValuePropertySourceChangeListener(
180
				key);
181
		property.addValueChangeListener(propertySource, propertyListener);
182
		keyToMasterValueListener.put(key, propertyListener);
183
	}
184
185
	private void removePropertySourceListener(Object key, Object propertySource) {
186
		IValuePropertyChangeListener propertyListener = (IValuePropertyChangeListener) keyToMasterValueListener
187
				.remove(key);
188
		if (propertyListener != null) {
189
			property
190
					.removeValueChangeListener(propertySource, propertyListener);
191
		}
192
	}
193
194
	protected Object doGet(Object key) {
195
		if (!map.containsKey(key))
196
			return null;
197
		return property.getValue(map.get(key));
198
	}
199
200
	protected Object doPut(Object key, Object value) {
201
		if (!map.containsKey(key))
202
			return null;
203
		Object source = map.get(key);
204
205
		Object oldValue = property.getValue(source);
206
207
		updating = true;
208
		try {
209
			property.setValue(source, value);
210
		} finally {
211
			updating = false;
212
		}
213
214
		Object newValue = property.getValue(source);
215
216
		if (!Util.equals(oldValue, newValue)) {
217
			fireMapChange(Diffs.createMapDiffSingleChange(key, oldValue,
218
					newValue));
219
		}
220
221
		return oldValue;
222
	}
223
224
	private Set entrySet;
225
226
	public Set entrySet() {
227
		getterCalled();
228
		if (entrySet == null)
229
			entrySet = new EntrySet();
230
		return entrySet;
231
	}
232
233
	class EntrySet extends AbstractSet {
234
		public Iterator iterator() {
235
			return new Iterator() {
236
				Iterator it = map.entrySet().iterator();
237
238
				public boolean hasNext() {
239
					getterCalled();
240
					return it.hasNext();
241
				}
242
243
				public Object next() {
244
					getterCalled();
245
					Map.Entry next = (Map.Entry) it.next();
246
					return new MapEntry(next.getKey());
247
				}
248
249
				public void remove() {
250
					it.remove();
251
				}
252
			};
253
		}
254
255
		public int size() {
256
			return map.size();
257
		}
258
	}
259
260
	class MapEntry implements Map.Entry {
261
		private Object key;
262
263
		MapEntry(Object key) {
264
			this.key = key;
265
		}
266
267
		public Object getKey() {
268
			getterCalled();
269
			return key;
270
		}
271
272
		public Object getValue() {
273
			getterCalled();
274
			return get(key);
275
		}
276
277
		public Object setValue(Object value) {
278
			return put(key, value);
279
		}
280
281
		public boolean equals(Object o) {
282
			getterCalled();
283
			if (o == this)
284
				return true;
285
			if (o == null)
286
				return false;
287
			if (!(o instanceof Map.Entry))
288
				return false;
289
			Map.Entry that = (Map.Entry) o;
290
			return Util.equals(this.getKey(), that.getKey())
291
					&& Util.equals(this.getValue(), that.getValue());
292
		}
293
294
		public int hashCode() {
295
			getterCalled();
296
			Object value = getValue();
297
			return (key == null ? 0 : key.hashCode())
298
					^ (value == null ? 0 : value.hashCode());
299
		}
300
	}
301
302
	public boolean isStale() {
303
		getterCalled();
304
		return map.isStale();
305
	}
306
307
	private void getterCalled() {
308
		ObservableTracker.getterCalled(this);
309
	}
310
311
	public Object getObserved() {
312
		return map;
313
	}
314
315
	public synchronized void dispose() {
316
		if (!disposed) {
317
			disposed = true;
318
			property = null;
319
		}
320
321
		super.dispose();
322
	}
323
}
(-)src/org/eclipse/core/internal/databinding/property/masterdetail/SetPropertyDetailValueMap.java (+217 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property.masterdetail;
13
14
import java.util.Collections;
15
import java.util.HashMap;
16
import java.util.Iterator;
17
import java.util.Map;
18
import java.util.Set;
19
20
import org.eclipse.core.databinding.observable.Diffs;
21
import org.eclipse.core.databinding.observable.map.MapDiff;
22
import org.eclipse.core.databinding.property.ISetProperty;
23
import org.eclipse.core.databinding.property.ISetPropertyChangeListener;
24
import org.eclipse.core.databinding.property.IValueProperty;
25
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
26
import org.eclipse.core.databinding.property.MapProperty;
27
import org.eclipse.core.databinding.property.SetPropertyChangeEvent;
28
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
29
30
/**
31
 * @since 3.3
32
 * 
33
 */
34
public class SetPropertyDetailValueMap extends MapProperty {
35
	private final ISetProperty masterProperty;
36
	private final IValueProperty detailProperty;
37
38
	private Map sourceToMasterElementToDetailListener = new HashMap();
39
40
	private ISetPropertyChangeListener masterListener = new MasterPropertyListener();
41
42
	private class MasterPropertyListener implements ISetPropertyChangeListener {
43
		public void handleSetPropertyChange(final SetPropertyChangeEvent event) {
44
			Object source = event.getSource();
45
46
			MapDiff diff;
47
			if (event.diff == null) {
48
				diff = null;
49
			} else {
50
				final Set removals = event.diff.getRemovals();
51
				final Set additions = event.diff.getAdditions();
52
53
				diff = new MapDiff() {
54
					public Set getAddedKeys() {
55
						return additions;
56
					}
57
58
					public Set getChangedKeys() {
59
						return Collections.EMPTY_SET;
60
					}
61
62
					public Object getNewValue(Object key) {
63
						return getValue(key);
64
					}
65
66
					public Object getOldValue(Object key) {
67
						return getValue(key);
68
					}
69
70
					private Object getValue(Object key) {
71
						return detailProperty.getValue(key);
72
					}
73
74
					public Set getRemovedKeys() {
75
						return removals;
76
					}
77
				};
78
79
				for (Iterator it = removals.iterator(); it.hasNext();) {
80
					removeElementPropertyListener(source, it.next());
81
				}
82
				for (Iterator it = additions.iterator(); it.hasNext();) {
83
					addElementPropertyListener(source, it.next());
84
				}
85
			}
86
87
			fireMapChange(source, diff);
88
		}
89
90
		private void addElementPropertyListener(Object source, Object element) {
91
			Map elementToDetailListener = (Map) sourceToMasterElementToDetailListener
92
					.get(source);
93
			if (elementToDetailListener == null) {
94
				sourceToMasterElementToDetailListener.put(source,
95
						elementToDetailListener = new HashMap());
96
			}
97
			if (!elementToDetailListener.containsKey(element)) {
98
				DetailPropertyListener detailListener = new DetailPropertyListener(
99
						source, element);
100
				detailProperty.addValueChangeListener(element, detailListener);
101
				elementToDetailListener.put(element, detailListener);
102
			}
103
		}
104
105
		private void removeElementPropertyListener(Object source, Object element) {
106
			Map elementToDetailListener = (Map) sourceToMasterElementToDetailListener
107
					.get(source);
108
			if (elementToDetailListener != null
109
					&& elementToDetailListener.containsKey(element)) {
110
				DetailPropertyListener detailListener = (DetailPropertyListener) elementToDetailListener
111
						.remove(element);
112
				detailProperty.removeValueChangeListener(element,
113
						detailListener);
114
			}
115
		}
116
	}
117
118
	private class DetailPropertyListener implements
119
			IValuePropertyChangeListener {
120
		private Object source;
121
		private Object masterValue;
122
123
		DetailPropertyListener(Object source, Object masterValue) {
124
			this.source = source;
125
			this.masterValue = masterValue;
126
		}
127
128
		public void handleValuePropertyChange(ValuePropertyChangeEvent event) {
129
			MapDiff diff;
130
			if (event.diff == null) {
131
				diff = null;
132
			} else {
133
				diff = Diffs.createMapDiffSingleChange(masterValue, event.diff
134
						.getOldValue(), event.diff.getNewValue());
135
			}
136
			fireMapChange(source, diff);
137
		}
138
	}
139
140
	/**
141
	 * @param masterProperty
142
	 * @param detailProperty
143
	 */
144
	public SetPropertyDetailValueMap(ISetProperty masterProperty,
145
			IValueProperty detailProperty) {
146
		this.masterProperty = masterProperty;
147
		this.detailProperty = detailProperty;
148
	}
149
150
	protected void addListenerTo(Object source) {
151
		masterProperty.addSetChangeListener(source, masterListener);
152
153
		Map masterElementToDetailListener = new HashMap();
154
		for (Iterator it = masterProperty.getSet(source).iterator(); it
155
				.hasNext();) {
156
			Object masterElement = it.next();
157
			DetailPropertyListener detailListener = new DetailPropertyListener(
158
					source, masterElement);
159
			detailProperty
160
					.addValueChangeListener(masterElement, detailListener);
161
			masterElementToDetailListener.put(masterElement, detailListener);
162
		}
163
164
		sourceToMasterElementToDetailListener.put(source,
165
				masterElementToDetailListener);
166
	}
167
168
	protected void removeListenerFrom(Object source) {
169
		masterProperty.removeSetChangeListener(source, masterListener);
170
		Map masterElementToDetailListener = (Map) sourceToMasterElementToDetailListener
171
				.remove(source);
172
		if (masterElementToDetailListener != null) {
173
			for (Iterator it = masterElementToDetailListener.entrySet()
174
					.iterator(); it.hasNext();) {
175
				Map.Entry entry = (Map.Entry) it.next();
176
				detailProperty.removeValueChangeListener(entry.getKey(),
177
						(DetailPropertyListener) entry.getValue());
178
			}
179
		}
180
	}
181
182
	public Map getMap(Object source) {
183
		Map result = new HashMap();
184
		for (Iterator it = masterProperty.getSet(source).iterator(); it
185
				.hasNext();) {
186
			Object element = it.next();
187
			result.put(element, detailProperty.getValue(element));
188
		}
189
		return result;
190
	}
191
192
	public void clear(Object source) {
193
		throw new UnsupportedOperationException();
194
	}
195
196
	public Object put(Object source, Object key, Object value) {
197
		if (!masterProperty.contains(source, key))
198
			return null;
199
		Object result = detailProperty.getValue(key);
200
		detailProperty.setValue(key, value);
201
		return result;
202
	}
203
204
	public void putAll(Object source, Map t) {
205
		Set masterSet = masterProperty.getSet(source);
206
		for (Iterator it = t.entrySet().iterator(); it.hasNext();) {
207
			Map.Entry entry = (Map.Entry) it.next();
208
			if (masterSet.contains(entry.getKey())) {
209
				detailProperty.setValue(entry.getKey(), entry.getValue());
210
			}
211
		}
212
	}
213
214
	public Object remove(Object source, Object key) {
215
		throw new UnsupportedOperationException();
216
	}
217
}
(-)src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailList.java (+173 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property.masterdetail;
13
14
import java.util.Collection;
15
import java.util.HashMap;
16
import java.util.List;
17
import java.util.Map;
18
19
import org.eclipse.core.databinding.observable.Diffs;
20
import org.eclipse.core.databinding.property.IListProperty;
21
import org.eclipse.core.databinding.property.IListPropertyChangeListener;
22
import org.eclipse.core.databinding.property.IValueProperty;
23
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
24
import org.eclipse.core.databinding.property.ListProperty;
25
import org.eclipse.core.databinding.property.ListPropertyChangeEvent;
26
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
27
28
/**
29
 * @since 3.3
30
 * 
31
 */
32
public class ValuePropertyDetailList extends ListProperty {
33
	private final IValueProperty masterProperty;
34
	private final IListProperty detailProperty;
35
36
	private Map sourceToDetailListener = new HashMap();
37
38
	private IValuePropertyChangeListener masterListener = new MasterPropertyListener();
39
40
	private class MasterPropertyListener implements
41
			IValuePropertyChangeListener {
42
		public void handleValuePropertyChange(ValuePropertyChangeEvent event) {
43
			Object oldSource = event.diff.getOldValue();
44
			Object newSource = event.diff.getNewValue();
45
46
			List oldList = detailProperty.getList(oldSource);
47
			List newList = detailProperty.getList(newSource);
48
49
			Object source = event.getSource();
50
51
			removeDetailPropertyListener(source);
52
			addDetailPropertyListener(source);
53
54
			fireListChange(source, Diffs.computeListDiff(oldList, newList));
55
		}
56
	}
57
58
	private class DetailPropertyListener implements IListPropertyChangeListener {
59
		private Object source;
60
		private Object masterValue;
61
62
		DetailPropertyListener(Object source, Object masterValue) {
63
			this.source = source;
64
			this.masterValue = masterValue;
65
		}
66
67
		public void handleListPropertyChange(ListPropertyChangeEvent event) {
68
			fireListChange(source, event.diff);
69
		}
70
	}
71
72
	/**
73
	 * @param masterProperty
74
	 * @param detailProperty
75
	 */
76
	public ValuePropertyDetailList(IValueProperty masterProperty,
77
			IListProperty detailProperty) {
78
		this.masterProperty = masterProperty;
79
		this.detailProperty = detailProperty;
80
	}
81
82
	protected void addListenerTo(Object source) {
83
		masterProperty.addValueChangeListener(source, masterListener);
84
		addDetailPropertyListener(source);
85
	}
86
87
	protected void removeListenerFrom(Object source) {
88
		masterProperty.removeValueChangeListener(source, masterListener);
89
		removeDetailPropertyListener(source);
90
	}
91
92
	private void addDetailPropertyListener(Object source) {
93
		Object masterValue = masterProperty.getValue(source);
94
		DetailPropertyListener detailListener = new DetailPropertyListener(
95
				source, masterValue);
96
		detailProperty.addListChangeListener(masterValue, detailListener);
97
		sourceToDetailListener.put(source, detailListener);
98
	}
99
100
	private void removeDetailPropertyListener(Object source) {
101
		DetailPropertyListener detailListener = (DetailPropertyListener) sourceToDetailListener
102
				.remove(source);
103
		if (detailListener != null) {
104
			detailProperty.removeListChangeListener(detailListener.masterValue,
105
					detailListener);
106
		}
107
		sourceToDetailListener.remove(source);
108
	}
109
110
	public List getList(Object source) {
111
		Object masterValue = masterProperty.getValue(source);
112
		return detailProperty.getList(masterValue);
113
	}
114
115
	public boolean add(Object source, Object o) {
116
		Object masterValue = masterProperty.getValue(source);
117
		return detailProperty.add(masterValue, o);
118
	}
119
120
	public boolean addAll(Object source, Collection c) {
121
		Object masterValue = masterProperty.getValue(source);
122
		return detailProperty.addAll(masterValue, c);
123
	}
124
125
	public void clear(Object source) {
126
		Object masterValue = masterProperty.getValue(source);
127
		detailProperty.clear(masterValue);
128
	}
129
130
	public Object getElementType() {
131
		return detailProperty.getElementType();
132
	}
133
134
	public boolean remove(Object source, Object o) {
135
		Object masterValue = masterProperty.getValue(source);
136
		return detailProperty.remove(masterValue, o);
137
	}
138
139
	public boolean removeAll(Object source, Collection c) {
140
		Object masterValue = masterProperty.getValue(source);
141
		return detailProperty.removeAll(masterValue, c);
142
	}
143
144
	public boolean retainAll(Object source, Collection c) {
145
		Object masterValue = masterProperty.getValue(source);
146
		return detailProperty.retainAll(masterValue, c);
147
	}
148
149
	public void add(Object source, int index, Object element) {
150
		Object masterValue = masterProperty.getValue(source);
151
		detailProperty.add(masterValue, index, element);
152
	}
153
154
	public boolean addAll(Object source, int index, Collection c) {
155
		Object masterValue = masterProperty.getValue(source);
156
		return detailProperty.addAll(masterValue, index, c);
157
	}
158
159
	public Object move(Object source, int oldIndex, int newIndex) {
160
		Object masterValue = masterProperty.getValue(source);
161
		return detailProperty.move(masterValue, oldIndex, newIndex);
162
	}
163
164
	public Object remove(Object source, int index) {
165
		Object masterValue = masterProperty.getValue(source);
166
		return detailProperty.remove(masterValue, index);
167
	}
168
169
	public Object set(Object source, int index, Object element) {
170
		Object masterValue = masterProperty.getValue(source);
171
		return detailProperty.set(masterValue, index, element);
172
	}
173
}
(-)src/org/eclipse/core/databinding/property/SetProperty.java (+51 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.Collection;
15
16
import org.eclipse.core.databinding.observable.set.SetDiff;
17
18
/**
19
 * Abstract implementation of ISetProperty
20
 * 
21
 * @since 1.2
22
 */
23
public abstract class SetProperty extends CollectionProperty implements
24
		ISetProperty {
25
	Collection getCollection(Object source) {
26
		return getSet(source);
27
	}
28
29
	public final void addSetChangeListener(Object source,
30
			ISetPropertyChangeListener listener) {
31
		getChangeSupport().addListener(source, listener);
32
	}
33
34
	public final void removeSetChangeListener(Object source,
35
			ISetPropertyChangeListener listener) {
36
		getChangeSupport().removeListener(source, listener);
37
	}
38
39
	/**
40
	 * Fires a SetPropertyChangeEvent with the given source and diff
41
	 * 
42
	 * @param source
43
	 *            the property source
44
	 * @param diff
45
	 *            the set diff
46
	 */
47
	protected void fireSetChange(Object source, SetDiff diff) {
48
		getChangeSupport().firePropertyChange(
49
				new SetPropertyChangeEvent(source, this, diff));
50
	}
51
}
(-)src/org/eclipse/core/databinding/property/IProperty.java (+25 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
/**
15
 * Interface for observing a property of a source object.
16
 * 
17
 * @since 1.2
18
 * @noimplement This interface is not intended to be implemented by clients.
19
 */
20
public interface IProperty {
21
	/**
22
	 * Disposes the property, removing all property listeners on source objects.
23
	 */
24
	public void dispose();
25
}
(-)src/org/eclipse/core/internal/databinding/property/masterdetail/ListPropertyDetailValueList.java (+259 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property.masterdetail;
13
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.Iterator;
19
import java.util.List;
20
import java.util.ListIterator;
21
import java.util.Map;
22
import java.util.Set;
23
24
import org.eclipse.core.databinding.observable.Diffs;
25
import org.eclipse.core.databinding.observable.list.ListDiff;
26
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
27
import org.eclipse.core.databinding.property.IListProperty;
28
import org.eclipse.core.databinding.property.IListPropertyChangeListener;
29
import org.eclipse.core.databinding.property.IValueProperty;
30
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
31
import org.eclipse.core.databinding.property.ListProperty;
32
import org.eclipse.core.databinding.property.ListPropertyChangeEvent;
33
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
34
import org.eclipse.core.internal.databinding.Util;
35
36
/**
37
 * @since 3.3
38
 * 
39
 */
40
public class ListPropertyDetailValueList extends ListProperty {
41
	private final IListProperty masterProperty;
42
	private final IValueProperty detailProperty;
43
44
	private Map sourceToMasterElementToDetailListener = new HashMap();
45
46
	private IListPropertyChangeListener masterListener = new MasterPropertyListener();
47
48
	private class MasterPropertyListener implements IListPropertyChangeListener {
49
		public void handleListPropertyChange(ListPropertyChangeEvent event) {
50
			Object source = event.getSource();
51
			ListDiffEntry[] masterEntries = event.diff.getDifferences();
52
			ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length];
53
			Set masterElementsAdded = new HashSet();
54
			Set masterElementsRemoved = new HashSet();
55
			for (int i = 0; i < masterEntries.length; i++) {
56
				ListDiffEntry masterDifference = masterEntries[i];
57
				int index = masterDifference.getPosition();
58
				boolean addition = masterDifference.isAddition();
59
				Object masterElement = masterDifference.getElement();
60
				Object elementDetailValue = detailProperty
61
						.getValue(masterElement);
62
				detailEntries[i] = Diffs.createListDiffEntry(index, addition,
63
						elementDetailValue);
64
				if (addition)
65
					masterElementsAdded.add(masterElement);
66
				else
67
					masterElementsRemoved.add(masterElement);
68
			}
69
70
			ListDiff diff = Diffs.createListDiff(detailEntries);
71
72
			for (Iterator it = masterElementsRemoved.iterator(); it.hasNext();) {
73
				Object element = it.next();
74
				if (!masterProperty.contains(source, element))
75
					removeElementPropertyListener(source, element);
76
			}
77
			for (Iterator it = masterElementsAdded.iterator(); it.hasNext();) {
78
				Object element = it.next();
79
				if (masterProperty.contains(source, element))
80
					addElementPropertyListener(source, element);
81
			}
82
83
			fireListChange(source, diff);
84
		}
85
86
		private void addElementPropertyListener(Object source, Object element) {
87
			Map elementToDetailListener = (Map) sourceToMasterElementToDetailListener
88
					.get(source);
89
			if (elementToDetailListener == null) {
90
				sourceToMasterElementToDetailListener.put(source,
91
						elementToDetailListener = new HashMap());
92
			}
93
			if (!elementToDetailListener.containsKey(element)) {
94
				DetailPropertyListener detailListener = new DetailPropertyListener(
95
						source, element);
96
				detailProperty.addValueChangeListener(element, detailListener);
97
				elementToDetailListener.put(element, detailListener);
98
			}
99
		}
100
101
		private void removeElementPropertyListener(Object source, Object element) {
102
			Map elementToDetailListener = (Map) sourceToMasterElementToDetailListener
103
					.get(source);
104
			if (elementToDetailListener != null
105
					&& elementToDetailListener.containsKey(element)) {
106
				DetailPropertyListener detailListener = (DetailPropertyListener) elementToDetailListener
107
						.remove(element);
108
				detailProperty.removeValueChangeListener(element,
109
						detailListener);
110
			}
111
		}
112
	}
113
114
	private class DetailPropertyListener implements
115
			IValuePropertyChangeListener {
116
		private Object source;
117
		private Object masterElement;
118
119
		DetailPropertyListener(Object source, Object masterElement) {
120
			this.source = source;
121
			this.masterElement = masterElement;
122
		}
123
124
		private int[] findIndices() {
125
			List indices = new ArrayList();
126
127
			List list = masterProperty.getList(source);
128
			for (ListIterator it = list.listIterator(); it.hasNext();) {
129
				if (Util.equals(masterElement, it.next()))
130
					indices.add(new Integer(it.previousIndex()));
131
			}
132
133
			int[] result = new int[indices.size()];
134
			for (int i = 0; i < result.length; i++) {
135
				result[i] = ((Integer) indices.get(i)).intValue();
136
			}
137
			return result;
138
		}
139
140
		public void handleValuePropertyChange(ValuePropertyChangeEvent event) {
141
			int[] indices = findIndices();
142
			Object oldValue = event.diff.getOldValue();
143
			Object newValue = event.diff.getNewValue();
144
			ListDiffEntry[] entries = new ListDiffEntry[indices.length * 2];
145
			for (int i = 0; i < indices.length; i++) {
146
				int index = indices[i];
147
				entries[i * 2] = Diffs.createListDiffEntry(index, false,
148
						oldValue);
149
				entries[i * 2 + 1] = Diffs.createListDiffEntry(index, true,
150
						newValue);
151
			}
152
153
			ListDiff diff = Diffs.createListDiff(entries);
154
			fireListChange(source, diff);
155
		}
156
	}
157
158
	/**
159
	 * @param masterProperty
160
	 * @param detailProperty
161
	 */
162
	public ListPropertyDetailValueList(IListProperty masterProperty,
163
			IValueProperty detailProperty) {
164
		this.masterProperty = masterProperty;
165
		this.detailProperty = detailProperty;
166
	}
167
168
	protected void addListenerTo(Object source) {
169
		masterProperty.addListChangeListener(source, masterListener);
170
171
		Map masterElementToDetailListener = new HashMap();
172
		for (Iterator it = masterProperty.getList(source).iterator(); it
173
				.hasNext();) {
174
			Object masterElement = it.next();
175
			DetailPropertyListener detailListener = new DetailPropertyListener(
176
					source, masterElement);
177
			detailProperty
178
					.addValueChangeListener(masterElement, detailListener);
179
			masterElementToDetailListener.put(masterElement, detailListener);
180
		}
181
182
		sourceToMasterElementToDetailListener.put(source,
183
				masterElementToDetailListener);
184
	}
185
186
	protected void removeListenerFrom(Object source) {
187
		masterProperty.removeListChangeListener(source, masterListener);
188
		Map masterElementToDetailListener = (Map) sourceToMasterElementToDetailListener
189
				.remove(source);
190
		if (masterElementToDetailListener != null) {
191
			for (Iterator it = masterElementToDetailListener.entrySet()
192
					.iterator(); it.hasNext();) {
193
				Map.Entry entry = (Map.Entry) it.next();
194
				detailProperty.removeValueChangeListener(entry.getKey(),
195
						(DetailPropertyListener) entry.getValue());
196
			}
197
		}
198
	}
199
200
	public List getList(Object source) {
201
		List result = new ArrayList();
202
		for (Iterator it = masterProperty.getList(source).iterator(); it
203
				.hasNext();) {
204
			result.add(detailProperty.getValue(it.next()));
205
		}
206
		return result;
207
	}
208
209
	public boolean add(Object source, Object o) {
210
		throw new UnsupportedOperationException();
211
	}
212
213
	public boolean addAll(Object source, Collection c) {
214
		throw new UnsupportedOperationException();
215
	}
216
217
	public void clear(Object source) {
218
		throw new UnsupportedOperationException();
219
	}
220
221
	public Object getElementType() {
222
		return detailProperty.getValueType();
223
	}
224
225
	public boolean remove(Object source, Object o) {
226
		throw new UnsupportedOperationException();
227
	}
228
229
	public boolean removeAll(Object source, Collection c) {
230
		throw new UnsupportedOperationException();
231
	}
232
233
	public boolean retainAll(Object source, Collection c) {
234
		throw new UnsupportedOperationException();
235
	}
236
237
	public void add(Object source, int index, Object element) {
238
		throw new UnsupportedOperationException();
239
	}
240
241
	public boolean addAll(Object source, int index, Collection c) {
242
		throw new UnsupportedOperationException();
243
	}
244
245
	public Object move(Object source, int oldIndex, int newIndex) {
246
		throw new UnsupportedOperationException();
247
	}
248
249
	public Object remove(Object source, int index) {
250
		throw new UnsupportedOperationException();
251
	}
252
253
	public Object set(Object source, int index, Object element) {
254
		Object masterElement = masterProperty.get(source, index);
255
		Object result = detailProperty.getValue(masterElement);
256
		detailProperty.setValue(masterElement, element);
257
		return result;
258
	}
259
}
(-)src/org/eclipse/core/databinding/property/IPropertyObservable.java (+28 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import org.eclipse.core.databinding.observable.IObserving;
15
16
/**
17
 * Provides access to the details of property observables
18
 * 
19
 * @since 1.2
20
 */
21
public interface IPropertyObservable extends IObserving {
22
	/**
23
	 * Returns the property being observed
24
	 * 
25
	 * @return the property being observed
26
	 */
27
	IProperty getProperty();
28
}
(-)src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailMap.java (+133 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property.masterdetail;
13
14
import java.util.HashMap;
15
import java.util.Map;
16
17
import org.eclipse.core.databinding.observable.Diffs;
18
import org.eclipse.core.databinding.observable.map.MapDiff;
19
import org.eclipse.core.databinding.property.IMapProperty;
20
import org.eclipse.core.databinding.property.IMapPropertyChangeListener;
21
import org.eclipse.core.databinding.property.IValueProperty;
22
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
23
import org.eclipse.core.databinding.property.MapProperty;
24
import org.eclipse.core.databinding.property.MapPropertyChangeEvent;
25
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
26
27
/**
28
 * @since 3.3
29
 * 
30
 */
31
public class ValuePropertyDetailMap extends MapProperty {
32
	private final IValueProperty masterProperty;
33
	private final IMapProperty detailProperty;
34
35
	private Map sourceToDetailListener = new HashMap();
36
37
	private IValuePropertyChangeListener masterListener = new MasterValueListener();
38
39
	private class MasterValueListener implements IValuePropertyChangeListener {
40
		public void handleValuePropertyChange(ValuePropertyChangeEvent event) {
41
			Object oldSource = event.diff.getOldValue();
42
			Object newSource = event.diff.getNewValue();
43
44
			Map oldMap = detailProperty.getMap(oldSource);
45
			Map newMap = detailProperty.getMap(newSource);
46
			MapDiff diff = Diffs.computeMapDiff(oldMap, newMap);
47
48
			Object source = event.getSource();
49
50
			removeDetailPropertyListener(source);
51
			addDetailPropertyListener(source);
52
53
			fireMapChange(source, diff);
54
		}
55
	}
56
57
	private class DetailPropertyListener implements IMapPropertyChangeListener {
58
		private Object source;
59
		private Object masterValue;
60
61
		DetailPropertyListener(Object source, Object masterValue) {
62
			this.source = source;
63
			this.masterValue = masterValue;
64
		}
65
66
		public void handleMapPropertyChange(MapPropertyChangeEvent event) {
67
			fireMapChange(source, event.diff);
68
		}
69
	}
70
71
	/**
72
	 * @param masterProperty
73
	 * @param detailProperty
74
	 */
75
	public ValuePropertyDetailMap(IValueProperty masterProperty,
76
			IMapProperty detailProperty) {
77
		this.masterProperty = masterProperty;
78
		this.detailProperty = detailProperty;
79
	}
80
81
	protected void addListenerTo(Object source) {
82
		masterProperty.addValueChangeListener(source, masterListener);
83
		addDetailPropertyListener(source);
84
	}
85
86
	protected void removeListenerFrom(Object source) {
87
		masterProperty.removeValueChangeListener(source, masterListener);
88
		removeDetailPropertyListener(source);
89
	}
90
91
	private void addDetailPropertyListener(Object source) {
92
		Object masterValue = masterProperty.getValue(source);
93
		DetailPropertyListener detailListener = new DetailPropertyListener(
94
				source, masterValue);
95
		detailProperty.addMapChangeListener(masterValue, detailListener);
96
		sourceToDetailListener.put(source, detailListener);
97
	}
98
99
	private void removeDetailPropertyListener(Object source) {
100
		DetailPropertyListener detailListener = (DetailPropertyListener) sourceToDetailListener
101
				.remove(source);
102
		if (detailListener != null) {
103
			detailProperty.removeMapChangeListener(detailListener.masterValue,
104
					detailListener);
105
		}
106
		sourceToDetailListener.remove(source);
107
	}
108
109
	public Map getMap(Object source) {
110
		Object masterValue = masterProperty.getValue(source);
111
		return detailProperty.getMap(masterValue);
112
	}
113
114
	public void clear(Object source) {
115
		Object masterValue = masterProperty.getValue(source);
116
		detailProperty.clear(masterValue);
117
	}
118
119
	public Object put(Object source, Object key, Object value) {
120
		Object masterValue = masterProperty.getValue(source);
121
		return detailProperty.put(masterValue, key, value);
122
	}
123
124
	public void putAll(Object source, Map t) {
125
		Object masterValue = masterProperty.getValue(source);
126
		detailProperty.putAll(masterValue, t);
127
	}
128
129
	public Object remove(Object source, Object key) {
130
		Object masterValue = masterProperty.getValue(source);
131
		return detailProperty.remove(masterValue, key);
132
	}
133
}
(-)src/org/eclipse/core/databinding/property/ISetPropertyChangeListener.java (+27 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
/**
15
 * Listener for changes to set properties on a property source
16
 * 
17
 * @since 1.2
18
 */
19
public interface ISetPropertyChangeListener extends IPropertyChangeListener {
20
	/**
21
	 * Handle a change to a set property on a specific property source.
22
	 * 
23
	 * @param event
24
	 *            an event describing the set change that occured.
25
	 */
26
	public void handleSetPropertyChange(SetPropertyChangeEvent event);
27
}
(-)src/org/eclipse/core/databinding/property/BasicSetProperty.java (+186 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.Collection;
15
import java.util.Collections;
16
import java.util.HashSet;
17
import java.util.Iterator;
18
import java.util.Set;
19
20
import org.eclipse.core.databinding.observable.Diffs;
21
import org.eclipse.core.databinding.observable.set.SetDiff;
22
import org.eclipse.core.internal.databinding.property.PropertyUpdateHelper;
23
24
/**
25
 * Abstract set property implementation for properties where the set can be
26
 * completely replaced in a single atomic operation.
27
 * <p>
28
 * For example, a set-typed bean property Customer.addresses can be modified by
29
 * calling Customer.setAddresses(List addresses).
30
 * 
31
 * @since 1.2
32
 */
33
public abstract class BasicSetProperty extends SetProperty {
34
	private PropertyUpdateHelper updateHelper = new PropertyUpdateHelper();
35
36
	/**
37
	 * Returns whether this property is currently being updated on the source.
38
	 * Implementors should query this value to avoid unnecessary calculations,
39
	 * such as computing a diff.
40
	 * 
41
	 * @param source
42
	 *            the property source
43
	 * @return whether this property is currently being updated on the source.
44
	 */
45
	protected boolean isUpdating(Object source) {
46
		return updateHelper.isUpdating(source);
47
	}
48
49
	/**
50
	 * Sets the set property on the source to the specified set, then fires a
51
	 * set change using the specified diff.
52
	 * 
53
	 * @param source
54
	 *            the property source
55
	 * @param set
56
	 *            the new set
57
	 * @param diff
58
	 *            the diff to be fired after the set property has been set. If
59
	 *            null, the diff will be computed.
60
	 */
61
	protected void setSet(Object source, Set set, SetDiff diff) {
62
		if (diff == null) {
63
			diff = Diffs.computeSetDiff(getSet(source), set);
64
		}
65
66
		updateHelper.setUpdating(source, true);
67
		try {
68
			doSetSet(source, set);
69
		} finally {
70
			updateHelper.setUpdating(source, false);
71
		}
72
73
		if (hasListeners(source)) {
74
			fireSetChange(source, diff);
75
		}
76
	}
77
78
	protected void fireSetChange(Object source, SetDiff diff) {
79
		if (!isUpdating(source))
80
			super.fireSetChange(source, diff);
81
	}
82
83
	/**
84
	 * Sets the set property on the source to the specified set.
85
	 * 
86
	 * @param source
87
	 *            the property source
88
	 * @param set
89
	 *            the new set
90
	 */
91
	protected abstract void doSetSet(Object source, Set set);
92
93
	public boolean add(Object source, Object o) {
94
		Set set = getSet(source);
95
		if (!set.contains(o)) {
96
			set = new HashSet(set);
97
			boolean added = set.add(o);
98
			if (added)
99
				setSet(source, set, Diffs.createSetDiff(Collections
100
						.singleton(o), Collections.EMPTY_SET));
101
			return added;
102
		}
103
		return false;
104
	}
105
106
	public boolean addAll(Object source, Collection c) {
107
		Set set = getSet(source);
108
		Set additions = new HashSet();
109
		for (Iterator it = c.iterator(); it.hasNext();) {
110
			Object o = it.next();
111
			if (!set.contains(o)) {
112
				additions.add(o);
113
			}
114
		}
115
		boolean changed = !additions.isEmpty();
116
		if (changed) {
117
			set = new HashSet(set);
118
			set.addAll(additions);
119
			setSet(source, set, Diffs.createSetDiff(additions,
120
					Collections.EMPTY_SET));
121
		}
122
		return changed;
123
	}
124
125
	public void clear(Object source) {
126
		setSet(source, new HashSet(), Diffs.createSetDiff(
127
				Collections.EMPTY_SET, getSet(source)));
128
	}
129
130
	public boolean remove(Object source, Object o) {
131
		Set set = getSet(source);
132
		if (set.contains(o)) {
133
			set = new HashSet(set);
134
			boolean removed = set.remove(o);
135
			if (removed)
136
				setSet(source, set, Diffs.createSetDiff(Collections.EMPTY_SET,
137
						Collections.singleton(o)));
138
			return removed;
139
		}
140
		return false;
141
	}
142
143
	public boolean removeAll(Object source, Collection c) {
144
		Set set = new HashSet(getSet(source));
145
		Set removals = new HashSet();
146
		for (Iterator it = set.iterator(); it.hasNext();) {
147
			Object o = it.next();
148
			if (c.contains(o)) {
149
				removals.add(o);
150
				it.remove();
151
			}
152
		}
153
		boolean changed = !removals.isEmpty();
154
		if (changed) {
155
			setSet(source, set, Diffs.createSetDiff(Collections.EMPTY_SET,
156
					removals));
157
		}
158
		return changed;
159
	}
160
161
	public boolean retainAll(Object source, Collection c) {
162
		Set set = new HashSet(getSet(source));
163
		Set removals = new HashSet();
164
		for (Iterator it = set.iterator(); it.hasNext();) {
165
			Object o = it.next();
166
			if (!c.contains(o)) {
167
				removals.add(o);
168
				it.remove();
169
			}
170
		}
171
		boolean changed = !removals.isEmpty();
172
		if (changed) {
173
			setSet(source, set, Diffs.createSetDiff(Collections.EMPTY_SET,
174
					removals));
175
		}
176
		return changed;
177
	}
178
179
	public synchronized void dispose() {
180
		if (updateHelper != null) {
181
			updateHelper.dispose();
182
			updateHelper = null;
183
		}
184
		super.dispose();
185
	}
186
}
(-)src/org/eclipse/core/databinding/property/MapPropertyChangeEvent.java (+57 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import org.eclipse.core.databinding.observable.map.MapDiff;
15
import org.eclipse.core.runtime.Assert;
16
17
/**
18
 * Map change event describing an incremental change of a map property on a
19
 * particular property source.
20
 * 
21
 * @since 1.2
22
 */
23
public class MapPropertyChangeEvent extends PropertyChangeEvent {
24
	private static final long serialVersionUID = 1L;
25
26
	/**
27
	 * The map property that changed
28
	 */
29
	public final IMapProperty property;
30
31
	/**
32
	 * MapDiff enumerating the added, changed, and removed entries in the map.
33
	 */
34
	public final MapDiff diff;
35
36
	/**
37
	 * Constructs a MapPropertyChangeEvent with the given attributes
38
	 * 
39
	 * @param source
40
	 *            the property source
41
	 * @param property
42
	 *            the property that changed on the source
43
	 * @param diff
44
	 *            a MapDiff describing the changes to the map property
45
	 */
46
	public MapPropertyChangeEvent(Object source, IMapProperty property,
47
			MapDiff diff) {
48
		super(source);
49
		this.property = property;
50
		Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$
51
		this.diff = diff;
52
	}
53
54
	void dispatch(IPropertyChangeListener listener) {
55
		((IMapPropertyChangeListener) listener).handleMapPropertyChange(this);
56
	}
57
}
(-)src/org/eclipse/core/databinding/property/PropertyChangeEvent.java (+24 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.EventObject;
15
16
abstract class PropertyChangeEvent extends EventObject {
17
	private static final long serialVersionUID = 1L;
18
19
	PropertyChangeEvent(Object source) {
20
		super(source);
21
	}
22
23
	abstract void dispatch(IPropertyChangeListener listener);
24
}
(-)src/org/eclipse/core/internal/databinding/property/masterdetail/MapPropertyDetailValueMap.java (+223 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property.masterdetail;
13
14
import java.util.HashMap;
15
import java.util.HashSet;
16
import java.util.Iterator;
17
import java.util.Map;
18
import java.util.Set;
19
20
import org.eclipse.core.databinding.observable.Diffs;
21
import org.eclipse.core.databinding.observable.map.MapDiff;
22
import org.eclipse.core.databinding.property.IMapProperty;
23
import org.eclipse.core.databinding.property.IMapPropertyChangeListener;
24
import org.eclipse.core.databinding.property.IValueProperty;
25
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
26
import org.eclipse.core.databinding.property.MapProperty;
27
import org.eclipse.core.databinding.property.MapPropertyChangeEvent;
28
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
29
import org.eclipse.core.internal.databinding.Util;
30
31
/**
32
 * @since 3.3
33
 * 
34
 */
35
public class MapPropertyDetailValueMap extends MapProperty {
36
	private final IMapProperty masterProperty;
37
	private final IValueProperty detailProperty;
38
39
	private Map sourceToKeyToDetailListener = new HashMap();
40
41
	private IMapPropertyChangeListener masterListener = new MasterPropertyListener();
42
43
	private class MasterPropertyListener implements IMapPropertyChangeListener {
44
		public void handleMapPropertyChange(final MapPropertyChangeEvent event) {
45
			Object source = event.getSource();
46
47
			Map oldValues = new HashMap();
48
			Map newValues = new HashMap();
49
50
			Set addedKeys = event.diff.getAddedKeys();
51
			for (Iterator it = addedKeys.iterator(); it.hasNext();) {
52
				Object key = it.next();
53
				Object newMasterValue = event.diff.getNewValue(key);
54
				Object newDetailValue = detailProperty.getValue(newMasterValue);
55
				newValues.put(key, newDetailValue);
56
				addPropertySourceListener(source, key, newMasterValue);
57
			}
58
59
			Set removedKeys = event.diff.getRemovedKeys();
60
			for (Iterator it = removedKeys.iterator(); it.hasNext();) {
61
				Object key = it.next();
62
				Object oldMasterValue = event.diff.getOldValue(key);
63
				Object oldDetailValue = detailProperty.getValue(oldMasterValue);
64
				oldValues.put(key, oldDetailValue);
65
				removePropertySourceListener(source, key, oldMasterValue);
66
			}
67
68
			Set changedKeys = new HashSet(event.diff.getChangedKeys());
69
			for (Iterator it = changedKeys.iterator(); it.hasNext();) {
70
				Object key = it.next();
71
72
				Object oldMasterValue = event.diff.getOldValue(key);
73
				Object newMasterValue = event.diff.getNewValue(key);
74
75
				Object oldDetailValue = detailProperty.getValue(oldMasterValue);
76
				Object newDetailValue = detailProperty.getValue(newMasterValue);
77
78
				if (Util.equals(oldDetailValue, newDetailValue)) {
79
					it.remove();
80
				} else {
81
					oldValues.put(key, oldDetailValue);
82
					newValues.put(key, newDetailValue);
83
				}
84
85
				removePropertySourceListener(source, key, oldMasterValue);
86
				addPropertySourceListener(source, key, newMasterValue);
87
			}
88
89
			MapDiff diff = Diffs.createMapDiff(addedKeys, removedKeys,
90
					changedKeys, oldValues, newValues);
91
92
			fireMapChange(source, diff);
93
		}
94
95
		private void addPropertySourceListener(Object source, Object key,
96
				Object masterValue) {
97
			Map keyToDetailListener = (Map) sourceToKeyToDetailListener
98
					.get(source);
99
			if (keyToDetailListener == null) {
100
				sourceToKeyToDetailListener.put(source,
101
						keyToDetailListener = new HashMap());
102
			}
103
			if (!keyToDetailListener.containsKey(key)) {
104
				DetailPropertyListener detailListener = new DetailPropertyListener(
105
						source, key);
106
				detailProperty.addValueChangeListener(masterValue,
107
						detailListener);
108
				keyToDetailListener.put(key, detailListener);
109
			}
110
		}
111
112
		private void removePropertySourceListener(Object source, Object key,
113
				Object masterValue) {
114
			Map keyToDetailListener = (Map) sourceToKeyToDetailListener
115
					.get(source);
116
			if (keyToDetailListener != null
117
					&& keyToDetailListener.containsKey(key)) {
118
				DetailPropertyListener detailListener = (DetailPropertyListener) keyToDetailListener
119
						.remove(key);
120
				detailProperty.removeValueChangeListener(masterValue,
121
						detailListener);
122
			}
123
		}
124
	}
125
126
	private class DetailPropertyListener implements
127
			IValuePropertyChangeListener {
128
		private Object source;
129
		private Object key;
130
131
		DetailPropertyListener(Object source, Object key) {
132
			this.source = source;
133
			this.key = key;
134
		}
135
136
		public void handleValuePropertyChange(ValuePropertyChangeEvent event) {
137
			fireMapChange(source, Diffs.createMapDiffSingleChange(key,
138
					event.diff.getOldValue(), event.diff.getNewValue()));
139
		}
140
	}
141
142
	/**
143
	 * @param masterProperty
144
	 * @param detailProperty
145
	 */
146
	public MapPropertyDetailValueMap(IMapProperty masterProperty,
147
			IValueProperty detailProperty) {
148
		this.masterProperty = masterProperty;
149
		this.detailProperty = detailProperty;
150
	}
151
152
	protected void addListenerTo(Object source) {
153
		masterProperty.addMapChangeListener(source, masterListener);
154
155
		Map keyToDetailListener = new HashMap();
156
		for (Iterator it = masterProperty.getMap(source).entrySet().iterator(); it
157
				.hasNext();) {
158
			Map.Entry entry = (Map.Entry) it.next();
159
			Object key = entry.getKey();
160
			Object masterValue = entry.getValue();
161
			DetailPropertyListener detailListener = new DetailPropertyListener(
162
					source, key);
163
			detailProperty.addValueChangeListener(masterValue, detailListener);
164
			keyToDetailListener.put(masterValue, detailListener);
165
		}
166
167
		sourceToKeyToDetailListener.put(source, keyToDetailListener);
168
	}
169
170
	protected void removeListenerFrom(Object source) {
171
		masterProperty.removeMapChangeListener(source, masterListener);
172
		Map masterElementToDetailListener = (Map) sourceToKeyToDetailListener
173
				.remove(source);
174
		if (masterElementToDetailListener != null) {
175
			for (Iterator it = masterElementToDetailListener.entrySet()
176
					.iterator(); it.hasNext();) {
177
				Map.Entry entry = (Map.Entry) it.next();
178
				detailProperty.removeValueChangeListener(entry.getKey(),
179
						(DetailPropertyListener) entry.getValue());
180
			}
181
		}
182
	}
183
184
	public Map getMap(Object source) {
185
		Map result = new HashMap();
186
		for (Iterator it = masterProperty.getMap(source).entrySet().iterator(); it
187
				.hasNext();) {
188
			Map.Entry entry = (Map.Entry) it.next();
189
			result.put(entry.getKey(), detailProperty
190
					.getValue(entry.getValue()));
191
		}
192
		return result;
193
	}
194
195
	public void clear(Object source) {
196
		throw new UnsupportedOperationException();
197
	}
198
199
	public Object put(Object source, Object key, Object value) {
200
		if (!masterProperty.containsKey(source, key))
201
			return null;
202
		Object masterValue = masterProperty.get(source, key);
203
204
		Object result = detailProperty.getValue(masterValue);
205
		detailProperty.setValue(masterValue, value);
206
		return result;
207
	}
208
209
	public void putAll(Object source, Map t) {
210
		for (Iterator it = t.entrySet().iterator(); it.hasNext();) {
211
			Map.Entry entry = (Map.Entry) it.next();
212
			Object masterKey = entry.getKey();
213
			Object detailValue = entry.getValue();
214
			if (masterProperty.getMap(source).containsKey(masterKey)) {
215
				detailProperty.setValue(masterKey, detailValue);
216
			}
217
		}
218
	}
219
220
	public Object remove(Object source, Object key) {
221
		throw new UnsupportedOperationException();
222
	}
223
}
(-)src/org/eclipse/core/databinding/property/ICollectionProperty.java (+186 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.Collection;
15
16
/**
17
 * Interface for collection-typed properties.
18
 * 
19
 * @since 1.2
20
 * @noimplement This interface is not intended to be implemented by clients.
21
 */
22
public interface ICollectionProperty extends IProperty {
23
	/**
24
	 * Returns the size of the source's collection property
25
	 * 
26
	 * @param source
27
	 *            the property source
28
	 * @return the size of the source's collection property
29
	 */
30
	int size(Object source);
31
32
	/**
33
	 * Returns whether the source's collection property is empty
34
	 * 
35
	 * @param source
36
	 *            the property source
37
	 * @return whether the source's collection property is empty
38
	 */
39
	boolean isEmpty(Object source);
40
41
	/**
42
	 * Returns whether the source's collection property contains the given
43
	 * element.
44
	 * 
45
	 * @param source
46
	 *            the property source
47
	 * @param o
48
	 *            the element
49
	 * @return whether the source's collection property contains the given
50
	 *         element.
51
	 */
52
	boolean contains(Object source, Object o);
53
54
	/**
55
	 * Returns an array of all elements in the source's collection property
56
	 * 
57
	 * @param source
58
	 *            the property source
59
	 * @return an array of all elements in the source's collection property
60
	 */
61
	Object[] toArray(Object source);
62
63
	/**
64
	 * Returns an array of all elements in the source's collection property
65
	 * 
66
	 * @param source
67
	 *            the property source
68
	 * @param array
69
	 *            the array into which the elements will be copied. If the array
70
	 *            is not large enough to hold all elements, the elements will be
71
	 *            returned in a new array of the same runtime type.
72
	 * @return an array of all elements in the source's collection property
73
	 */
74
	Object[] toArray(Object source, Object[] array);
75
76
	/**
77
	 * Adds the element to the source's collection property
78
	 * 
79
	 * @param source
80
	 *            the property source
81
	 * @param o
82
	 *            the element to add
83
	 * @return whether the element was added to the source's collection property
84
	 */
85
	boolean add(Object source, Object o);
86
87
	/**
88
	 * Removes the element from the source's collection property
89
	 * 
90
	 * @param source
91
	 *            the property source
92
	 * @param o
93
	 *            the element to remove
94
	 * @return whether the element was removed from the source's collection
95
	 *         property
96
	 */
97
	boolean remove(Object source, Object o);
98
99
	/**
100
	 * Returns whether the source's collection property contains all elements in
101
	 * the given collection
102
	 * 
103
	 * @param source
104
	 *            the property source
105
	 * @param c
106
	 *            the collection of elements to test for
107
	 * @return whether the source's collection property contains all elements in
108
	 *         the given collection
109
	 */
110
	boolean containsAll(Object source, Collection c);
111
112
	/**
113
	 * Adds all elements in the specified collection to the source's collection
114
	 * property.
115
	 * 
116
	 * @param source
117
	 *            the property source
118
	 * @param c
119
	 *            the collection of elements to add.
120
	 * @return whether the source's collection property was changed
121
	 */
122
	boolean addAll(Object source, Collection c);
123
124
	/**
125
	 * Removes all elements from the source's collection property which are
126
	 * contained in the specified collection.
127
	 * 
128
	 * @param source
129
	 *            the property source
130
	 * @param c
131
	 *            the collection of elements to be removed
132
	 * @return whether the source's collection property was changed
133
	 */
134
	boolean removeAll(Object source, Collection c);
135
136
	/**
137
	 * Removes all elements from the source's collection property which are not
138
	 * contained in the specified collection.
139
	 * 
140
	 * @param source
141
	 *            the property source
142
	 * @param c
143
	 *            the collection of elements to retain
144
	 * @return whether the source's collection property was changed
145
	 */
146
	boolean retainAll(Object source, Collection c);
147
148
	/**
149
	 * Removes all elements from the source's collection property.
150
	 * 
151
	 * @param source
152
	 *            the property source
153
	 */
154
	void clear(Object source);
155
156
	/**
157
	 * Returns whether the source's collection property is equal to the
158
	 * argument.
159
	 * 
160
	 * @param source
161
	 *            the property source
162
	 * @param o
163
	 *            the object to test for equality to the source's collection
164
	 *            property
165
	 * @return whether the source's collection property is equal to the argument
166
	 */
167
	boolean equals(Object source, Object o);
168
169
	/**
170
	 * Returns the hash code of the source's collection property.
171
	 * 
172
	 * @param source
173
	 *            the property source
174
	 * @return the hash code of the source's collection property
175
	 */
176
	int hashCode(Object source);
177
178
	/**
179
	 * Returns the type of the elements in the collection or <code>null</code>
180
	 * if untyped
181
	 * 
182
	 * @return the type of the elements in the collection or <code>null</code>
183
	 *         if untyped
184
	 */
185
	Object getElementType();
186
}
(-)src/org/eclipse/core/databinding/property/ListPropertyChangeEvent.java (+57 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import org.eclipse.core.databinding.observable.list.ListDiff;
15
import org.eclipse.core.runtime.Assert;
16
17
/**
18
 * List change event describing an incremental change of a list property on a
19
 * particular property source.
20
 * 
21
 * @since 1.2
22
 */
23
public class ListPropertyChangeEvent extends PropertyChangeEvent {
24
	private static final long serialVersionUID = 1L;
25
26
	/**
27
	 * The list property that changed
28
	 */
29
	public final IListProperty property;
30
31
	/**
32
	 * ListDiff enumerating the added and removed elements in the list.
33
	 */
34
	public final ListDiff diff;
35
36
	/**
37
	 * Constructs a ListPropertyChangeEvent with the given attributes
38
	 * 
39
	 * @param source
40
	 *            the property source
41
	 * @param property
42
	 *            the property that changed on the source
43
	 * @param diff
44
	 *            a ListDiff describing the changes to the list property
45
	 */
46
	public ListPropertyChangeEvent(Object source, IListProperty property,
47
			ListDiff diff) {
48
		super(source);
49
		this.property = property;
50
		Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$
51
		this.diff = diff;
52
	}
53
54
	void dispatch(IPropertyChangeListener listener) {
55
		((IListPropertyChangeListener) listener).handleListPropertyChange(this);
56
	}
57
}
(-)src/org/eclipse/core/databinding/property/PropertyChangeSupport.java (+118 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.Collections;
15
import java.util.IdentityHashMap;
16
import java.util.Iterator;
17
import java.util.Map;
18
19
import org.eclipse.core.runtime.ListenerList;
20
21
class PropertyChangeSupport {
22
	private Property property;
23
	private Map changeListeners;
24
25
	PropertyChangeSupport(Property property) {
26
		this.property = property;
27
		this.changeListeners = null;
28
	}
29
30
	protected final void addListener(Object source,
31
			IPropertyChangeListener listener) {
32
		boolean hadListeners = hasListeners(source);
33
34
		if (changeListeners == null) {
35
			synchronized (this) {
36
				if (changeListeners == null) {
37
					changeListeners = Collections
38
							.synchronizedMap(new IdentityHashMap());
39
				}
40
			}
41
		}
42
43
		ListenerList listeners;
44
		synchronized (changeListeners) {
45
			if (changeListeners.containsKey(source)) {
46
				listeners = (ListenerList) changeListeners.get(source);
47
			} else {
48
				changeListeners.put(source, listeners = new ListenerList());
49
			}
50
		}
51
52
		synchronized (listeners) {
53
			listeners.add(listener);
54
		}
55
56
		if (!hadListeners)
57
			property.addListenerTo(source);
58
	}
59
60
	/**
61
	 * Returns whether the change support has listeners registered for the given
62
	 * property source
63
	 * 
64
	 * @param source
65
	 *            the property source
66
	 * @return whether the change support has listeners registered for the given
67
	 *         property source
68
	 */
69
	public boolean hasListeners(Object source) {
70
		return changeListeners != null && changeListeners.containsKey(source);
71
	}
72
73
	protected final void removeListener(Object source,
74
			IPropertyChangeListener listener) {
75
		if (changeListeners != null) {
76
			ListenerList listeners = (ListenerList) changeListeners.get(source);
77
			if (listeners != null) {
78
				listeners.remove(listener);
79
				if (listeners.isEmpty()) {
80
					synchronized (listeners) {
81
						if (listeners.isEmpty()) {
82
							changeListeners.remove(source);
83
							property.removeListenerFrom(source);
84
						}
85
					}
86
				}
87
			}
88
		}
89
	}
90
91
	protected final void firePropertyChange(PropertyChangeEvent event) {
92
		ListenerList listenerList;
93
		synchronized (this) {
94
			if (changeListeners == null)
95
				return;
96
			listenerList = (ListenerList) changeListeners
97
					.get(event.getSource());
98
		}
99
		if (listenerList != null) {
100
			Object[] listeners = listenerList.getListeners();
101
			for (int i = 0; i < listeners.length; i++) {
102
				event.dispatch((IPropertyChangeListener) listeners[i]);
103
			}
104
		}
105
	}
106
107
	void dispose() {
108
		if (changeListeners != null) {
109
			for (Iterator iterator = changeListeners.keySet().iterator(); iterator
110
					.hasNext();) {
111
				Object source = iterator.next();
112
				property.removeListenerFrom(source);
113
				iterator.remove();
114
			}
115
			changeListeners = null;
116
		}
117
	}
118
}
(-)src/org/eclipse/core/databinding/property/PropertyObservables.java (+371 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import org.eclipse.core.databinding.observable.IObservable;
15
import org.eclipse.core.databinding.observable.Realm;
16
import org.eclipse.core.databinding.observable.list.IObservableList;
17
import org.eclipse.core.databinding.observable.map.IObservableMap;
18
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
19
import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
20
import org.eclipse.core.databinding.observable.set.IObservableSet;
21
import org.eclipse.core.databinding.observable.value.IObservableValue;
22
import org.eclipse.core.internal.databinding.property.ListValuePropertyObservableList;
23
import org.eclipse.core.internal.databinding.property.MapValuePropertyObservableMap;
24
import org.eclipse.core.internal.databinding.property.PropertyObservableList;
25
import org.eclipse.core.internal.databinding.property.PropertyObservableMap;
26
import org.eclipse.core.internal.databinding.property.PropertyObservableSet;
27
import org.eclipse.core.internal.databinding.property.PropertyObservableValue;
28
import org.eclipse.core.internal.databinding.property.SetValuePropertyObservableMap;
29
30
/**
31
 * A factory for creating observables observing properties of source objects
32
 * 
33
 * @since 1.2
34
 */
35
public class PropertyObservables {
36
	/**
37
	 * Returns an observable value on the default realm that tracks the given
38
	 * property of the source object.
39
	 * 
40
	 * @param source
41
	 *            the property value
42
	 * @param property
43
	 *            the value property to observe
44
	 * @return an observable value on the default realm that tracks the given
45
	 *         property of the source object.
46
	 * @since 1.2
47
	 */
48
	public static IObservableValue observeValue(Object source,
49
			IValueProperty property) {
50
		return observeValue(Realm.getDefault(), source, property);
51
	}
52
53
	/**
54
	 * Returns an observable value on the given realm that tracks the given
55
	 * property of the source object
56
	 * 
57
	 * @param realm
58
	 *            the realm
59
	 * @param source
60
	 *            the property source
61
	 * @param property
62
	 *            the value property observe
63
	 * @return an observable value that tracks the given property of the source
64
	 *         object
65
	 * @since 1.2
66
	 */
67
	public static IObservableValue observeValue(Realm realm, Object source,
68
			IValueProperty property) {
69
		return new PropertyObservableValue(realm, source, property);
70
	}
71
72
	/**
73
	 * Returns a factory for creating observable values on the default realm
74
	 * tracking the given property of a particular source object
75
	 * 
76
	 * @param property
77
	 *            the property to observe
78
	 * @return a factory for creating observable values on the default realm
79
	 *         tracking the given property of a particular source object
80
	 */
81
	public static IObservableFactory valueFactory(IValueProperty property) {
82
		return valueFactory(Realm.getDefault(), property);
83
	}
84
85
	/**
86
	 * Returns a factory for creating observable values on the given realm
87
	 * tracking the given property of a particular source object
88
	 * 
89
	 * @param realm
90
	 *            the realm
91
	 * @param property
92
	 *            the property to observe
93
	 * @return a factory for creating observable values on the given realm
94
	 *         tracking the given property of a particular source object
95
	 */
96
	public static IObservableFactory valueFactory(final Realm realm,
97
			final IValueProperty property) {
98
		return new IObservableFactory() {
99
			public IObservable createObservable(Object target) {
100
				return observeValue(realm, target, property);
101
			}
102
		};
103
	}
104
105
	/**
106
	 * Returns an observable value on the master observable's realm which tracks
107
	 * the given property of the current value of <code>master</code>.
108
	 * 
109
	 * @param master
110
	 *            the master observable
111
	 * @param property
112
	 *            the property to observe
113
	 * @return an observable value which tracks the given property of the
114
	 *         current value of <code>master</code>.
115
	 */
116
	public static IObservableValue observeDetailValue(IObservableValue master,
117
			IValueProperty property) {
118
		return MasterDetailObservables.detailValue(master, valueFactory(master
119
				.getRealm(), property), property.getValueType());
120
	}
121
122
	/**
123
	 * Returns an observable set on the default realm that tracks the given
124
	 * property of the source object
125
	 * 
126
	 * @param source
127
	 *            the property source
128
	 * @param property
129
	 *            the property to observe
130
	 * @return an observable set on the default realm that tracks the given
131
	 *         property of the source object
132
	 */
133
	public static IObservableSet observeSet(Object source, ISetProperty property) {
134
		return observeSet(Realm.getDefault(), source, property);
135
	}
136
137
	/**
138
	 * Returns an observable set on the given realm that tracks the given
139
	 * property of the source object
140
	 * 
141
	 * @param realm
142
	 *            the realm
143
	 * @param source
144
	 *            the property source
145
	 * @param property
146
	 *            the property to observe
147
	 * @return an observable set on the given realm that tracks the given
148
	 *         property of the source object
149
	 */
150
	public static IObservableSet observeSet(Realm realm, Object source,
151
			ISetProperty property) {
152
		return new PropertyObservableSet(realm, source, property);
153
	}
154
155
	/**
156
	 * Returns a factory for creating observable sets on the default realm
157
	 * tracking the given property of a particular source object
158
	 * 
159
	 * @param property
160
	 *            the property to be observed
161
	 * @return a factory for creating observable sets on the default realm
162
	 *         tracking the given property of a particular source object
163
	 */
164
	public static IObservableFactory setFactory(ISetProperty property) {
165
		return setFactory(Realm.getDefault(), property);
166
	}
167
168
	/**
169
	 * Returns a factory for creating obervable sets on the given realm tracking
170
	 * the given property of a particular source object
171
	 * 
172
	 * @param realm
173
	 *            the realm
174
	 * @param property
175
	 *            the property to be observed
176
	 * @return a factory for creating obervable sets on the given realm tracking
177
	 *         the given property of a particular source object
178
	 */
179
	public static IObservableFactory setFactory(final Realm realm,
180
			final ISetProperty property) {
181
		return new IObservableFactory() {
182
			public IObservable createObservable(Object target) {
183
				return observeSet(realm, target, property);
184
			}
185
		};
186
	}
187
188
	/**
189
	 * Returns an observable set on the master observable's realm which tracks
190
	 * the given property of the current value of <code>master</code>.
191
	 * 
192
	 * @param master
193
	 *            the master observable
194
	 * @param property
195
	 *            the property to observe
196
	 * @return an observable set on the given realm which tracks the given
197
	 *         property of the current value of <code>master</code>.
198
	 */
199
	public static IObservableSet observeDetailSet(IObservableValue master,
200
			ISetProperty property) {
201
		return MasterDetailObservables.detailSet(master, setFactory(master
202
				.getRealm(), property), property.getElementType());
203
	}
204
205
	/**
206
	 * Returns an observable list on the default realm that tracks the given
207
	 * property of the source object
208
	 * 
209
	 * @param source
210
	 *            the property source
211
	 * @param property
212
	 *            the property to observe
213
	 * @return an observable list on the default realm that tracks the given
214
	 *         property of the source object
215
	 */
216
	public static IObservableList observeList(Object source,
217
			IListProperty property) {
218
		return observeList(Realm.getDefault(), source, property);
219
	}
220
221
	/**
222
	 * Returns an observable list on the given realm that tracks the given
223
	 * property of the source object
224
	 * 
225
	 * @param realm
226
	 *            the realm
227
	 * @param source
228
	 *            the property source
229
	 * @param property
230
	 *            the property to observe
231
	 * @return an observable list on the given realm that tracks the given
232
	 *         property of the source object
233
	 */
234
	public static IObservableList observeList(Realm realm, Object source,
235
			IListProperty property) {
236
		return new PropertyObservableList(realm, source, property);
237
	}
238
239
	/**
240
	 * Returns a factory for creating observable lists on the default realm
241
	 * tracking the given property of a particular source object
242
	 * 
243
	 * @param property
244
	 *            the property to be observed
245
	 * @return an observable value factory on
246
	 */
247
	public static IObservableFactory listFactory(IListProperty property) {
248
		return listFactory(Realm.getDefault(), property);
249
	}
250
251
	/**
252
	 * Returns a factory for creating obervable lists on the given realm
253
	 * tracking the given property of a particular source object
254
	 * 
255
	 * @param realm
256
	 *            the realm
257
	 * @param property
258
	 *            the property to be observed
259
	 * @return an observable value factory on
260
	 */
261
	public static IObservableFactory listFactory(final Realm realm,
262
			final IListProperty property) {
263
		return new IObservableFactory() {
264
			public IObservable createObservable(Object target) {
265
				return observeList(realm, target, property);
266
			}
267
		};
268
	}
269
270
	/**
271
	 * Returns an observable list on the master observable's realm which tracks
272
	 * the given property of the current value of <code>master</code>.
273
	 * 
274
	 * @param master
275
	 *            the master observable
276
	 * @param property
277
	 *            the property to observe
278
	 * @return an observable list on the given realm which tracks the given
279
	 *         property of the current value of <code>master</code>.
280
	 */
281
	public static IObservableList observeDetailList(IObservableValue master,
282
			IListProperty property) {
283
		return MasterDetailObservables.detailList(master, listFactory(master
284
				.getRealm(), property), property.getElementType());
285
	}
286
287
	/**
288
	 * Returns an observable list on the master observable's realm which tracks
289
	 * the given property of each element of <code>master</code>.
290
	 * 
291
	 * @param master
292
	 *            the master observable
293
	 * @param property
294
	 *            the property to observe on each element in the master
295
	 *            observable
296
	 * @return an observable
297
	 */
298
	public static IObservableList observeDetailValues(IObservableList master,
299
			IValueProperty property) {
300
		return new ListValuePropertyObservableList(master, property);
301
	}
302
303
	/**
304
	 * Returns an observable map on the default realm which tracks the given
305
	 * property of the source object
306
	 * 
307
	 * @param source
308
	 *            the property source
309
	 * @param property
310
	 *            the property to observe
311
	 * @return an observable map on the default realm which tracks the given
312
	 *         property of the source object
313
	 */
314
	public static IObservableMap observeMap(Object source, IMapProperty property) {
315
		return observeMap(Realm.getDefault(), source, property);
316
	}
317
318
	/**
319
	 * Returns an observable map on the given realm which tracks the given
320
	 * property of the source object
321
	 * 
322
	 * @param realm
323
	 *            the realm
324
	 * @param source
325
	 *            the property source
326
	 * @param property
327
	 *            the property to observe
328
	 * @return an observable map on the given realm which tracks the given
329
	 *         property of the source object
330
	 */
331
	public static IObservableMap observeMap(Realm realm, Object source,
332
			IMapProperty property) {
333
		return new PropertyObservableMap(realm, source, property);
334
	}
335
336
	/**
337
	 * Returns an observable map on the master observable's realm tracking the
338
	 * current values of the given property for the elements in the given set.
339
	 * 
340
	 * @param keySet
341
	 *            the master observable
342
	 * @param valueProperty
343
	 *            the property to observe on each element of the master
344
	 *            observable
345
	 * @return an observable map that tracks the current value of the given
346
	 *         property for the elements in the given set.
347
	 */
348
	public static IObservableMap observeDetailValues(IObservableSet keySet,
349
			IValueProperty valueProperty) {
350
		return new SetValuePropertyObservableMap(keySet, valueProperty);
351
	}
352
353
	/**
354
	 * Returns an observable map on the master observable's realm which tracks
355
	 * the current values of the given property for the entry values in the
356
	 * given map.
357
	 * 
358
	 * @param map
359
	 *            the master observable
360
	 * @param valueProperty
361
	 *            the property to observe on each entry value in the master
362
	 *            observable
363
	 * @return an observable map on the master observable's realm which tracks
364
	 *         the current value of the given property for the elements in the
365
	 *         given map's values collection
366
	 */
367
	public static IObservableMap observeDetailValues(IObservableMap map,
368
			IValueProperty valueProperty) {
369
		return new MapValuePropertyObservableMap(map, valueProperty);
370
	}
371
}
(-)src/org/eclipse/core/internal/databinding/property/masterdetail/ValuePropertyDetailSet.java (+150 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property.masterdetail;
13
14
import java.util.Collection;
15
import java.util.HashMap;
16
import java.util.Map;
17
import java.util.Set;
18
19
import org.eclipse.core.databinding.observable.Diffs;
20
import org.eclipse.core.databinding.observable.set.SetDiff;
21
import org.eclipse.core.databinding.property.ISetProperty;
22
import org.eclipse.core.databinding.property.ISetPropertyChangeListener;
23
import org.eclipse.core.databinding.property.IValueProperty;
24
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
25
import org.eclipse.core.databinding.property.SetProperty;
26
import org.eclipse.core.databinding.property.SetPropertyChangeEvent;
27
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
28
29
/**
30
 * @since 3.3
31
 * 
32
 */
33
public class ValuePropertyDetailSet extends SetProperty {
34
	private final IValueProperty masterProperty;
35
	private final ISetProperty detailProperty;
36
37
	private Map sourceToDetailListener = new HashMap();
38
39
	private IValuePropertyChangeListener masterListener = new MasterPropertyListener();
40
41
	private class MasterPropertyListener implements
42
			IValuePropertyChangeListener {
43
		public void handleValuePropertyChange(ValuePropertyChangeEvent event) {
44
			Object oldSource = event.diff.getOldValue();
45
			Object newSource = event.diff.getNewValue();
46
47
			Set oldSet = detailProperty.getSet(oldSource);
48
			Set newSet = detailProperty.getSet(newSource);
49
			SetDiff diff = Diffs.computeSetDiff(oldSet, newSet);
50
51
			Object source = event.getSource();
52
53
			removeDetailPropertyListener(source);
54
			addDetailPropertyListener(source);
55
56
			fireSetChange(source, diff);
57
		}
58
	}
59
60
	private class DetailPropertyListener implements ISetPropertyChangeListener {
61
		private Object source;
62
		private Object masterValue;
63
64
		DetailPropertyListener(Object source, Object masterValue) {
65
			this.source = source;
66
			this.masterValue = masterValue;
67
		}
68
69
		public void handleSetPropertyChange(SetPropertyChangeEvent event) {
70
			fireSetChange(source, event.diff);
71
		}
72
	}
73
74
	/**
75
	 * @param masterProperty
76
	 * @param detailProperty
77
	 */
78
	public ValuePropertyDetailSet(IValueProperty masterProperty,
79
			ISetProperty detailProperty) {
80
		this.masterProperty = masterProperty;
81
		this.detailProperty = detailProperty;
82
	}
83
84
	protected void addListenerTo(Object source) {
85
		masterProperty.addValueChangeListener(source, masterListener);
86
		addDetailPropertyListener(source);
87
	}
88
89
	protected void removeListenerFrom(Object source) {
90
		masterProperty.removeValueChangeListener(source, masterListener);
91
		removeDetailPropertyListener(source);
92
	}
93
94
	private void addDetailPropertyListener(Object source) {
95
		Object masterValue = masterProperty.getValue(source);
96
		DetailPropertyListener detailListener = new DetailPropertyListener(
97
				source, masterValue);
98
		detailProperty.addSetChangeListener(masterValue, detailListener);
99
		sourceToDetailListener.put(source, detailListener);
100
	}
101
102
	private void removeDetailPropertyListener(Object source) {
103
		DetailPropertyListener detailListener = (DetailPropertyListener) sourceToDetailListener
104
				.remove(source);
105
		if (detailListener != null) {
106
			detailProperty.removeSetChangeListener(detailListener.masterValue,
107
					detailListener);
108
		}
109
		sourceToDetailListener.remove(source);
110
	}
111
112
	public Set getSet(Object source) {
113
		Object masterValue = masterProperty.getValue(source);
114
		return detailProperty.getSet(masterValue);
115
	}
116
117
	public boolean add(Object source, Object o) {
118
		Object masterValue = masterProperty.getValue(source);
119
		return detailProperty.add(masterValue, o);
120
	}
121
122
	public boolean addAll(Object source, Collection c) {
123
		Object masterValue = masterProperty.getValue(source);
124
		return detailProperty.addAll(masterValue, c);
125
	}
126
127
	public void clear(Object source) {
128
		Object masterValue = masterProperty.getValue(source);
129
		detailProperty.clear(masterValue);
130
	}
131
132
	public Object getElementType() {
133
		return detailProperty.getElementType();
134
	}
135
136
	public boolean remove(Object source, Object o) {
137
		Object masterValue = masterProperty.getValue(source);
138
		return detailProperty.remove(masterValue, o);
139
	}
140
141
	public boolean removeAll(Object source, Collection c) {
142
		Object masterValue = masterProperty.getValue(source);
143
		return detailProperty.removeAll(masterValue, c);
144
	}
145
146
	public boolean retainAll(Object source, Collection c) {
147
		Object masterValue = masterProperty.getValue(source);
148
		return detailProperty.retainAll(masterValue, c);
149
	}
150
}
(-)src/org/eclipse/core/internal/databinding/property/PropertyObservableList.java (+374 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property;
13
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.Collections;
17
import java.util.ConcurrentModificationException;
18
import java.util.Iterator;
19
import java.util.List;
20
import java.util.ListIterator;
21
22
import org.eclipse.core.databinding.observable.ObservableTracker;
23
import org.eclipse.core.databinding.observable.Realm;
24
import org.eclipse.core.databinding.observable.list.AbstractObservableList;
25
import org.eclipse.core.databinding.property.IListProperty;
26
import org.eclipse.core.databinding.property.IListPropertyChangeListener;
27
import org.eclipse.core.databinding.property.IProperty;
28
import org.eclipse.core.databinding.property.IPropertyObservable;
29
import org.eclipse.core.databinding.property.ListPropertyChangeEvent;
30
31
/**
32
 * @since 3.3
33
 * 
34
 */
35
public class PropertyObservableList extends AbstractObservableList implements
36
		IPropertyObservable {
37
	private Object source;
38
	private IListProperty property;
39
40
	private volatile boolean updating = false;
41
42
	private boolean disposed = false;
43
44
	private transient volatile int modCount = 0;
45
46
	private IListPropertyChangeListener listener = new IListPropertyChangeListener() {
47
		public void handleListPropertyChange(final ListPropertyChangeEvent event) {
48
			if (!disposed && !updating) {
49
				getRealm().exec(new Runnable() {
50
					public void run() {
51
						getRealm().exec(new Runnable() {
52
							public void run() {
53
								modCount++;
54
								fireListChange(event.diff);
55
							}
56
						});
57
					}
58
				});
59
			}
60
		}
61
	};
62
63
	/**
64
	 * @param realm
65
	 * @param source
66
	 * @param property
67
	 */
68
	public PropertyObservableList(Realm realm, Object source,
69
			IListProperty property) {
70
		super(realm);
71
		this.source = source;
72
		this.property = property;
73
	}
74
75
	protected void firstListenerAdded() {
76
		if (!disposed) {
77
			property.addListChangeListener(source, listener);
78
		}
79
	}
80
81
	protected void lastListenerRemoved() {
82
		if (!disposed) {
83
			property.removeListChangeListener(source, listener);
84
		}
85
	}
86
87
	private void getterCalled() {
88
		ObservableTracker.getterCalled(this);
89
	}
90
91
	public Object getElementType() {
92
		return property.getElementType();
93
	}
94
95
	// Queries
96
97
	protected int doGetSize() {
98
		return property.size(source);
99
	}
100
101
	public boolean contains(Object o) {
102
		getterCalled();
103
		return property.contains(source, o);
104
	}
105
106
	public boolean containsAll(Collection c) {
107
		getterCalled();
108
		return property.containsAll(source, c);
109
	}
110
111
	public Object get(int index) {
112
		getterCalled();
113
		return property.get(source, index);
114
	}
115
116
	public int indexOf(Object o) {
117
		getterCalled();
118
		return property.indexOf(source, o);
119
	}
120
121
	public boolean isEmpty() {
122
		getterCalled();
123
		return property.isEmpty(source);
124
	}
125
126
	public int lastIndexOf(Object o) {
127
		getterCalled();
128
		return property.lastIndexOf(source, o);
129
	}
130
131
	public Object[] toArray() {
132
		getterCalled();
133
		return property.toArray(source);
134
	}
135
136
	public Object[] toArray(Object[] a) {
137
		getterCalled();
138
		return property.toArray(source, a);
139
	}
140
141
	// Single change operations
142
143
	public boolean add(Object o) {
144
		checkRealm();
145
		return property.add(source, o);
146
	}
147
148
	public Iterator iterator() {
149
		getterCalled();
150
		return new Iterator() {
151
			int lastReturned = -1;
152
			int expectedModCount = modCount;
153
			ListIterator delegate = new ArrayList(property.getList(source))
154
					.listIterator();
155
156
			public boolean hasNext() {
157
				getterCalled();
158
				checkForComodification();
159
				return delegate.hasNext();
160
			}
161
162
			public Object next() {
163
				getterCalled();
164
				checkForComodification();
165
				Object next = delegate.next();
166
				lastReturned = delegate.previousIndex();
167
				return next;
168
			}
169
170
			public void remove() {
171
				checkRealm();
172
				checkForComodification();
173
				if (lastReturned == -1)
174
					throw new IllegalStateException();
175
176
				delegate.remove(); // stay in sync
177
178
				property.remove(source, lastReturned);
179
180
				lastReturned = -1;
181
				expectedModCount = modCount;
182
			}
183
184
			private void checkForComodification() {
185
				if (expectedModCount != modCount)
186
					throw new ConcurrentModificationException();
187
			}
188
		};
189
	}
190
191
	public Object move(int oldIndex, int newIndex) {
192
		getterCalled();
193
		return property.move(source, oldIndex, newIndex);
194
	}
195
196
	public boolean remove(Object o) {
197
		getterCalled();
198
		return property.remove(source, o);
199
	}
200
201
	public void add(int index, Object o) {
202
		getterCalled();
203
		property.add(source, index, o);
204
	}
205
206
	public ListIterator listIterator() {
207
		return listIterator(0);
208
	}
209
210
	public ListIterator listIterator(final int index) {
211
		getterCalled();
212
		return new ListIterator() {
213
			int lastReturned = -1;
214
			int expectedModCount = modCount;
215
			ListIterator delegate = new ArrayList(property.getList(source))
216
					.listIterator(index);
217
218
			public boolean hasNext() {
219
				getterCalled();
220
				checkForComodification();
221
				return delegate.hasNext();
222
			}
223
224
			public int nextIndex() {
225
				getterCalled();
226
				checkForComodification();
227
				return delegate.nextIndex();
228
			}
229
230
			public Object next() {
231
				getterCalled();
232
				checkForComodification();
233
				Object next = delegate.next();
234
				lastReturned = delegate.previousIndex();
235
				return next;
236
			}
237
238
			public boolean hasPrevious() {
239
				getterCalled();
240
				checkForComodification();
241
				return delegate.hasPrevious();
242
			}
243
244
			public int previousIndex() {
245
				getterCalled();
246
				checkForComodification();
247
				return delegate.previousIndex();
248
			}
249
250
			public Object previous() {
251
				getterCalled();
252
				checkForComodification();
253
				Object previous = delegate.previous();
254
				lastReturned = delegate.nextIndex();
255
				return previous;
256
			}
257
258
			public void add(Object o) {
259
				checkRealm();
260
				checkForComodification();
261
				int index = delegate.nextIndex();
262
263
				delegate.add(o); // keep in sync
264
265
				property.add(source, index, o);
266
267
				lastReturned = -1;
268
				expectedModCount = modCount;
269
			}
270
271
			public void set(Object o) {
272
				checkRealm();
273
				checkForComodification();
274
275
				delegate.set(o);
276
277
				property.set(source, lastReturned, o);
278
279
				expectedModCount = modCount;
280
			}
281
282
			public void remove() {
283
				checkRealm();
284
				checkForComodification();
285
				if (lastReturned == -1)
286
					throw new IllegalStateException();
287
288
				delegate.remove(); // keep in sync
289
290
				property.remove(source, lastReturned);
291
292
				lastReturned = -1;
293
				expectedModCount = modCount;
294
			}
295
296
			private void checkForComodification() {
297
				if (expectedModCount != modCount)
298
					throw new ConcurrentModificationException();
299
			}
300
301
		};
302
	}
303
304
	public Object remove(int index) {
305
		getterCalled();
306
		return property.remove(source, index);
307
	}
308
309
	public Object set(int index, Object o) {
310
		getterCalled();
311
		return property.set(source, index, o);
312
	}
313
314
	public List subList(int fromIndex, int toIndex) {
315
		getterCalled();
316
		return Collections.unmodifiableList(property.getList(source).subList(
317
				fromIndex, toIndex));
318
	}
319
320
	// Bulk change operations
321
322
	public boolean addAll(Collection c) {
323
		getterCalled();
324
		return property.addAll(source, c);
325
	}
326
327
	public boolean addAll(int index, Collection c) {
328
		getterCalled();
329
		return property.addAll(source, index, c);
330
	}
331
332
	public boolean removeAll(Collection c) {
333
		getterCalled();
334
		return property.removeAll(source, c);
335
	}
336
337
	public boolean retainAll(Collection c) {
338
		getterCalled();
339
		return property.retainAll(source, c);
340
	}
341
342
	public void clear() {
343
		getterCalled();
344
		property.clear(source);
345
	}
346
347
	public boolean equals(Object o) {
348
		getterCalled();
349
		return property.equals(source, o);
350
	}
351
352
	public int hashCode() {
353
		getterCalled();
354
		return property.hashCode(source);
355
	}
356
357
	public Object getObserved() {
358
		return source;
359
	}
360
361
	public IProperty getProperty() {
362
		return property;
363
	}
364
365
	public synchronized void dispose() {
366
		if (!disposed) {
367
			disposed = true;
368
			property.removeListChangeListener(source, listener);
369
			property = null;
370
			source = null;
371
		}
372
		super.dispose();
373
	}
374
}
(-)src/org/eclipse/core/internal/databinding/property/PropertyUpdateHelper.java (+65 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property;
13
14
import java.util.HashMap;
15
import java.util.Map;
16
17
/**
18
 * @since 3.3
19
 * 
20
 */
21
public class PropertyUpdateHelper {
22
	private ThreadLocal localMap = new ThreadLocal() {
23
		protected Object initialValue() {
24
			return new HashMap();
25
		}
26
	};
27
28
	/**
29
	 * @param source
30
	 * @return blah
31
	 */
32
	public boolean isUpdating(Object source) {
33
		Map map = (Map) localMap.get();
34
		return map.containsKey(source);
35
	}
36
37
	/**
38
	 * @param source
39
	 * @param state
40
	 */
41
	public void setUpdating(Object source, boolean state) {
42
		Map map = (Map) localMap.get();
43
		Integer count = (Integer) map.get(source);
44
		int newCount = (count == null ? 0 : count.intValue())
45
				+ (state ? 1 : -1);
46
		if (newCount > 0)
47
			map.put(source, new Integer(newCount));
48
		else
49
			map.remove(source);
50
	}
51
52
	/**
53
	 * 
54
	 */
55
	public void dispose() {
56
		if (localMap != null) {
57
			Map map = (Map) localMap.get();
58
			if (map != null) {
59
				map.clear();
60
				localMap.set(null);
61
			}
62
			localMap = null;
63
		}
64
	}
65
}
(-)src/org/eclipse/core/internal/databinding/property/ListValuePropertyObservableList.java (+379 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property;
13
14
import java.lang.reflect.Array;
15
import java.util.ArrayList;
16
import java.util.Collection;
17
import java.util.Iterator;
18
import java.util.List;
19
import java.util.ListIterator;
20
21
import org.eclipse.core.databinding.observable.Diffs;
22
import org.eclipse.core.databinding.observable.IObserving;
23
import org.eclipse.core.databinding.observable.ObservableTracker;
24
import org.eclipse.core.databinding.observable.list.AbstractObservableList;
25
import org.eclipse.core.databinding.observable.list.IListChangeListener;
26
import org.eclipse.core.databinding.observable.list.IObservableList;
27
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
28
import org.eclipse.core.databinding.observable.list.ListDiff;
29
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
30
import org.eclipse.core.databinding.observable.set.IObservableSet;
31
import org.eclipse.core.databinding.observable.set.ISetChangeListener;
32
import org.eclipse.core.databinding.observable.set.SetChangeEvent;
33
import org.eclipse.core.databinding.observable.set.WritableSet;
34
import org.eclipse.core.databinding.property.IValueProperty;
35
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
36
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
37
import org.eclipse.core.internal.databinding.Util;
38
39
/**
40
 * @since 3.3
41
 * 
42
 */
43
public class ListValuePropertyObservableList extends AbstractObservableList
44
		implements IObserving {
45
	private IObservableList masterList;
46
	private IValueProperty detailProperty;
47
48
	private IObservableSet knownMasterElements;
49
50
	private boolean disposed = false;
51
52
	private IListChangeListener masterListener = new IListChangeListener() {
53
		public void handleListChange(ListChangeEvent event) {
54
			ListDiffEntry[] masterEntries = event.diff.getDifferences();
55
			ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length];
56
			for (int i = 0; i < masterEntries.length; i++) {
57
				ListDiffEntry masterDifference = masterEntries[i];
58
				int index = masterDifference.getPosition();
59
				boolean addition = masterDifference.isAddition();
60
				Object masterElement = masterDifference.getElement();
61
				Object elementDetailValue = detailProperty
62
						.getValue(masterElement);
63
				detailEntries[i] = Diffs.createListDiffEntry(index, addition,
64
						elementDetailValue);
65
			}
66
67
			// Removes listeners from elements removed from list
68
			knownMasterElements.retainAll(masterList);
69
70
			// Adds listeners to elements added to list
71
			knownMasterElements.addAll(masterList);
72
73
			fireListChange(Diffs.createListDiff(detailEntries));
74
		}
75
	};
76
77
	private IValuePropertyChangeListener detailListener = new IValuePropertyChangeListener() {
78
		public void handleValuePropertyChange(
79
				final ValuePropertyChangeEvent event) {
80
			if (!disposed) {
81
				Object source = event.getSource();
82
				int[] indices = findIndices(source);
83
				Object oldValue = event.diff.getOldValue();
84
				Object newValue = event.diff.getNewValue();
85
				ListDiffEntry[] entries = new ListDiffEntry[indices.length * 2];
86
				for (int i = 0; i < indices.length; i++) {
87
					int index = indices[i];
88
					entries[i * 2] = Diffs.createListDiffEntry(index, false,
89
							oldValue);
90
					entries[i * 2 + 1] = Diffs.createListDiffEntry(index, true,
91
							newValue);
92
				}
93
94
				ListDiff diff = Diffs.createListDiff(entries);
95
				fireListChange(diff);
96
			}
97
		}
98
99
		private int[] findIndices(Object masterElement) {
100
			List indices = new ArrayList();
101
102
			for (ListIterator it = masterList.listIterator(); it.hasNext();) {
103
				if (Util.equals(masterElement, it.next()))
104
					indices.add(new Integer(it.previousIndex()));
105
			}
106
107
			int[] result = new int[indices.size()];
108
			for (int i = 0; i < result.length; i++) {
109
				result[i] = ((Integer) indices.get(i)).intValue();
110
			}
111
			return result;
112
		}
113
	};
114
115
	/**
116
	 * @param masterList
117
	 * @param valueProperty
118
	 */
119
	public ListValuePropertyObservableList(IObservableList masterList,
120
			IValueProperty valueProperty) {
121
		super(masterList.getRealm());
122
		this.masterList = masterList;
123
		this.detailProperty = valueProperty;
124
	}
125
126
	protected void firstListenerAdded() {
127
		knownMasterElements = new WritableSet(getRealm());
128
		knownMasterElements.addSetChangeListener(new ISetChangeListener() {
129
			public void handleSetChange(SetChangeEvent event) {
130
				for (Iterator it = event.diff.getRemovals().iterator(); it
131
						.hasNext();) {
132
					detailProperty.addValueChangeListener(it.next(),
133
							detailListener);
134
				}
135
				for (Iterator it = event.diff.getAdditions().iterator(); it
136
						.hasNext();) {
137
					detailProperty.removeValueChangeListener(it.next(),
138
							detailListener);
139
				}
140
			}
141
		});
142
		knownMasterElements.addAll(masterList);
143
		masterList.addListChangeListener(masterListener);
144
	}
145
146
	protected void lastListenerRemoved() {
147
		masterList.removeListChangeListener(masterListener);
148
		if (knownMasterElements != null) {
149
			knownMasterElements.clear();
150
			knownMasterElements.dispose();
151
			knownMasterElements = null;
152
		}
153
	}
154
155
	protected int doGetSize() {
156
		getterCalled();
157
		return masterList.size();
158
	}
159
160
	private void getterCalled() {
161
		ObservableTracker.getterCalled(this);
162
	}
163
164
	public Object getElementType() {
165
		return detailProperty.getValueType();
166
	}
167
168
	public Object get(int index) {
169
		getterCalled();
170
		Object masterElement = masterList.get(index);
171
		return detailProperty.getValue(masterElement);
172
	}
173
174
	public boolean add(Object o) {
175
		throw new UnsupportedOperationException();
176
	}
177
178
	public boolean addAll(Collection c) {
179
		throw new UnsupportedOperationException();
180
	}
181
182
	public boolean addAll(int index, Collection c) {
183
		throw new UnsupportedOperationException();
184
	}
185
186
	public boolean contains(Object o) {
187
		getterCalled();
188
189
		Iterator it;
190
		if (knownMasterElements != null)
191
			it = knownMasterElements.iterator();
192
		else
193
			it = masterList.iterator();
194
		for (; it.hasNext();) {
195
			if (Util.equals(detailProperty.getValue(it.next()), o))
196
				return true;
197
		}
198
		return false;
199
	}
200
201
	public boolean isEmpty() {
202
		getterCalled();
203
		return masterList.isEmpty();
204
	}
205
206
	public boolean isStale() {
207
		getterCalled();
208
		return masterList.isStale();
209
	}
210
211
	public Iterator iterator() {
212
		getterCalled();
213
		return new Iterator() {
214
			Iterator it = masterList.iterator();
215
216
			public boolean hasNext() {
217
				getterCalled();
218
				return it.hasNext();
219
			}
220
221
			public Object next() {
222
				getterCalled();
223
				Object masterElement = it.next();
224
				return detailProperty.getValue(masterElement);
225
			}
226
227
			public void remove() {
228
				throw new UnsupportedOperationException();
229
			}
230
		};
231
	}
232
233
	public Object move(int oldIndex, int newIndex) {
234
		throw new UnsupportedOperationException();
235
	}
236
237
	public boolean remove(Object o) {
238
		throw new UnsupportedOperationException();
239
	}
240
241
	public boolean removeAll(Collection c) {
242
		throw new UnsupportedOperationException();
243
	}
244
245
	public boolean retainAll(Collection c) {
246
		throw new UnsupportedOperationException();
247
	}
248
249
	public Object[] toArray() {
250
		getterCalled();
251
		Object[] masterElements = masterList.toArray();
252
		Object[] result = new Object[masterElements.length];
253
		for (int i = 0; i < result.length; i++) {
254
			result[i] = detailProperty.getValue(masterElements[i]);
255
		}
256
		return result;
257
	}
258
259
	public Object[] toArray(Object[] a) {
260
		getterCalled();
261
		Object[] masterElements = masterList.toArray();
262
		if (a.length < masterElements.length)
263
			a = (Object[]) Array.newInstance(a.getClass().getComponentType(),
264
					masterElements.length);
265
		for (int i = 0; i < masterElements.length; i++) {
266
			a[i] = detailProperty.getValue(masterElements[i]);
267
		}
268
		return a;
269
	}
270
271
	public void add(int index, Object o) {
272
		throw new UnsupportedOperationException();
273
	}
274
275
	public void clear() {
276
		throw new UnsupportedOperationException();
277
	}
278
279
	public ListIterator listIterator() {
280
		return listIterator(0);
281
	}
282
283
	public ListIterator listIterator(final int index) {
284
		getterCalled();
285
		return new ListIterator() {
286
			ListIterator it = masterList.listIterator(index);
287
			Object lastReturned;
288
			boolean haveIterated = false;
289
290
			public void add(Object arg0) {
291
				throw new UnsupportedOperationException();
292
			}
293
294
			public boolean hasNext() {
295
				getterCalled();
296
				return it.hasNext();
297
			}
298
299
			public boolean hasPrevious() {
300
				getterCalled();
301
				return it.hasPrevious();
302
			}
303
304
			public Object next() {
305
				getterCalled();
306
				Object result = lastReturned = detailProperty.getValue(it
307
						.next());
308
				haveIterated = true;
309
				return result;
310
			}
311
312
			public int nextIndex() {
313
				getterCalled();
314
				return it.nextIndex();
315
			}
316
317
			public Object previous() {
318
				getterCalled();
319
				Object result = lastReturned = detailProperty.getValue(it
320
						.previous());
321
				haveIterated = true;
322
				return result;
323
			}
324
325
			public int previousIndex() {
326
				getterCalled();
327
				return it.previousIndex();
328
			}
329
330
			public void remove() {
331
				throw new UnsupportedOperationException();
332
			}
333
334
			public void set(Object o) {
335
				checkRealm();
336
				if (!haveIterated)
337
					throw new IllegalStateException();
338
				// if listeners are registered, the value property listener will
339
				// cause the list change event to fire. otherwise it doesn't
340
				// matter because noone is listening
341
				detailProperty.setValue(lastReturned, o);
342
			}
343
		};
344
	}
345
346
	public Object remove(int index) {
347
		throw new UnsupportedOperationException();
348
	}
349
350
	public Object set(int index, Object o) {
351
		checkRealm();
352
		Object masterElement = masterList.get(index);
353
		Object oldValue = detailProperty.getValue(masterElement);
354
		detailProperty.setValue(masterElement, o);
355
		return oldValue;
356
	}
357
358
	public Object getObserved() {
359
		return masterList;
360
	}
361
362
	public synchronized void dispose() {
363
		if (masterList != null) {
364
			masterList.removeListChangeListener(masterListener);
365
			masterList = null;
366
			masterListener = null;
367
		}
368
		if (knownMasterElements != null) {
369
			knownMasterElements.clear();
370
			knownMasterElements.dispose();
371
			knownMasterElements = null;
372
			detailListener = null;
373
		}
374
		detailProperty = null;
375
		disposed = true;
376
377
		super.dispose();
378
	}
379
}
(-)src/org/eclipse/core/internal/databinding/property/PropertyObservableSet.java (+197 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property;
13
14
import java.util.Collection;
15
import java.util.ConcurrentModificationException;
16
import java.util.HashSet;
17
import java.util.Iterator;
18
import java.util.Set;
19
20
import org.eclipse.core.databinding.observable.Realm;
21
import org.eclipse.core.databinding.observable.set.AbstractObservableSet;
22
import org.eclipse.core.databinding.property.IProperty;
23
import org.eclipse.core.databinding.property.IPropertyObservable;
24
import org.eclipse.core.databinding.property.ISetProperty;
25
import org.eclipse.core.databinding.property.ISetPropertyChangeListener;
26
import org.eclipse.core.databinding.property.SetPropertyChangeEvent;
27
28
/**
29
 * @since 3.3
30
 * 
31
 */
32
public class PropertyObservableSet extends AbstractObservableSet implements
33
		IPropertyObservable {
34
	private Object source;
35
	private ISetProperty property;
36
37
	private boolean updating = false;
38
39
	private boolean disposed = false;
40
41
	private transient volatile int modCount = 0;
42
43
	private ISetPropertyChangeListener listener = new ISetPropertyChangeListener() {
44
		public void handleSetPropertyChange(final SetPropertyChangeEvent event) {
45
			if (!disposed && !updating) {
46
				getRealm().exec(new Runnable() {
47
					public void run() {
48
						getRealm().exec(new Runnable() {
49
							public void run() {
50
								modCount++;
51
								fireSetChange(event.diff);
52
							}
53
						});
54
					}
55
				});
56
			}
57
		}
58
	};
59
60
	/**
61
	 * @param realm
62
	 * @param source
63
	 * @param property
64
	 */
65
	public PropertyObservableSet(Realm realm, Object source,
66
			ISetProperty property) {
67
		super(realm);
68
		this.source = source;
69
		this.property = property;
70
	}
71
72
	protected void firstListenerAdded() {
73
		if (!disposed) {
74
			property.addSetChangeListener(source, listener);
75
		}
76
	}
77
78
	protected void lastListenerRemoved() {
79
		if (!disposed) {
80
			property.removeSetChangeListener(source, listener);
81
		}
82
	}
83
84
	protected Set getWrappedSet() {
85
		return property.getSet(source);
86
	}
87
88
	public Object getElementType() {
89
		return property.getElementType();
90
	}
91
92
	// Queries
93
94
	protected int doGetSize() {
95
		return property.size(source);
96
	}
97
98
	// Single change operations
99
100
	public boolean add(Object o) {
101
		checkRealm();
102
		return property.add(source, o);
103
	}
104
105
	public Iterator iterator() {
106
		getterCalled();
107
		return new Iterator() {
108
			int expectedModCount = modCount;
109
			Iterator delegate = new HashSet(property.getSet(source)).iterator();
110
			Object last = null;
111
112
			public boolean hasNext() {
113
				getterCalled();
114
				checkForComodification();
115
				return delegate.hasNext();
116
			}
117
118
			public Object next() {
119
				getterCalled();
120
				checkForComodification();
121
				Object next = delegate.next();
122
				last = next;
123
				return next;
124
			}
125
126
			public void remove() {
127
				checkRealm();
128
				checkForComodification();
129
130
				delegate.remove(); // stay in sync
131
132
				property.remove(source, last);
133
134
				last = null;
135
				expectedModCount = modCount;
136
			}
137
138
			private void checkForComodification() {
139
				if (expectedModCount != modCount)
140
					throw new ConcurrentModificationException();
141
			}
142
		};
143
	}
144
145
	public boolean remove(Object o) {
146
		getterCalled();
147
		return property.remove(source, o);
148
	}
149
150
	// Bulk change operations
151
152
	public boolean addAll(Collection c) {
153
		getterCalled();
154
		return property.addAll(source, c);
155
	}
156
157
	public boolean removeAll(Collection c) {
158
		getterCalled();
159
		return property.removeAll(source, c);
160
	}
161
162
	public boolean retainAll(Collection c) {
163
		getterCalled();
164
		return property.retainAll(source, c);
165
	}
166
167
	public void clear() {
168
		getterCalled();
169
		property.clear(source);
170
	}
171
172
	public boolean equals(Object o) {
173
		return property.equals(source, o);
174
	}
175
176
	public int hashCode() {
177
		return property.hashCode(source);
178
	}
179
180
	public Object getObserved() {
181
		return source;
182
	}
183
184
	public IProperty getProperty() {
185
		return property;
186
	}
187
188
	public synchronized void dispose() {
189
		if (!disposed) {
190
			disposed = true;
191
			property.removeSetChangeListener(source, listener);
192
			property = null;
193
			source = null;
194
		}
195
		super.dispose();
196
	}
197
}
(-)src/org/eclipse/core/databinding/property/IPropertyChangeListener.java (+23 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
/**
15
 * Marker interface for all listener types in the properties framework.
16
 * 
17
 * @noimplement This interface is not intended to be implemented by clients.
18
 * 
19
 * @since 1.2
20
 */
21
public interface IPropertyChangeListener {
22
23
}
(-)src/org/eclipse/core/databinding/property/CollectionProperty.java (+56 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import java.util.Collection;
15
16
/**
17
 * Abstract implementation of ICollectionProperty.
18
 * 
19
 * @since 1.2
20
 */
21
public abstract class CollectionProperty extends Property implements
22
		ICollectionProperty {
23
	abstract Collection getCollection(Object source);
24
25
	public boolean contains(Object source, Object o) {
26
		return getCollection(source).contains(o);
27
	}
28
29
	public boolean containsAll(Object source, Collection c) {
30
		return getCollection(source).containsAll(c);
31
	}
32
33
	public boolean equals(Object source, Object o) {
34
		return getCollection(source).equals(o);
35
	}
36
37
	public int hashCode(Object source) {
38
		return getCollection(source).hashCode();
39
	}
40
41
	public boolean isEmpty(Object source) {
42
		return getCollection(source).isEmpty();
43
	}
44
45
	public int size(Object source) {
46
		return getCollection(source).size();
47
	}
48
49
	public Object[] toArray(Object source, Object[] array) {
50
		return getCollection(source).toArray(array);
51
	}
52
53
	public Object[] toArray(Object source) {
54
		return getCollection(source).toArray();
55
	}
56
}
(-)src/org/eclipse/core/internal/databinding/property/SetValuePropertyObservableMap.java (+92 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property;
13
14
import org.eclipse.core.databinding.observable.Diffs;
15
import org.eclipse.core.databinding.observable.IObserving;
16
import org.eclipse.core.databinding.observable.map.ComputedObservableMap;
17
import org.eclipse.core.databinding.observable.set.IObservableSet;
18
import org.eclipse.core.databinding.property.IValueProperty;
19
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
20
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
21
22
/**
23
 * @since 3.3
24
 * 
25
 */
26
public class SetValuePropertyObservableMap extends ComputedObservableMap
27
		implements IObserving {
28
	private IValueProperty property;
29
30
	private boolean disposed = false;
31
32
	private IValuePropertyChangeListener listener = new IValuePropertyChangeListener() {
33
		public void handleValuePropertyChange(
34
				final ValuePropertyChangeEvent event) {
35
			if (!disposed) {
36
				getRealm().exec(new Runnable() {
37
					public void run() {
38
						Object key = event.getSource();
39
						Object oldValue = event.diff.getOldValue();
40
						Object newValue = event.diff.getNewValue();
41
						fireMapChange(Diffs.createMapDiffSingleChange(key,
42
								oldValue, newValue));
43
					}
44
				});
45
			}
46
		}
47
	};
48
49
	/**
50
	 * @param keySet
51
	 * @param valueProperty
52
	 */
53
	public SetValuePropertyObservableMap(IObservableSet keySet,
54
			IValueProperty valueProperty) {
55
		super(keySet);
56
		this.property = valueProperty;
57
	}
58
59
	protected Object doGet(Object key) {
60
		return property.getValue(key);
61
	}
62
63
	protected Object doPut(Object key, Object value) {
64
		Object result = property.getValue(key);
65
		property.setValue(key, value);
66
		return result;
67
	}
68
69
	protected void hookListener(Object addedKey) {
70
		if (!disposed)
71
			property.addValueChangeListener(addedKey, listener);
72
	}
73
74
	protected void unhookListener(Object removedKey) {
75
		if (!disposed)
76
			property.removeValueChangeListener(removedKey, listener);
77
	}
78
79
	public Object getObserved() {
80
		return keySet();
81
	}
82
83
	public synchronized void dispose() {
84
		super.dispose();
85
86
		if (!disposed) {
87
			disposed = true;
88
			property = null;
89
			listener = null;
90
		}
91
	}
92
}
(-)src/org/eclipse/core/databinding/property/BasicValueProperty.java (+93 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
import org.eclipse.core.databinding.observable.Diffs;
15
import org.eclipse.core.databinding.observable.value.ValueDiff;
16
import org.eclipse.core.internal.databinding.Util;
17
import org.eclipse.core.internal.databinding.property.PropertyUpdateHelper;
18
19
/**
20
 * Abstract IValueProperty implementation.
21
 * <p>
22
 * This class automatically fires value change events when changes are initiated
23
 * through a call to {@link #setValue(Object, Object)}.
24
 * 
25
 * @since 1.2
26
 */
27
public abstract class BasicValueProperty extends ValueProperty {
28
	private PropertyUpdateHelper updateHelper = new PropertyUpdateHelper();
29
30
	/**
31
	 * Returns whether this property is currently being updated on the source.
32
	 * Implementors should query this value to avoid unnecessary calculations,
33
	 * such as computing a diff.
34
	 * 
35
	 * @param source
36
	 *            the property source
37
	 * @return whether this property is currently being updated on the source.
38
	 */
39
	protected boolean isUpdating(Object source) {
40
		return updateHelper.isUpdating(source);
41
	}
42
43
	/**
44
	 * Sets the value property on the source to the specified value, then fires
45
	 * a value change.
46
	 * 
47
	 * @param source
48
	 *            the property source
49
	 * @param value
50
	 *            the new value
51
	 */
52
	public void setValue(Object source, Object value) {
53
		Object oldValue = getValue(source);
54
55
		updateHelper.setUpdating(source, true);
56
		try {
57
			doSetValue(source, value);
58
		} finally {
59
			updateHelper.setUpdating(source, false);
60
		}
61
62
		if (hasListeners(source)) {
63
			Object newValue = getValue(source);
64
			if (!Util.equals(oldValue, newValue)) {
65
				fireValueChange(source, Diffs.createValueDiff(oldValue,
66
						newValue));
67
			}
68
		}
69
	}
70
71
	protected void fireValueChange(Object source, ValueDiff diff) {
72
		if (!isUpdating(source))
73
			super.fireValueChange(source, diff);
74
	}
75
76
	/**
77
	 * Sets the value property on the source to the specified list.
78
	 * 
79
	 * @param source
80
	 *            the property source
81
	 * @param value
82
	 *            the new value
83
	 */
84
	protected abstract void doSetValue(Object source, Object value);
85
86
	public synchronized void dispose() {
87
		if (updateHelper != null) {
88
			updateHelper.dispose();
89
			updateHelper = null;
90
		}
91
		super.dispose();
92
	}
93
}
(-)src/org/eclipse/core/internal/databinding/property/PropertyObservableValue.java (+115 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding.property;
13
14
import org.eclipse.core.databinding.observable.Diffs;
15
import org.eclipse.core.databinding.observable.Realm;
16
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
17
import org.eclipse.core.databinding.property.IProperty;
18
import org.eclipse.core.databinding.property.IPropertyObservable;
19
import org.eclipse.core.databinding.property.IValueProperty;
20
import org.eclipse.core.databinding.property.IValuePropertyChangeListener;
21
import org.eclipse.core.databinding.property.ValuePropertyChangeEvent;
22
import org.eclipse.core.internal.databinding.Util;
23
24
/**
25
 * @since 1.2
26
 * 
27
 */
28
public class PropertyObservableValue extends AbstractObservableValue implements
29
		IPropertyObservable {
30
	private Object source;
31
	private IValueProperty property;
32
33
	private boolean updating = false;
34
35
	private boolean disposed = false;
36
37
	private IValuePropertyChangeListener listener = new IValuePropertyChangeListener() {
38
		public void handleValuePropertyChange(
39
				final ValuePropertyChangeEvent event) {
40
			if (!disposed && !updating) {
41
				getRealm().exec(new Runnable() {
42
					public void run() {
43
						fireValueChange(event.diff);
44
					}
45
				});
46
			}
47
		}
48
	};
49
50
	/**
51
	 * @param realm
52
	 * @param source
53
	 * @param property
54
	 */
55
	public PropertyObservableValue(Realm realm, Object source,
56
			IValueProperty property) {
57
		super(realm);
58
		this.source = source;
59
		this.property = property;
60
	}
61
62
	protected void firstListenerAdded() {
63
		if (!disposed) {
64
			property.addValueChangeListener(source, listener);
65
		}
66
	}
67
68
	protected void lastListenerRemoved() {
69
		if (!disposed) {
70
			property.removeValueChangeListener(source, listener);
71
		}
72
	}
73
74
	protected Object doGetValue() {
75
		return property.getValue(source);
76
	}
77
78
	protected void doSetValue(Object value) {
79
		Object oldValue = doGetValue();
80
81
		updating = true;
82
		try {
83
			property.setValue(source, value);
84
		} finally {
85
			updating = false;
86
		}
87
88
		Object newValue = doGetValue();
89
		if (!Util.equals(oldValue, newValue)) {
90
			fireValueChange(Diffs.createValueDiff(oldValue, newValue));
91
		}
92
	}
93
94
	public Object getValueType() {
95
		return property.getValueType();
96
	}
97
98
	public Object getObserved() {
99
		return source;
100
	}
101
102
	public IProperty getProperty() {
103
		return property;
104
	}
105
106
	public synchronized void dispose() {
107
		if (!disposed) {
108
			disposed = true;
109
			property.removeValueChangeListener(source, listener);
110
			property = null;
111
			source = null;
112
		}
113
		super.dispose();
114
	}
115
}
(-)src/org/eclipse/core/databinding/property/IListPropertyChangeListener.java (+27 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
/**
15
 * Listener for changes to list properties on a property source
16
 * 
17
 * @since 1.2
18
 */
19
public interface IListPropertyChangeListener extends IPropertyChangeListener {
20
	/**
21
	 * Handle a change to a list property on a specific property source.
22
	 * 
23
	 * @param event
24
	 *            an event describing the list change that occured.
25
	 */
26
	public void handleListPropertyChange(ListPropertyChangeEvent event);
27
}
(-)src/org/eclipse/core/databinding/property/Property.java (+78 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 194734)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property;
13
14
/**
15
 * Abstract implementation of IProperty
16
 * 
17
 * @since 1.2
18
 */
19
public abstract class Property implements IProperty {
20
	private PropertyChangeSupport changeSupport;
21
22
	synchronized PropertyChangeSupport getChangeSupport() {
23
		if (changeSupport == null)
24
			changeSupport = new PropertyChangeSupport(this);
25
		return changeSupport;
26
	}
27
28
	/**
29
	 * Adds a listener on the given property source for changes to the property.
30
	 * The listener should fire a property change event whenever the a property
31
	 * change is observed. This method is called when the first property change
32
	 * listener is added for the given source object.
33
	 * <p>
34
	 * This method does nothing if the source object has no listener support for
35
	 * the property.
36
	 * 
37
	 * @param source
38
	 *            the source object to observe for property changes.
39
	 * @see #removeListenerFrom(Object)
40
	 * @noreference This method is not intended to be referenced by clients.
41
	 */
42
	protected abstract void addListenerTo(Object source);
43
44
	/**
45
	 * Remove the listeners previous added on the property source for changes to
46
	 * the property. This method is called when the last property change
47
	 * listener is removed for the given source object.
48
	 * <p>
49
	 * This method does nothing if the source object has no listener support for
50
	 * the property.
51
	 * 
52
	 * @param source
53
	 *            the source object to stop observing for property changes.
54
	 * @see #addListenerTo(Object)
55
	 * @noreference This method is not intended to be referenced by clients.
56
	 */
57
	protected abstract void removeListenerFrom(Object source);
58
59
	/**
60
	 * Returns whether this property has listeners registered for the given
61
	 * property source
62
	 * 
63
	 * @param source
64
	 *            the property source
65
	 * @return whether this property has listeners registered for the given
66
	 *         property source
67
	 */
68
	protected boolean hasListeners(Object source) {
69
		return changeSupport != null && changeSupport.hasListeners(source);
70
	}
71
72
	public synchronized void dispose() {
73
		if (changeSupport != null) {
74
			changeSupport.dispose();
75
			changeSupport = null;
76
		}
77
	}
78
}

Return to bug 194734