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

(-)src/org/eclipse/core/databinding/observable/map/MapDiff.java (+34 lines)
Lines 7-16 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Matthew Hall - bug 194734
10
 *******************************************************************************/
11
 *******************************************************************************/
11
12
12
package org.eclipse.core.databinding.observable.map;
13
package org.eclipse.core.databinding.observable.map;
13
14
15
import java.util.Iterator;
16
import java.util.Map;
14
import java.util.Set;
17
import java.util.Set;
15
18
16
/**
19
/**
Lines 18-23 Link Here
18
 * 
21
 * 
19
 */
22
 */
20
public abstract class MapDiff {
23
public abstract class MapDiff {
24
	/**
25
	 * Returns true if the diff has no added, removed or changed entries.
26
	 * 
27
	 * @return true if the diff has no added, removed or changed entries.
28
	 * @since 1.2
29
	 */
30
	public boolean isEmpty() {
31
		return getAddedKeys().isEmpty() && getRemovedKeys().isEmpty()
32
				&& getChangedKeys().isEmpty();
33
	}
34
35
	/**
36
	 * Applies the changes in this diff to the given map
37
	 * 
38
	 * @param map
39
	 *            the map to which the diff will be applied
40
	 * @since 1.2
41
	 */
42
	public void applyTo(Map map) {
43
		for (Iterator it = getAddedKeys().iterator(); it.hasNext();) {
44
			Object key = it.next();
45
			map.put(key, getNewValue(key));
46
		}
47
		for (Iterator it = getChangedKeys().iterator(); it.hasNext();) {
48
			Object key = it.next();
49
			map.put(key, getNewValue(key));
50
		}
51
		for (Iterator it = getRemovedKeys().iterator(); it.hasNext();) {
52
			map.remove(it.next());
53
		}
54
	}
21
55
22
	/**
56
	/**
23
	 * @return the set of keys which were added
57
	 * @return the set of keys which were added
(-)META-INF/MANIFEST.MF (+6 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.list,
19
 org.eclipse.core.databinding.property.map,
20
 org.eclipse.core.databinding.property.set,
21
 org.eclipse.core.databinding.property.value,
17
 org.eclipse.core.databinding.util,
22
 org.eclipse.core.databinding.util,
18
 org.eclipse.core.databinding.validation;x-internal:=false,
23
 org.eclipse.core.databinding.validation;x-internal:=false,
19
 org.eclipse.core.internal.databinding;x-friends:="org.eclipse.core.databinding.beans",
24
 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,
26
 org.eclipse.core.internal.databinding.observable;x-internal:=true,
22
 org.eclipse.core.internal.databinding.observable.masterdetail;x-friends:="org.eclipse.jface.tests.databinding",
27
 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",
28
 org.eclipse.core.internal.databinding.observable.tree;x-friends:="org.eclipse.jface.databinding,org.eclipse.jface.tests.databinding",
29
 org.eclipse.core.internal.databinding.property;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"
30
 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)"
31
Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)"
26
Import-Package-Comment: see http://wiki.eclipse.org/
32
Import-Package-Comment: see http://wiki.eclipse.org/
(-)src/org/eclipse/core/databinding/observable/list/ListDiff.java (-1 / +39 lines)
Lines 7-17 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Matthew Hall - bug 208858
10
 *     Matthew Hall - bug 208858, 194734
11
 *******************************************************************************/
11
 *******************************************************************************/
12
12
13
package org.eclipse.core.databinding.observable.list;
13
package org.eclipse.core.databinding.observable.list;
14
14
15
import java.util.List;
16
15
import org.eclipse.core.internal.databinding.Util;
17
import org.eclipse.core.internal.databinding.Util;
16
18
17
/**
19
/**
Lines 84-89 Link Here
84
	}
86
	}
85
87
86
	/**
88
	/**
89
	 * Returns true if the diff contains no added, removed, moved or replaced
90
	 * elements.
91
	 * 
92
	 * @return true if the diff contains no added, removed, moved or replaced
93
	 *         elements.
94
	 * @since 1.2
95
	 */
96
	public boolean isEmpty() {
97
		return getDifferences().length == 0;
98
	}
99
100
	/**
101
	 * Applies the changes in this diff to the given list
102
	 * 
103
	 * @param list
104
	 *            the list to which the diff will be applied
105
	 * @since 1.2
106
	 */
107
	public void applyTo(final List list) {
108
		accept(new ListDiffVisitor() {
109
			public void handleAdd(int index, Object element) {
110
				list.add(index, element);
111
			}
112
113
			public void handleRemove(int index, Object element) {
114
				list.remove(index);
115
			}
116
117
			public void handleReplace(int index, Object oldElement,
118
					Object newElement) {
119
				list.set(index, newElement);
120
			}
121
		});
122
	}
123
124
	/**
87
	 * @see java.lang.Object#toString()
125
	 * @see java.lang.Object#toString()
88
	 */
126
	 */
89
	public String toString() {
127
	public String toString() {
(-)src/org/eclipse/core/databinding/observable/set/SetDiff.java (+23 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Matthew Hall - bug 194734
10
 *******************************************************************************/
11
 *******************************************************************************/
11
12
12
package org.eclipse.core.databinding.observable.set;
13
package org.eclipse.core.databinding.observable.set;
Lines 30-35 Link Here
30
	public abstract Set getRemovals();
31
	public abstract Set getRemovals();
31
	
32
	
32
	/**
33
	/**
34
	 * Returns true if the diff has no added or removed elements.
35
	 * 
36
	 * @return true if the diff has no added or removed elements.
37
	 * @since 1.2
38
	 */
39
	public boolean isEmpty() {
40
		return getAdditions().isEmpty() && getRemovals().isEmpty();
41
	}
42
43
	/**
44
	 * Applies the changes in this diff to the given set
45
	 * 
46
	 * @param set
47
	 *            the set to which the diff will be applied
48
	 * @since 1.2
49
	 */
50
	public void applyTo(Set set) {
51
		set.addAll(getAdditions());
52
		set.removeAll(getRemovals());
53
	}
54
55
	/**
33
	 * @see java.lang.Object#toString()
56
	 * @see java.lang.Object#toString()
34
	 */
57
	 */
35
	public String toString() {
58
	public String toString() {
(-)src/org/eclipse/core/databinding/property/value/IValueProperty.java (+73 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.value;
13
14
import org.eclipse.core.databinding.property.IProperty;
15
16
17
/**
18
 * Interface for value-typed properties
19
 * 
20
 * @since 1.2
21
 * @noimplement This interface is not intended to be implemented by clients.
22
 */
23
public interface IValueProperty extends IProperty {
24
	/**
25
	 * Returns the source's value property
26
	 * 
27
	 * @param source
28
	 *            the property source
29
	 * @return the current value of the source's value property
30
	 */
31
	public Object getValue(Object source);
32
33
	/**
34
	 * Sets the source's value property to the specified value
35
	 * 
36
	 * @param source
37
	 *            the property source
38
	 * @param value
39
	 *            the new value
40
	 */
41
	public void setValue(Object source, Object value);
42
43
	/**
44
	 * Returns the value type of the property, or <code>null</code> if untyped.
45
	 * 
46
	 * @return the value type of the property, or <code>null</code> if untyped.
47
	 */
48
	public Object getValueType();
49
50
	/**
51
	 * Adds the given value property change listener to the list of listeners
52
	 * for the given source.
53
	 * 
54
	 * @param source
55
	 *            the property source
56
	 * @param listener
57
	 *            the listener
58
	 */
59
	public void addValueChangeListener(Object source,
60
			IValuePropertyChangeListener listener);
61
62
	/**
63
	 * Removes the given list property change listener from the list of
64
	 * listeners for the given source.
65
	 * 
66
	 * @param source
67
	 *            the property source
68
	 * @param listener
69
	 *            the listener
70
	 */
71
	public void removeValueChangeListener(Object source,
72
			IValuePropertyChangeListener listener);
73
}
(-)src/org/eclipse/core/databinding/property/set/ISetPropertyChangeListener.java (+29 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.set;
13
14
import org.eclipse.core.databinding.property.IPropertyChangeListener;
15
16
/**
17
 * Listener for changes to set properties on a property source
18
 * 
19
 * @since 1.2
20
 */
21
public interface ISetPropertyChangeListener extends IPropertyChangeListener {
22
	/**
23
	 * Handle a change to a set property on a specific property source.
24
	 * 
25
	 * @param event
26
	 *            an event describing the set change that occured.
27
	 */
28
	public void handleSetPropertyChange(SetPropertyChangeEvent event);
29
}
(-)src/org/eclipse/core/databinding/property/set/SimpleSetProperty.java (+235 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
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property.set;
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.databinding.property.SimpleProperty;
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 SimpleSetProperty extends SimpleProperty implements
34
		ISetProperty {
35
	public void addSetChangeListener(Object source,
36
			ISetPropertyChangeListener listener) {
37
		addPropertyChangeListener(source, listener);
38
	}
39
40
	public void removeSetChangeListener(Object source,
41
			ISetPropertyChangeListener listener) {
42
		removePropertyChangeListener(source, listener);
43
	}
44
45
	protected final Object getProperty(Object source) {
46
		return getSet(source);
47
	}
48
49
	public boolean contains(Object source, Object o) {
50
		return getSet(source).contains(o);
51
	}
52
53
	public boolean containsAll(Object source, Collection c) {
54
		return getSet(source).containsAll(c);
55
	}
56
57
	public boolean equals(Object source, Object o) {
58
		return getSet(source).equals(o);
59
	}
60
61
	public int hashCode(Object source) {
62
		return getSet(source).hashCode();
63
	}
64
65
	public boolean isEmpty(Object source) {
66
		return getSet(source).isEmpty();
67
	}
68
69
	public int size(Object source) {
70
		return getSet(source).size();
71
	}
72
73
	public Object[] toArray(Object source, Object[] array) {
74
		return getSet(source).toArray(array);
75
	}
76
77
	public Object[] toArray(Object source) {
78
		return getSet(source).toArray();
79
	}
80
81
	private void updateSetAndFireChange(Object source, Set set, SetDiff diff) {
82
		setUpdating(source, true);
83
		try {
84
			updateSet(source, set, diff);
85
		} finally {
86
			setUpdating(source, false);
87
		}
88
89
		if (hasListeners(source)) {
90
			fireSetChange(source, diff);
91
		}
92
	}
93
94
	/**
95
	 * Updates the property on the source with the specified change. A set
96
	 * change event is fired in the calling method, so implementers do not need
97
	 * to call {@link #fireSetChange(Object, SetDiff)}.
98
	 * 
99
	 * @param source
100
	 *            the property source
101
	 * @param set
102
	 *            the new set
103
	 * @param diff
104
	 *            a diff describing the change
105
	 */
106
	protected abstract void updateSet(Object source, Set set, SetDiff diff);
107
108
	/**
109
	 * Notifies that the source's property changed. Listeners registered in the
110
	 * {@link #addListenerTo(Object)} method should call this method when a
111
	 * property change is observed on the source object.
112
	 * 
113
	 * @param source
114
	 *            the property source
115
	 * @param diff
116
	 *            a diff describing the set change. If null, a diff will be
117
	 *            computed from the cached state and the current state.
118
	 */
119
	protected final void fireSetChange(Object source, SetDiff diff) {
120
		if (!isUpdating(source) && hasListeners(source)) {
121
			if (diff == null) {
122
				diff = Diffs.computeSetDiff((Set) getCached(source),
123
						getSet(source));
124
			}
125
			if (!diff.isEmpty()) {
126
				firePropertyChange(new SetPropertyChangeEvent(source, this,
127
						diff));
128
			}
129
		}
130
	}
131
132
	public boolean add(Object source, Object o) {
133
		Set set = getSet(source);
134
		if (!set.contains(o)) {
135
			set = new HashSet(set);
136
			boolean added = set.add(o);
137
			if (added) {
138
				updateSetAndFireChange(source, set, Diffs.createSetDiff(
139
						Collections.singleton(o), Collections.EMPTY_SET));
140
			}
141
			return added;
142
		}
143
		return false;
144
	}
145
146
	public boolean addAll(Object source, Collection c) {
147
		if (c.isEmpty())
148
			return false;
149
150
		Set set = getSet(source);
151
		Set additions = new HashSet();
152
		for (Iterator it = c.iterator(); it.hasNext();) {
153
			Object o = it.next();
154
			if (!set.contains(o)) {
155
				additions.add(o);
156
			}
157
		}
158
		boolean changed = !additions.isEmpty();
159
		if (changed) {
160
			set = new HashSet(set);
161
			set.addAll(additions);
162
163
			updateSetAndFireChange(source, set, Diffs.createSetDiff(additions,
164
					Collections.EMPTY_SET));
165
		}
166
		return changed;
167
	}
168
169
	public void clear(Object source) {
170
		if (!isEmpty(source)) {
171
			updateSetAndFireChange(source, new HashSet(), Diffs.createSetDiff(
172
					Collections.EMPTY_SET, getSet(source)));
173
		}
174
	}
175
176
	public boolean remove(Object source, Object o) {
177
		Set set = getSet(source);
178
		if (set.contains(o)) {
179
			set = new HashSet(set);
180
			boolean removed = set.remove(o);
181
			if (removed) {
182
				updateSetAndFireChange(source, set, Diffs.createSetDiff(
183
						Collections.EMPTY_SET, Collections.singleton(o)));
184
			}
185
			return removed;
186
		}
187
		return false;
188
	}
189
190
	public boolean removeAll(Object source, Collection c) {
191
		if (c.isEmpty())
192
			return false;
193
194
		Set set = new HashSet(getSet(source));
195
		Set removals = new HashSet();
196
		for (Iterator it = set.iterator(); it.hasNext();) {
197
			Object o = it.next();
198
			if (c.contains(o)) {
199
				removals.add(o);
200
				it.remove();
201
			}
202
		}
203
		boolean changed = !removals.isEmpty();
204
		if (changed) {
205
			updateSetAndFireChange(source, set, Diffs.createSetDiff(
206
					Collections.EMPTY_SET, removals));
207
		}
208
		return changed;
209
	}
210
211
	public boolean retainAll(Object source, Collection c) {
212
		if (isEmpty(source))
213
			return false;
214
		if (c.isEmpty()) {
215
			clear(source);
216
			return true;
217
		}
218
219
		Set set = new HashSet(getSet(source));
220
		Set removals = new HashSet();
221
		for (Iterator it = set.iterator(); it.hasNext();) {
222
			Object o = it.next();
223
			if (!c.contains(o)) {
224
				removals.add(o);
225
				it.remove();
226
			}
227
		}
228
		boolean changed = !removals.isEmpty();
229
		if (changed) {
230
			updateSetAndFireChange(source, set, Diffs.createSetDiff(
231
					Collections.EMPTY_SET, removals));
232
		}
233
		return changed;
234
	}
235
}
(-)src/org/eclipse/core/databinding/property/list/IListProperty.java (+165 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.list;
13
14
import java.util.Collection;
15
import java.util.List;
16
17
import org.eclipse.core.databinding.property.ICollectionProperty;
18
19
/**
20
 * Interface for list-typed properties.
21
 * 
22
 * @since 1.2
23
 * @noimplement This interface is not intended to be implemented by clients.
24
 */
25
public interface IListProperty extends ICollectionProperty {
26
	/**
27
	 * Returns a List with the current contents of the source's list property
28
	 * 
29
	 * @param source
30
	 *            the property source
31
	 * @return a List with the current contents of the source's list property
32
	 */
33
	List getList(Object source);
34
35
	/**
36
	 * Inserts all elements in the specified collection into the source's list
37
	 * property at the specified index.
38
	 * 
39
	 * @param source
40
	 *            the property source
41
	 * @param index
42
	 *            the insertion index
43
	 * @param c
44
	 *            the collection of elements to add
45
	 * @return whether the source's list property was changed
46
	 */
47
	boolean addAll(Object source, int index, Collection c);
48
49
	/**
50
	 * Returns the element at the specified position in the source's list
51
	 * property
52
	 * 
53
	 * @param source
54
	 *            the property source
55
	 * @param index
56
	 *            the element position
57
	 * @return the element at the given position in the source's list property
58
	 */
59
	Object get(Object source, int index);
60
61
	/**
62
	 * Replaces the element at the specified position in the source's list
63
	 * property with the given element.
64
	 * 
65
	 * @param source
66
	 *            the property source
67
	 * @param index
68
	 *            the element position
69
	 * @param element
70
	 *            the replacement element
71
	 * @return the element previously at the specified position in the source's
72
	 *         list property
73
	 */
74
	Object set(Object source, int index, Object element);
75
76
	/**
77
	 * Moves the element at the specified old position in the source's list
78
	 * property to the specified new position
79
	 * 
80
	 * @param source
81
	 *            the property source
82
	 * @param oldIndex
83
	 *            the old element position
84
	 * @param newIndex
85
	 *            the new element position
86
	 * @return the element that was moved
87
	 */
88
	Object move(Object source, int oldIndex, int newIndex);
89
90
	/**
91
	 * Inserts the element into the source's list property at the specified
92
	 * position
93
	 * 
94
	 * @param source
95
	 *            the property source
96
	 * @param index
97
	 *            the insertion index
98
	 * @param element
99
	 *            the element to insert
100
	 */
101
	void add(Object source, int index, Object element);
102
103
	/**
104
	 * Removes the element from the source's list property which is located at
105
	 * the specified position
106
	 * 
107
	 * @param source
108
	 *            the property source
109
	 * @param index
110
	 *            the index of the element to remove
111
	 * @return the element that was removed from the source's list property
112
	 */
113
	Object remove(Object source, int index);
114
115
	/**
116
	 * Returns the index of the first location of the given element in the
117
	 * source's list property, or -1 if the list does not contain the element.
118
	 * 
119
	 * @param source
120
	 *            the property source
121
	 * @param o
122
	 *            the element
123
	 * @return the index of the first location of the given element in the
124
	 *         source's list property, or -1 if the list does not contain the
125
	 *         element
126
	 */
127
	int indexOf(Object source, Object o);
128
129
	/**
130
	 * Returns the index of the last location of the given element in the
131
	 * source's list property, or -1 if the list does not contain the given
132
	 * element.
133
	 * 
134
	 * @param source
135
	 * @param o
136
	 * @return the index of the last location of the given element in the
137
	 *         source's list property, or -1 if the list does not contain the
138
	 *         element
139
	 */
140
	int lastIndexOf(Object source, Object o);
141
142
	/**
143
	 * Adds the given list property change listener to the list of listeners for
144
	 * the given source.
145
	 * 
146
	 * @param source
147
	 *            the property source
148
	 * @param listener
149
	 *            the listener
150
	 */
151
	public void addListChangeListener(Object source,
152
			IListPropertyChangeListener listener);
153
154
	/**
155
	 * Removes the given list property change listener from the list of
156
	 * listeners for the given source.
157
	 * 
158
	 * @param source
159
	 *            the property source
160
	 * @param listener
161
	 *            the listener
162
	 */
163
	public void removeListChangeListener(Object source,
164
			IListPropertyChangeListener listener);
165
}
(-)src/org/eclipse/core/internal/databinding/observable/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.observable;
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.IProperty;
23
import org.eclipse.core.databinding.property.IPropertyObservable;
24
import org.eclipse.core.databinding.property.map.IMapProperty;
25
import org.eclipse.core.databinding.property.map.IMapPropertyChangeListener;
26
import org.eclipse.core.databinding.property.map.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/internal/databinding/property/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;
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.value.IValueProperty;
20
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
21
import org.eclipse.core.databinding.property.value.ValueProperty;
22
import org.eclipse.core.databinding.property.value.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/observable/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.observable;
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.IProperty;
26
import org.eclipse.core.databinding.property.IPropertyObservable;
27
import org.eclipse.core.databinding.property.list.IListProperty;
28
import org.eclipse.core.databinding.property.list.IListPropertyChangeListener;
29
import org.eclipse.core.databinding.property.list.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/observable/masterdetail/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.observable.masterdetail;
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.value.IValueProperty;
32
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
33
import org.eclipse.core.databinding.property.value.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/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;
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.set.ISetProperty;
22
import org.eclipse.core.databinding.property.set.ISetPropertyChangeListener;
23
import org.eclipse.core.databinding.property.set.SetProperty;
24
import org.eclipse.core.databinding.property.set.SetPropertyChangeEvent;
25
import org.eclipse.core.databinding.property.value.IValueProperty;
26
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
27
import org.eclipse.core.databinding.property.value.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/databinding/property/Properties.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;
13
14
import org.eclipse.core.databinding.property.list.IListProperty;
15
import org.eclipse.core.databinding.property.map.IMapProperty;
16
import org.eclipse.core.databinding.property.set.ISetProperty;
17
import org.eclipse.core.databinding.property.value.IValueProperty;
18
import org.eclipse.core.internal.databinding.property.ListPropertyDetailValueList;
19
import org.eclipse.core.internal.databinding.property.MapPropertyDetailValueMap;
20
import org.eclipse.core.internal.databinding.property.SetPropertyDetailValueMap;
21
import org.eclipse.core.internal.databinding.property.ValuePropertyDetailList;
22
import org.eclipse.core.internal.databinding.property.ValuePropertyDetailMap;
23
import org.eclipse.core.internal.databinding.property.ValuePropertyDetailSet;
24
import org.eclipse.core.internal.databinding.property.ValuePropertyDetailValue;
25
26
/**
27
 * A factory for chaining properties together 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 Properties, 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 = Properties.detailValue(ab, bc)
49
 * assertTrue(abc.getValue(a) == c);
50
 * </pre>
51
 * 
52
 * @since 1.2
53
 */
54
public class Properties {
55
	// Properties of IValueProperty 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/databinding/property/value/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.value;
13
14
import org.eclipse.core.databinding.observable.value.ValueDiff;
15
import org.eclipse.core.databinding.property.Property;
16
17
/**
18
 * Abstract implementation of IValueProperty.
19
 * 
20
 * @since 1.2
21
 */
22
public abstract class ValueProperty extends Property implements IValueProperty {
23
	public final void addValueChangeListener(Object source,
24
			IValuePropertyChangeListener listener) {
25
		addPropertyChangeListener(source, listener);
26
	}
27
28
	public final void removeValueChangeListener(Object source,
29
			IValuePropertyChangeListener listener) {
30
		removePropertyChangeListener(source, listener);
31
	}
32
33
	/**
34
	 * Fires a ValuePropertyChangeEvent with the given source and diff
35
	 * 
36
	 * @param source
37
	 *            the property source
38
	 * @param diff
39
	 *            the value diff
40
	 */
41
	protected final void fireValueChange(Object source, ValueDiff diff) {
42
		firePropertyChange(new ValuePropertyChangeEvent(source, this, diff));
43
	}
44
}
(-)src/org/eclipse/core/databinding/property/map/IMapProperty.java (+179 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.map;
13
14
import java.util.Map;
15
16
import org.eclipse.core.databinding.property.IProperty;
17
18
/**
19
 * Interface for map-typed properties
20
 * 
21
 * @since 1.2
22
 * @noimplement This interface is not intended to be implemented by clients.
23
 */
24
public interface IMapProperty extends IProperty {
25
	/**
26
	 * Returns a Map with the current contents of the source's map property
27
	 * 
28
	 * @param source
29
	 *            the property source
30
	 * @return a Map with the current contents of the source's map property
31
	 */
32
	Map getMap(Object source);
33
34
	/**
35
	 * Returns the size of the source's map property
36
	 * 
37
	 * @param source
38
	 *            the property source
39
	 * @return the size of the source's map property
40
	 */
41
	int size(Object source);
42
43
	/**
44
	 * Returns whether the source's map property is empty
45
	 * 
46
	 * @param source
47
	 *            the property source
48
	 * @return whether the source's map property is empty
49
	 */
50
	boolean isEmpty(Object source);
51
52
	/**
53
	 * Returns whether the specified key is contained in the key set of the
54
	 * source's map property
55
	 * 
56
	 * @param source
57
	 *            the property source
58
	 * @param key
59
	 *            the key
60
	 * @return whether the specified key is contained in the key set of the
61
	 *         source's map property
62
	 */
63
	boolean containsKey(Object source, Object key);
64
65
	/**
66
	 * Returns whether the specified value is contains in the values collection
67
	 * of the source's map property
68
	 * 
69
	 * @param source
70
	 *            the property source
71
	 * @param value
72
	 *            the value
73
	 * @return whether the specified value is contains in the values collection
74
	 *         of the source's map property
75
	 */
76
	boolean containsValue(Object source, Object value);
77
78
	/**
79
	 * Returns the value associated with the specified key in the source's map
80
	 * property
81
	 * 
82
	 * @param source
83
	 *            the property source
84
	 * @param key
85
	 *            the key
86
	 * @return the value associated with the specified key in the source's map
87
	 *         property
88
	 */
89
	Object get(Object source, Object key);
90
91
	/**
92
	 * Associates the specified value with the specified key in the source's map
93
	 * property
94
	 * 
95
	 * @param source
96
	 *            the property source
97
	 * @param key
98
	 *            the key
99
	 * @param value
100
	 *            the value
101
	 * @return the value that was previously associated with the given key in
102
	 *         the source's map property
103
	 */
104
	Object put(Object source, Object key, Object value);
105
106
	/**
107
	 * Removes the mapping for the specified key from the source's map property
108
	 * 
109
	 * @param source
110
	 *            the property source
111
	 * @param key
112
	 *            the key
113
	 * @return the value that was previously associated with the specified key
114
	 *         in the source's map property, or null if no such mapping exists
115
	 */
116
	Object remove(Object source, Object key);
117
118
	/**
119
	 * Adds all mappings in the specified map to the source's map property.
120
	 * 
121
	 * @param source
122
	 *            the property source
123
	 * @param t
124
	 *            the map
125
	 */
126
	void putAll(Object source, Map t);
127
128
	/**
129
	 * Removes all mapping from the source's map property
130
	 * 
131
	 * @param source
132
	 *            the property source
133
	 */
134
	void clear(Object source);
135
136
	/**
137
	 * Returns whether the source's map property is equal to the argument
138
	 * 
139
	 * @param source
140
	 *            the property source
141
	 * @param o
142
	 *            the object to test for equality
143
	 * @return whether the source's map property is equal to the argument
144
	 */
145
	boolean equals(Object source, Object o);
146
147
	/**
148
	 * Returns the hash code of the source's map property
149
	 * 
150
	 * @param source
151
	 *            the property source
152
	 * @return the hash code of the source's map property
153
	 */
154
	int hashCode(Object source);
155
156
	/**
157
	 * Adds the given map property change listener to the list of listeners for
158
	 * the given source.
159
	 * 
160
	 * @param source
161
	 *            the property source
162
	 * @param listener
163
	 *            the listener
164
	 */
165
	public void addMapChangeListener(Object source,
166
			IMapPropertyChangeListener listener);
167
168
	/**
169
	 * Removes the given map property change listener from the list of listeners
170
	 * for the given source.
171
	 * 
172
	 * @param source
173
	 *            the property source
174
	 * @param listener
175
	 *            the listener
176
	 */
177
	public void removeMapChangeListener(Object source,
178
			IMapPropertyChangeListener listener);
179
}
(-)src/org/eclipse/core/internal/databinding/property/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;
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.map.IMapProperty;
20
import org.eclipse.core.databinding.property.map.IMapPropertyChangeListener;
21
import org.eclipse.core.databinding.property.map.MapProperty;
22
import org.eclipse.core.databinding.property.map.MapPropertyChangeEvent;
23
import org.eclipse.core.databinding.property.value.IValueProperty;
24
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
25
import org.eclipse.core.databinding.property.value.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/internal/databinding/property/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;
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.map.IMapProperty;
23
import org.eclipse.core.databinding.property.map.IMapPropertyChangeListener;
24
import org.eclipse.core.databinding.property.map.MapProperty;
25
import org.eclipse.core.databinding.property.map.MapPropertyChangeEvent;
26
import org.eclipse.core.databinding.property.value.IValueProperty;
27
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
28
import org.eclipse.core.databinding.property.value.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/value/ValuePropertyChangeEvent.java (+60 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.value;
13
14
import org.eclipse.core.databinding.observable.value.ValueDiff;
15
import org.eclipse.core.databinding.property.IPropertyChangeListener;
16
import org.eclipse.core.databinding.property.PropertyChangeEvent;
17
import org.eclipse.core.runtime.Assert;
18
19
/**
20
 * Value change event describing a change of a value property on a particular
21
 * property source.
22
 * 
23
 * @since 1.2
24
 */
25
public class ValuePropertyChangeEvent extends PropertyChangeEvent {
26
	private static final long serialVersionUID = 1L;
27
28
	/**
29
	 * The value property that changed
30
	 */
31
	public final IValueProperty property;
32
33
	/**
34
	 * ValueDiff with the old and new values of the property.
35
	 */
36
	public final ValueDiff diff;
37
38
	/**
39
	 * Constructs a ValuePropertyChangeEvent with the given attributes
40
	 * 
41
	 * @param source
42
	 *            the property source
43
	 * @param property
44
	 *            the property that changed on the source
45
	 * @param diff
46
	 *            a ValueDiff describing the changes to the value property
47
	 */
48
	public ValuePropertyChangeEvent(Object source, IValueProperty property,
49
			ValueDiff diff) {
50
		super(source);
51
		this.property = property;
52
		Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$
53
		this.diff = diff;
54
	}
55
56
	protected void dispatch(IPropertyChangeListener listener) {
57
		((IValuePropertyChangeListener) listener)
58
				.handleValuePropertyChange(this);
59
	}
60
}
(-)src/org/eclipse/core/internal/databinding/observable/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.observable;
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.value.IValueProperty;
20
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
21
import org.eclipse.core.databinding.property.value.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/IProperty.java (+31 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
	 * Returns whether the property is disposed
23
	 * @return whether the property is disposed
24
	 */
25
	public boolean isDisposed();
26
27
	/**
28
	 * Disposes the property, removing all property listeners on source objects.
29
	 */
30
	public void dispose();
31
}
(-)src/org/eclipse/core/databinding/property/list/SimpleListProperty.java (+288 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
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property.list;
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.databinding.property.SimpleProperty;
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 SimpleListProperty extends SimpleProperty implements
35
		IListProperty {
36
	public void addListChangeListener(Object source,
37
			IListPropertyChangeListener listener) {
38
		addPropertyChangeListener(source, listener);
39
	}
40
41
	public void removeListChangeListener(Object source,
42
			IListPropertyChangeListener listener) {
43
		removePropertyChangeListener(source, listener);
44
	}
45
46
	protected final Object getProperty(Object source) {
47
		return getList(source);
48
	}
49
50
	public boolean contains(Object source, Object o) {
51
		return getList(source).contains(o);
52
	}
53
54
	public boolean containsAll(Object source, Collection c) {
55
		return getList(source).containsAll(c);
56
	}
57
58
	public boolean equals(Object source, Object o) {
59
		return getList(source).equals(o);
60
	}
61
62
	public Object get(Object source, int index) {
63
		return getList(source).get(index);
64
	}
65
66
	public int hashCode(Object source) {
67
		return getList(source).hashCode();
68
	}
69
70
	public int indexOf(Object source, Object o) {
71
		return getList(source).indexOf(o);
72
	}
73
74
	public boolean isEmpty(Object source) {
75
		return getList(source).isEmpty();
76
	}
77
78
	public int lastIndexOf(Object source, Object o) {
79
		return getList(source).lastIndexOf(o);
80
	}
81
82
	public int size(Object source) {
83
		return getList(source).size();
84
	}
85
86
	public Object[] toArray(Object source, Object[] array) {
87
		return getList(source).toArray(array);
88
	}
89
90
	public Object[] toArray(Object source) {
91
		return getList(source).toArray();
92
	}
93
94
	private void updateListAndFireChange(Object source, List list, ListDiff diff) {
95
		setUpdating(source, true);
96
		try {
97
			updateList(source, list, diff);
98
		} finally {
99
			setUpdating(source, false);
100
		}
101
102
		if (hasListeners(source)) {
103
			fireListChange(source, diff);
104
		}
105
	}
106
107
	/**
108
	 * Updates the property on the source with the specified change. A list
109
	 * change is fired in the calling method, so implementers do not need to
110
	 * call {@link #fireListChange(Object, ListDiff)}.
111
	 * 
112
	 * @param source
113
	 *            the property source
114
	 * @param list
115
	 *            the new list
116
	 * @param diff
117
	 *            a diff describing the change
118
	 */
119
	protected abstract void updateList(Object source, List list, ListDiff diff);
120
121
	/**
122
	 * Notifies that the source's property changed. Listeners registered in the
123
	 * {@link #addListenerTo(Object)} method should call this method when a
124
	 * property change is observed on the source object.
125
	 * 
126
	 * @param source
127
	 *            the property source
128
	 * @param diff
129
	 *            a diff describing the list change. If null, a diff will be
130
	 *            computed from the cached state and the current state.
131
	 */
132
	protected final void fireListChange(Object source, ListDiff diff) {
133
		if (!isUpdating(source) && hasListeners(source)) {
134
			if (diff == null) {
135
				diff = Diffs.computeListDiff((List) getCached(source),
136
						getList(source));
137
			}
138
			if (!diff.isEmpty()) {
139
				firePropertyChange(new ListPropertyChangeEvent(source, this,
140
						diff));
141
			}
142
		}
143
	}
144
145
	public boolean add(Object source, Object o) {
146
		add(source, size(source), o);
147
		return true;
148
	}
149
150
	public void add(Object source, int index, Object element) {
151
		List list = new ArrayList(getList(source));
152
		list.add(index, element);
153
		updateListAndFireChange(source, list, Diffs.createListDiff(Diffs
154
				.createListDiffEntry(index, true, element)));
155
	}
156
157
	public boolean addAll(Object source, Collection c) {
158
		if (c.isEmpty())
159
			return false;
160
		addAll(source, size(source), c);
161
		return true;
162
	}
163
164
	public boolean addAll(Object source, int index, Collection c) {
165
		if (c.isEmpty()) {
166
			return false;
167
		}
168
169
		List list = new ArrayList(getList(source));
170
		List entries = new ArrayList();
171
		int i = index;
172
		for (Iterator it = c.iterator(); it.hasNext(); i++) {
173
			Object o = it.next();
174
			list.add(i, o);
175
			entries.add(Diffs.createListDiffEntry(i, true, o));
176
		}
177
		boolean changed = !entries.isEmpty();
178
		if (changed) {
179
			ListDiffEntry[] ea = (ListDiffEntry[]) entries
180
					.toArray(new ListDiffEntry[entries.size()]);
181
			updateListAndFireChange(source, list, Diffs.createListDiff(ea));
182
		}
183
		return changed;
184
	}
185
186
	public void clear(Object source) {
187
		if (!isEmpty(source)) {
188
			List list = getList(source);
189
			ListDiffEntry[] entries = new ListDiffEntry[list.size()];
190
			int i = 0;
191
			for (Iterator it = getList(source).iterator(); it.hasNext(); i++) {
192
				entries[i] = Diffs.createListDiffEntry(0, false, it.next());
193
			}
194
			updateListAndFireChange(source, new ArrayList(), Diffs
195
					.createListDiff(entries));
196
		}
197
	}
198
199
	public Object move(Object source, int oldIndex, int newIndex) {
200
		if (oldIndex == newIndex)
201
			return get(source, oldIndex);
202
		List list = new ArrayList(getList(source));
203
		Object result = list.remove(oldIndex);
204
		list.add(newIndex, result);
205
		updateListAndFireChange(source, list, Diffs.createListDiff(Diffs
206
				.createListDiffEntry(oldIndex, false, result), Diffs
207
				.createListDiffEntry(newIndex, true, result)));
208
		return result;
209
	}
210
211
	public boolean remove(Object source, Object o) {
212
		int i = indexOf(source, o);
213
		if (i == -1)
214
			return false;
215
		remove(source, i);
216
		return true;
217
	}
218
219
	public Object remove(Object source, int index) {
220
		List list = new ArrayList(getList(source));
221
		Object result = list.remove(index);
222
		updateListAndFireChange(source, list, Diffs.createListDiff(Diffs
223
				.createListDiffEntry(index, false, result)));
224
		return result;
225
	}
226
227
	public boolean removeAll(Object source, Collection c) {
228
		if (isEmpty(source)) {
229
			return false;
230
		}
231
		if (c.isEmpty()) {
232
			return false;
233
		}
234
		List list = new ArrayList(getList(source));
235
		List entries = new ArrayList();
236
		for (ListIterator it = list.listIterator(); it.hasNext();) {
237
			Object o = it.next();
238
			if (c.contains(o)) {
239
				entries.add(Diffs.createListDiffEntry(it.previousIndex(),
240
						false, o));
241
				it.remove();
242
			}
243
		}
244
		boolean changed = !entries.isEmpty();
245
		if (changed) {
246
			ListDiffEntry[] ea = (ListDiffEntry[]) entries
247
					.toArray(new ListDiffEntry[entries.size()]);
248
			updateListAndFireChange(source, list, Diffs.createListDiff(ea));
249
		}
250
		return changed;
251
	}
252
253
	public boolean retainAll(Object source, Collection c) {
254
		if (isEmpty(source)) {
255
			return false;
256
		}
257
		if (c.isEmpty()) {
258
			clear(source);
259
			return true;
260
		}
261
		List list = new ArrayList(getList(source));
262
		List entries = new ArrayList();
263
		for (ListIterator it = list.listIterator(); it.hasNext();) {
264
			Object o = it.next();
265
			if (!c.contains(o)) {
266
				entries.add(Diffs.createListDiffEntry(it.previousIndex(),
267
						false, o));
268
				it.remove();
269
			}
270
		}
271
		boolean changed = !entries.isEmpty();
272
		if (changed) {
273
			ListDiffEntry[] ea = (ListDiffEntry[]) entries
274
					.toArray(new ListDiffEntry[entries.size()]);
275
			updateListAndFireChange(source, list, Diffs.createListDiff(ea));
276
		}
277
		return changed;
278
	}
279
280
	public Object set(Object source, int index, Object element) {
281
		List list = new ArrayList(getList(source));
282
		Object result = list.set(index, element);
283
		updateListAndFireChange(source, list, Diffs.createListDiff(Diffs
284
				.createListDiffEntry(index, false, result), Diffs
285
				.createListDiffEntry(index, true, element)));
286
		return result;
287
	}
288
}
(-)src/org/eclipse/core/databinding/property/IPropertyObservable.java (+28 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.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/databinding/property/set/SetPropertyChangeEvent.java (+59 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.set;
13
14
import org.eclipse.core.databinding.observable.set.SetDiff;
15
import org.eclipse.core.databinding.property.IPropertyChangeListener;
16
import org.eclipse.core.databinding.property.PropertyChangeEvent;
17
import org.eclipse.core.runtime.Assert;
18
19
/**
20
 * Set change event describing an incremental change of a set property on a
21
 * particular property source.
22
 * 
23
 * @since 1.2
24
 */
25
public class SetPropertyChangeEvent extends PropertyChangeEvent {
26
	private static final long serialVersionUID = 1L;
27
28
	/**
29
	 * The set property that changed
30
	 */
31
	public final ISetProperty property;
32
33
	/**
34
	 * SetDiff enumerating the added and removed elements in the set.
35
	 */
36
	public final SetDiff diff;
37
38
	/**
39
	 * Constructs a SetPropertyChangeEvent with the given attributes
40
	 * 
41
	 * @param source
42
	 *            the property source
43
	 * @param property
44
	 *            the property that changed on the source
45
	 * @param diff
46
	 *            a SetDiff describing the changes to the set property
47
	 */
48
	public SetPropertyChangeEvent(Object source, ISetProperty property,
49
			SetDiff diff) {
50
		super(source);
51
		this.property = property;
52
		Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$
53
		this.diff = diff;
54
	}
55
56
	protected void dispatch(IPropertyChangeListener listener) {
57
		((ISetPropertyChangeListener) listener).handleSetPropertyChange(this);
58
	}
59
}
(-)src/org/eclipse/core/databinding/property/value/IValuePropertyChangeListener.java (+29 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.value;
13
14
import org.eclipse.core.databinding.property.IPropertyChangeListener;
15
16
/**
17
 * Listener for changes to value properties on a property source
18
 * 
19
 * @since 1.2
20
 */
21
public interface IValuePropertyChangeListener extends IPropertyChangeListener {
22
	/**
23
	 * Handle a change to a value property on a specific property source.
24
	 * 
25
	 * @param event
26
	 *            an event describing the value change that occured.
27
	 */
28
	public void handleValuePropertyChange(ValuePropertyChangeEvent event);
29
}
(-)src/org/eclipse/core/databinding/property/SimpleProperty.java (+104 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.internal.databinding.property.PropertyCache;
15
import org.eclipse.core.internal.databinding.property.PropertyUpdateHelper;
16
17
/**
18
 * Abstract IProperty implementation with
19
 * 
20
 * @since 1.2
21
 */
22
public abstract class SimpleProperty extends Property {
23
	private PropertyCache cache = new PropertyCache();
24
	private PropertyUpdateHelper updateHelper = new PropertyUpdateHelper();
25
26
	protected final Object getCached(Object source) {
27
		PropertyCache cache = this.cache;
28
		return cache == null ? null : cache.get(source);
29
	}
30
31
	/**
32
	 * Returns whether this property is currently being updated on the source.
33
	 * Implementors should query this value to avoid unnecessary calculations,
34
	 * such as computing a diff.
35
	 * 
36
	 * @param source
37
	 *            the property source
38
	 * @return whether this property is currently being updated on the source.
39
	 */
40
	protected final boolean isUpdating(Object source) {
41
		if (!hasListeners(source))
42
			return false;
43
		PropertyUpdateHelper updateHelper = this.updateHelper;
44
		return updateHelper != null && updateHelper.isUpdating(source);
45
	}
46
47
	protected final void setUpdating(Object source, boolean state) {
48
		PropertyUpdateHelper updateHelper = this.updateHelper;
49
		if (updateHelper != null)
50
			updateHelper.setUpdating(source, state);
51
	}
52
53
	/**
54
	 * Adds a listener to the specified property source.
55
	 * <p>
56
	 * The <code>SimpleProperty</code> implementation of this method ensures
57
	 * that the property state is cached for as long as the property has
58
	 * listeners on the given source. If overriding this method in a subclass,
59
	 * <code>super.addListenerTo</code> must be invoked.
60
	 */
61
	protected void addListenerTo(Object source) {
62
		PropertyCache cache = this.cache;
63
		if (cache != null)
64
			cache.put(source, getProperty(source));
65
	}
66
67
	/**
68
	 * Removes a listener from the specified property source.
69
	 * <p>
70
	 * The <code>SimpleProperty</code> implementation of this method ensures
71
	 * that the property state for the given source is removed from the cache.
72
	 * If overriding this method in a subclass,
73
	 * <code>super.removeListenerFrom</code> must be invoked.
74
	 */
75
	protected void removeListenerFrom(Object source) {
76
		PropertyCache cache = this.cache;
77
		if (cache != null)
78
			cache.remove(source);
79
	}
80
81
	protected abstract Object getProperty(Object source);
82
83
	protected void firePropertyChange(PropertyChangeEvent event) {
84
		Object source = event.getSource();
85
		if (!isUpdating(source)) {
86
			PropertyCache cache = this.cache;
87
			if (cache != null)
88
				cache.put(source, getProperty(source));
89
			super.firePropertyChange(event);
90
		}
91
	}
92
93
	public synchronized void dispose() {
94
		if (cache != null) {
95
			cache.dispose();
96
			cache = null;
97
		}
98
		if (updateHelper != null) {
99
			updateHelper.dispose();
100
			updateHelper = null;
101
		}
102
		super.dispose();
103
	}
104
}
(-)src/org/eclipse/core/databinding/property/list/ListProperty.java (+91 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.list;
13
14
import java.util.Collection;
15
16
import org.eclipse.core.databinding.observable.list.ListDiff;
17
import org.eclipse.core.databinding.property.Property;
18
19
/**
20
 * Abstract implementation of IListProperty.
21
 * 
22
 * @since 1.2
23
 */
24
public abstract class ListProperty extends Property implements
25
		IListProperty {
26
	public boolean contains(Object source, Object o) {
27
		return getList(source).contains(o);
28
	}
29
30
	public boolean containsAll(Object source, Collection c) {
31
		return getList(source).containsAll(c);
32
	}
33
34
	public boolean equals(Object source, Object o) {
35
		return getList(source).equals(o);
36
	}
37
38
	public Object get(Object source, int index) {
39
		return getList(source).get(index);
40
	}
41
42
	public int hashCode(Object source) {
43
		return getList(source).hashCode();
44
	}
45
46
	public int indexOf(Object source, Object o) {
47
		return getList(source).indexOf(o);
48
	}
49
50
	public boolean isEmpty(Object source) {
51
		return getList(source).isEmpty();
52
	}
53
54
	public int lastIndexOf(Object source, Object o) {
55
		return getList(source).lastIndexOf(o);
56
	}
57
58
	public int size(Object source) {
59
		return getList(source).size();
60
	}
61
62
	public Object[] toArray(Object source, Object[] array) {
63
		return getList(source).toArray(array);
64
	}
65
66
	public Object[] toArray(Object source) {
67
		return getList(source).toArray();
68
	}
69
70
	public final void addListChangeListener(Object source,
71
			IListPropertyChangeListener listener) {
72
		addPropertyChangeListener(source, listener);
73
	}
74
75
	public final void removeListChangeListener(Object source,
76
			IListPropertyChangeListener listener) {
77
		removePropertyChangeListener(source, listener);
78
	}
79
80
	/**
81
	 * Fires a ListPropertyChangeEvent with the given source and diff
82
	 * 
83
	 * @param source
84
	 *            the property source
85
	 * @param diff
86
	 *            the list diff
87
	 */
88
	protected final void fireListChange(Object source, ListDiff diff) {
89
		firePropertyChange(new ListPropertyChangeEvent(source, this, diff));
90
	}
91
}
(-)src/org/eclipse/core/databinding/property/PropertyChangeEvent.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 java.util.EventObject;
15
16
/**
17
 * Base class for change events in the properties API
18
 * 
19
 * @since 1.2
20
 */
21
public abstract class PropertyChangeEvent extends EventObject {
22
	private static final long serialVersionUID = 1L;
23
24
	protected PropertyChangeEvent(Object source) {
25
		super(source);
26
	}
27
28
	protected abstract void dispatch(IPropertyChangeListener listener);
29
30
	public boolean equals(Object obj) {
31
		if (obj == this)
32
			return true;
33
		if (obj == null)
34
			return false;
35
		if (getClass() != obj.getClass())
36
			return false;
37
38
		return getSource().equals(((PropertyChangeEvent) obj).getSource());
39
	}
40
41
	public int hashCode() {
42
		return getSource().hashCode();
43
	}
44
}
(-)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/map/IMapPropertyChangeListener.java (+29 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.map;
13
14
import org.eclipse.core.databinding.property.IPropertyChangeListener;
15
16
/**
17
 * Listener for changes to map properties on a property source
18
 * 
19
 * @since 1.2
20
 */
21
public interface IMapPropertyChangeListener extends IPropertyChangeListener {
22
	/**
23
	 * Handle a change to a map property on a specific property source.
24
	 * 
25
	 * @param event
26
	 *            an event describing the map change that occured.
27
	 */
28
	public void handleMapPropertyChange(MapPropertyChangeEvent event);
29
}
(-)src/org/eclipse/core/internal/databinding/observable/masterdetail/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.observable.masterdetail;
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.value.IValueProperty;
19
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
20
import org.eclipse.core.databinding.property.value.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/PropertyChangeSupport.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.databinding.property;
13
14
import java.util.HashMap;
15
import java.util.Iterator;
16
import java.util.Map;
17
18
import org.eclipse.core.internal.databinding.IdentityWrapper;
19
import org.eclipse.core.runtime.ListenerList;
20
21
/**
22
 * Utility class which maintains a listener list per property source object.
23
 * 
24
 * @since 1.2
25
 */
26
abstract class PropertyChangeSupport {
27
	private Map changeListeners;
28
29
	PropertyChangeSupport() {
30
		this.changeListeners = null;
31
	}
32
33
	/**
34
	 * Adds the listener to the list of listeners for the specified source
35
	 * 
36
	 * @param source
37
	 *            the property source
38
	 * @param listener
39
	 *            the listener to add
40
	 */
41
	synchronized final void addListener(Object source,
42
			IPropertyChangeListener listener) {
43
		boolean hadListeners = hasListeners(source);
44
45
		if (changeListeners == null) {
46
			changeListeners = new HashMap();
47
		}
48
49
		ListenerList listeners;
50
		IdentityWrapper identityWrapper = new IdentityWrapper(source);
51
		if (changeListeners.containsKey(identityWrapper)) {
52
			listeners = (ListenerList) changeListeners.get(identityWrapper);
53
		} else {
54
			changeListeners
55
					.put(identityWrapper, listeners = new ListenerList());
56
		}
57
58
		listeners.add(listener);
59
60
		if (!hadListeners) {
61
			addListenerTo(source);
62
		}
63
	}
64
65
	/**
66
	 * Removes the listener from the list of listeners for the specified source
67
	 * 
68
	 * @param source
69
	 *            the property source
70
	 * @param listener
71
	 *            the listener to remove
72
	 */
73
	synchronized final void removeListener(Object source,
74
			IPropertyChangeListener listener) {
75
		if (changeListeners != null) {
76
			IdentityWrapper identityWrapper = new IdentityWrapper(source);
77
			ListenerList listeners = (ListenerList) changeListeners
78
					.get(identityWrapper);
79
			if (listeners != null) {
80
				listeners.remove(listener);
81
				if (listeners.isEmpty()) {
82
					synchronized (listeners) {
83
						if (listeners.isEmpty()) {
84
							changeListeners.remove(identityWrapper);
85
							removeListenerFrom(source);
86
						}
87
					}
88
				}
89
			}
90
		}
91
	}
92
93
	/**
94
	 * Returns whether the change support has listeners registered for the given
95
	 * property source
96
	 * 
97
	 * @param source
98
	 *            the property source
99
	 * @return whether the change support has listeners registered for the given
100
	 *         property source
101
	 */
102
	synchronized final boolean hasListeners(Object source) {
103
		return changeListeners != null
104
				&& changeListeners.containsKey(new IdentityWrapper(source));
105
	}
106
107
	protected abstract void addListenerTo(Object source);
108
109
	protected abstract void removeListenerFrom(Object source);
110
111
	private synchronized final Object[] getListeners(Object source) {
112
		if (changeListeners == null)
113
			return null;
114
		ListenerList list = (ListenerList) changeListeners
115
				.get(new IdentityWrapper(source));
116
		if (list == null)
117
			return null;
118
		return list.getListeners();
119
	}
120
121
	/**
122
	 * Notifies all registered listeners of the specified property change
123
	 * 
124
	 * @param event
125
	 *            the property change event
126
	 */
127
	public final void firePropertyChange(PropertyChangeEvent event) {
128
		Object[] listeners = getListeners(event.getSource());
129
		if (listeners != null) {
130
			for (int i = 0; i < listeners.length; i++) {
131
				event.dispatch((IPropertyChangeListener) listeners[i]);
132
			}
133
		}
134
	}
135
136
	/**
137
	 * Diposes the change support, removing all registered listeners
138
	 */
139
	public synchronized void dispose() {
140
		if (changeListeners != null) {
141
			for (Iterator iterator = changeListeners.keySet().iterator(); iterator
142
					.hasNext();) {
143
				Object source = ((IdentityWrapper) iterator.next()).unwrap();
144
				removeListenerFrom(source);
145
				iterator.remove();
146
			}
147
			changeListeners = null;
148
		}
149
	}
150
}
(-)src/org/eclipse/core/internal/databinding/property/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;
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.map.MapProperty;
23
import org.eclipse.core.databinding.property.set.ISetProperty;
24
import org.eclipse.core.databinding.property.set.ISetPropertyChangeListener;
25
import org.eclipse.core.databinding.property.set.SetPropertyChangeEvent;
26
import org.eclipse.core.databinding.property.value.IValueProperty;
27
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
28
import org.eclipse.core.databinding.property.value.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/databinding/property/map/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.map;
13
14
import org.eclipse.core.databinding.observable.map.MapDiff;
15
import org.eclipse.core.databinding.property.Property;
16
17
/**
18
 * Abstract implementation of IMapProperty
19
 * 
20
 * @since 1.2
21
 */
22
public abstract class MapProperty extends Property implements IMapProperty {
23
	public boolean containsKey(Object source, Object key) {
24
		return getMap(source).containsKey(key);
25
	}
26
27
	public boolean containsValue(Object source, Object value) {
28
		return getMap(source).containsValue(value);
29
	}
30
31
	public boolean equals(Object source, Object o) {
32
		return getMap(source).equals(o);
33
	}
34
35
	public Object get(Object source, Object key) {
36
		return getMap(source).get(key);
37
	}
38
39
	public int hashCode(Object source) {
40
		return getMap(source).hashCode();
41
	}
42
43
	public boolean isEmpty(Object source) {
44
		return getMap(source).isEmpty();
45
	}
46
47
	public int size(Object source) {
48
		return getMap(source).size();
49
	}
50
51
	public final void addMapChangeListener(Object source,
52
			IMapPropertyChangeListener listener) {
53
		addPropertyChangeListener(source, listener);
54
	}
55
56
	public final void removeMapChangeListener(Object source,
57
			IMapPropertyChangeListener listener) {
58
		removePropertyChangeListener(source, listener);
59
	}
60
61
	/**
62
	 * Fires a MapPropertyChangeEvent with the given source and diff
63
	 * 
64
	 * @param source
65
	 *            the property source
66
	 * @param diff
67
	 *            the map diff
68
	 */
69
	protected final void fireMapChange(Object source, MapDiff diff) {
70
		firePropertyChange(new MapPropertyChangeEvent(source, this, diff));
71
	}
72
}
(-)src/org/eclipse/core/databinding/property/set/SetProperty.java (+79 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.set;
13
14
import java.util.Collection;
15
16
import org.eclipse.core.databinding.observable.set.SetDiff;
17
import org.eclipse.core.databinding.property.Property;
18
19
/**
20
 * Abstract implementation of ISetProperty
21
 * 
22
 * @since 1.2
23
 */
24
public abstract class SetProperty extends Property implements
25
		ISetProperty {
26
	public boolean contains(Object source, Object o) {
27
		return getSet(source).contains(o);
28
	}
29
30
	public boolean containsAll(Object source, Collection c) {
31
		return getSet(source).containsAll(c);
32
	}
33
34
	public boolean equals(Object source, Object o) {
35
		return getSet(source).equals(o);
36
	}
37
38
	public int hashCode(Object source) {
39
		return getSet(source).hashCode();
40
	}
41
42
	public boolean isEmpty(Object source) {
43
		return getSet(source).isEmpty();
44
	}
45
46
	public int size(Object source) {
47
		return getSet(source).size();
48
	}
49
50
	public Object[] toArray(Object source, Object[] array) {
51
		return getSet(source).toArray(array);
52
	}
53
54
	public Object[] toArray(Object source) {
55
		return getSet(source).toArray();
56
	}
57
58
	public final void addSetChangeListener(Object source,
59
			ISetPropertyChangeListener listener) {
60
		addPropertyChangeListener(source, listener);
61
	}
62
63
	public final void removeSetChangeListener(Object source,
64
			ISetPropertyChangeListener listener) {
65
		removePropertyChangeListener(source, listener);
66
	}
67
68
	/**
69
	 * Fires a SetPropertyChangeEvent with the given source and diff
70
	 * 
71
	 * @param source
72
	 *            the property source
73
	 * @param diff
74
	 *            the set diff
75
	 */
76
	protected final void fireSetChange(Object source, SetDiff diff) {
77
		firePropertyChange(new SetPropertyChangeEvent(source, this, diff));
78
	}
79
}
(-)src/org/eclipse/core/databinding/property/value/SimpleValueProperty.java (+110 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
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property.value;
13
14
import org.eclipse.core.databinding.observable.Diffs;
15
import org.eclipse.core.databinding.observable.value.ValueDiff;
16
import org.eclipse.core.databinding.property.SimpleProperty;
17
import org.eclipse.core.internal.databinding.Util;
18
19
/**
20
 * Abstract implementation of IValueProperty. This class satisfies many of the
21
 * functional requirements of the IValueProperty interface, and tries to leave
22
 * only the aspects specific to the concrete class
23
 * <p>
24
 * 
25
 * 
26
 * @since 1.2
27
 */
28
public abstract class SimpleValueProperty extends SimpleProperty implements
29
		IValueProperty {
30
	public void addValueChangeListener(Object source,
31
			IValuePropertyChangeListener listener) {
32
		addPropertyChangeListener(source, listener);
33
	}
34
35
	public void removeValueChangeListener(Object source,
36
			IValuePropertyChangeListener listener) {
37
		removePropertyChangeListener(source, listener);
38
	}
39
40
	protected final Object getProperty(Object source) {
41
		return getValue(source);
42
	}
43
44
	/**
45
	 * Sets the value property on the source to the specified value, then fires
46
	 * a value change.
47
	 * 
48
	 * @param source
49
	 *            the property source
50
	 * @param value
51
	 *            the new value
52
	 */
53
	public final void setValue(Object source, Object value) {
54
		Object oldValue = getCached(source);
55
56
		setUpdating(source, true);
57
		try {
58
			updateValue(source, value, Diffs.createValueDiff(oldValue, value));
59
		} finally {
60
			setUpdating(source, false);
61
		}
62
63
		if (hasListeners(source)) {
64
			Object newValue = getValue(source);
65
			if (!Util.equals(oldValue, newValue)) {
66
				fireValueChange(source, Diffs.createValueDiff(oldValue,
67
						newValue));
68
			}
69
		}
70
	}
71
72
	/**
73
	 * Updates the property on the source with the specified change. A value
74
	 * change event is fired in the calling method, so implementers do not need
75
	 * to call {@link #fireValueChange(Object, ValueDiff)}.
76
	 * 
77
	 * @param source
78
	 *            the property source
79
	 * @param value
80
	 *            the new value
81
	 * @param diff
82
	 *            a diff describing the change
83
	 */
84
	protected abstract void updateValue(Object source, Object value,
85
			ValueDiff diff);
86
87
	/**
88
	 * Notifies that the source's property changed. Listeners registered in the
89
	 * {@link #addListenerTo(Object)} method should call this method when a
90
	 * property change is observed on the source object.
91
	 * 
92
	 * @param source
93
	 *            the property source
94
	 * @param diff
95
	 *            a diff describing the value change. If null, a diff will be
96
	 *            created from the cached value and the current value.
97
	 */
98
	protected final void fireValueChange(Object source, ValueDiff diff) {
99
		if (!isUpdating(source) && hasListeners(source)) {
100
			if (diff == null) {
101
				diff = Diffs.createValueDiff(getCached(source),
102
						getValue(source));
103
			}
104
			if (!Util.equals(diff.getOldValue(), diff.getNewValue())) {
105
				firePropertyChange(new ValuePropertyChangeEvent(source, this,
106
						diff));
107
			}
108
		}
109
	}
110
}
(-)src/org/eclipse/core/databinding/property/PropertyObservables.java (+375 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.databinding.property.list.IListProperty;
23
import org.eclipse.core.databinding.property.map.IMapProperty;
24
import org.eclipse.core.databinding.property.set.ISetProperty;
25
import org.eclipse.core.databinding.property.value.IValueProperty;
26
import org.eclipse.core.internal.databinding.observable.PropertyObservableList;
27
import org.eclipse.core.internal.databinding.observable.PropertyObservableMap;
28
import org.eclipse.core.internal.databinding.observable.PropertyObservableSet;
29
import org.eclipse.core.internal.databinding.observable.PropertyObservableValue;
30
import org.eclipse.core.internal.databinding.observable.masterdetail.ListValuePropertyObservableList;
31
import org.eclipse.core.internal.databinding.observable.masterdetail.MapValuePropertyObservableMap;
32
import org.eclipse.core.internal.databinding.observable.masterdetail.SetValuePropertyObservableMap;
33
34
/**
35
 * A factory for creating observables observing properties of source objects
36
 * 
37
 * @since 1.2
38
 */
39
public class PropertyObservables {
40
	/**
41
	 * Returns an observable value on the default realm that tracks the given
42
	 * property of the source object.
43
	 * 
44
	 * @param source
45
	 *            the property value
46
	 * @param property
47
	 *            the value property to observe
48
	 * @return an observable value on the default realm that tracks the given
49
	 *         property of the source object.
50
	 * @since 1.2
51
	 */
52
	public static IObservableValue observeValue(Object source,
53
			IValueProperty property) {
54
		return observeValue(Realm.getDefault(), source, property);
55
	}
56
57
	/**
58
	 * Returns an observable value on the given realm that tracks the given
59
	 * property of the source object
60
	 * 
61
	 * @param realm
62
	 *            the realm
63
	 * @param source
64
	 *            the property source
65
	 * @param property
66
	 *            the value property observe
67
	 * @return an observable value that tracks the given property of the source
68
	 *         object
69
	 * @since 1.2
70
	 */
71
	public static IObservableValue observeValue(Realm realm, Object source,
72
			IValueProperty property) {
73
		return new PropertyObservableValue(realm, source, property);
74
	}
75
76
	/**
77
	 * Returns a factory for creating observable values on the default realm
78
	 * tracking the given property of a particular source object
79
	 * 
80
	 * @param property
81
	 *            the property to observe
82
	 * @return a factory for creating observable values on the default realm
83
	 *         tracking the given property of a particular source object
84
	 */
85
	public static IObservableFactory valueFactory(IValueProperty property) {
86
		return valueFactory(Realm.getDefault(), property);
87
	}
88
89
	/**
90
	 * Returns a factory for creating observable values on the given realm
91
	 * tracking the given property of a particular source object
92
	 * 
93
	 * @param realm
94
	 *            the realm
95
	 * @param property
96
	 *            the property to observe
97
	 * @return a factory for creating observable values on the given realm
98
	 *         tracking the given property of a particular source object
99
	 */
100
	public static IObservableFactory valueFactory(final Realm realm,
101
			final IValueProperty property) {
102
		return new IObservableFactory() {
103
			public IObservable createObservable(Object target) {
104
				return observeValue(realm, target, property);
105
			}
106
		};
107
	}
108
109
	/**
110
	 * Returns an observable value on the master observable's realm which tracks
111
	 * the given property of the current value of <code>master</code>.
112
	 * 
113
	 * @param master
114
	 *            the master observable
115
	 * @param property
116
	 *            the property to observe
117
	 * @return an observable value which tracks the given property of the
118
	 *         current value of <code>master</code>.
119
	 */
120
	public static IObservableValue observeDetailValue(IObservableValue master,
121
			IValueProperty property) {
122
		return MasterDetailObservables.detailValue(master, valueFactory(master
123
				.getRealm(), property), property.getValueType());
124
	}
125
126
	/**
127
	 * Returns an observable set on the default realm that tracks the given
128
	 * property of the source object
129
	 * 
130
	 * @param source
131
	 *            the property source
132
	 * @param property
133
	 *            the property to observe
134
	 * @return an observable set on the default realm that tracks the given
135
	 *         property of the source object
136
	 */
137
	public static IObservableSet observeSet(Object source, ISetProperty property) {
138
		return observeSet(Realm.getDefault(), source, property);
139
	}
140
141
	/**
142
	 * Returns an observable set on the given realm that tracks the given
143
	 * property of the source object
144
	 * 
145
	 * @param realm
146
	 *            the realm
147
	 * @param source
148
	 *            the property source
149
	 * @param property
150
	 *            the property to observe
151
	 * @return an observable set on the given realm that tracks the given
152
	 *         property of the source object
153
	 */
154
	public static IObservableSet observeSet(Realm realm, Object source,
155
			ISetProperty property) {
156
		return new PropertyObservableSet(realm, source, property);
157
	}
158
159
	/**
160
	 * Returns a factory for creating observable sets on the default realm
161
	 * tracking the given property of a particular source object
162
	 * 
163
	 * @param property
164
	 *            the property to be observed
165
	 * @return a factory for creating observable sets on the default realm
166
	 *         tracking the given property of a particular source object
167
	 */
168
	public static IObservableFactory setFactory(ISetProperty property) {
169
		return setFactory(Realm.getDefault(), property);
170
	}
171
172
	/**
173
	 * Returns a factory for creating obervable sets on the given realm tracking
174
	 * the given property of a particular source object
175
	 * 
176
	 * @param realm
177
	 *            the realm
178
	 * @param property
179
	 *            the property to be observed
180
	 * @return a factory for creating obervable sets on the given realm tracking
181
	 *         the given property of a particular source object
182
	 */
183
	public static IObservableFactory setFactory(final Realm realm,
184
			final ISetProperty property) {
185
		return new IObservableFactory() {
186
			public IObservable createObservable(Object target) {
187
				return observeSet(realm, target, property);
188
			}
189
		};
190
	}
191
192
	/**
193
	 * Returns an observable set on the master observable's realm which tracks
194
	 * the given property of the current value of <code>master</code>.
195
	 * 
196
	 * @param master
197
	 *            the master observable
198
	 * @param property
199
	 *            the property to observe
200
	 * @return an observable set on the given realm which tracks the given
201
	 *         property of the current value of <code>master</code>.
202
	 */
203
	public static IObservableSet observeDetailSet(IObservableValue master,
204
			ISetProperty property) {
205
		return MasterDetailObservables.detailSet(master, setFactory(master
206
				.getRealm(), property), property.getElementType());
207
	}
208
209
	/**
210
	 * Returns an observable list on the default realm that tracks the given
211
	 * property of the source object
212
	 * 
213
	 * @param source
214
	 *            the property source
215
	 * @param property
216
	 *            the property to observe
217
	 * @return an observable list on the default realm that tracks the given
218
	 *         property of the source object
219
	 */
220
	public static IObservableList observeList(Object source,
221
			IListProperty property) {
222
		return observeList(Realm.getDefault(), source, property);
223
	}
224
225
	/**
226
	 * Returns an observable list on the given realm that tracks the given
227
	 * property of the source object
228
	 * 
229
	 * @param realm
230
	 *            the realm
231
	 * @param source
232
	 *            the property source
233
	 * @param property
234
	 *            the property to observe
235
	 * @return an observable list on the given realm that tracks the given
236
	 *         property of the source object
237
	 */
238
	public static IObservableList observeList(Realm realm, Object source,
239
			IListProperty property) {
240
		return new PropertyObservableList(realm, source, property);
241
	}
242
243
	/**
244
	 * Returns a factory for creating observable lists on the default realm
245
	 * tracking the given property of a particular source object
246
	 * 
247
	 * @param property
248
	 *            the property to be observed
249
	 * @return an observable value factory on
250
	 */
251
	public static IObservableFactory listFactory(IListProperty property) {
252
		return listFactory(Realm.getDefault(), property);
253
	}
254
255
	/**
256
	 * Returns a factory for creating obervable lists on the given realm
257
	 * tracking the given property of a particular source object
258
	 * 
259
	 * @param realm
260
	 *            the realm
261
	 * @param property
262
	 *            the property to be observed
263
	 * @return an observable value factory on
264
	 */
265
	public static IObservableFactory listFactory(final Realm realm,
266
			final IListProperty property) {
267
		return new IObservableFactory() {
268
			public IObservable createObservable(Object target) {
269
				return observeList(realm, target, property);
270
			}
271
		};
272
	}
273
274
	/**
275
	 * Returns an observable list on the master observable's realm which tracks
276
	 * the given property of the current value of <code>master</code>.
277
	 * 
278
	 * @param master
279
	 *            the master observable
280
	 * @param property
281
	 *            the property to observe
282
	 * @return an observable list on the given realm which tracks the given
283
	 *         property of the current value of <code>master</code>.
284
	 */
285
	public static IObservableList observeDetailList(IObservableValue master,
286
			IListProperty property) {
287
		return MasterDetailObservables.detailList(master, listFactory(master
288
				.getRealm(), property), property.getElementType());
289
	}
290
291
	/**
292
	 * Returns an observable list on the master observable's realm which tracks
293
	 * the given property of each element of <code>master</code>.
294
	 * 
295
	 * @param master
296
	 *            the master observable
297
	 * @param property
298
	 *            the property to observe on each element in the master
299
	 *            observable
300
	 * @return an observable
301
	 */
302
	public static IObservableList observeDetailValues(IObservableList master,
303
			IValueProperty property) {
304
		return new ListValuePropertyObservableList(master, property);
305
	}
306
307
	/**
308
	 * Returns an observable map on the default realm which tracks the given
309
	 * property of the source object
310
	 * 
311
	 * @param source
312
	 *            the property source
313
	 * @param property
314
	 *            the property to observe
315
	 * @return an observable map on the default realm which tracks the given
316
	 *         property of the source object
317
	 */
318
	public static IObservableMap observeMap(Object source, IMapProperty property) {
319
		return observeMap(Realm.getDefault(), source, property);
320
	}
321
322
	/**
323
	 * Returns an observable map on the given realm which tracks the given
324
	 * property of the source object
325
	 * 
326
	 * @param realm
327
	 *            the realm
328
	 * @param source
329
	 *            the property source
330
	 * @param property
331
	 *            the property to observe
332
	 * @return an observable map on the given realm which tracks the given
333
	 *         property of the source object
334
	 */
335
	public static IObservableMap observeMap(Realm realm, Object source,
336
			IMapProperty property) {
337
		return new PropertyObservableMap(realm, source, property);
338
	}
339
340
	/**
341
	 * Returns an observable map on the master observable's realm tracking the
342
	 * current values of the given property for the elements in the given set.
343
	 * 
344
	 * @param keySet
345
	 *            the master observable
346
	 * @param valueProperty
347
	 *            the property to observe on each element of the master
348
	 *            observable
349
	 * @return an observable map that tracks the current value of the given
350
	 *         property for the elements in the given set.
351
	 */
352
	public static IObservableMap observeDetailValues(IObservableSet keySet,
353
			IValueProperty valueProperty) {
354
		return new SetValuePropertyObservableMap(keySet, valueProperty);
355
	}
356
357
	/**
358
	 * Returns an observable map on the master observable's realm which tracks
359
	 * the current values of the given property for the entry values in the
360
	 * given map.
361
	 * 
362
	 * @param map
363
	 *            the master observable
364
	 * @param valueProperty
365
	 *            the property to observe on each entry value in the master
366
	 *            observable
367
	 * @return an observable map on the master observable's realm which tracks
368
	 *         the current value of the given property for the elements in the
369
	 *         given map's values collection
370
	 */
371
	public static IObservableMap observeDetailValues(IObservableMap map,
372
			IValueProperty valueProperty) {
373
		return new MapValuePropertyObservableMap(map, valueProperty);
374
	}
375
}
(-)src/org/eclipse/core/internal/databinding/property/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;
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.list.IListProperty;
21
import org.eclipse.core.databinding.property.list.IListPropertyChangeListener;
22
import org.eclipse.core.databinding.property.list.ListProperty;
23
import org.eclipse.core.databinding.property.list.ListPropertyChangeEvent;
24
import org.eclipse.core.databinding.property.value.IValueProperty;
25
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
26
import org.eclipse.core.databinding.property.value.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/internal/databinding/property/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;
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.list.IListProperty;
28
import org.eclipse.core.databinding.property.list.IListPropertyChangeListener;
29
import org.eclipse.core.databinding.property.list.ListProperty;
30
import org.eclipse.core.databinding.property.list.ListPropertyChangeEvent;
31
import org.eclipse.core.databinding.property.value.IValueProperty;
32
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
33
import org.eclipse.core.databinding.property.value.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/internal/databinding/property/PropertyCache.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
import org.eclipse.core.internal.databinding.IdentityWrapper;
18
19
/**
20
 * @since 3.3
21
 * 
22
 */
23
public class PropertyCache {
24
	private Map map;
25
26
	private Map getMap() {
27
		if (map == null)
28
			map = new HashMap();
29
		return map;
30
	}
31
32
	/**
33
	 * @param source
34
	 * @param property
35
	 */
36
	public synchronized void put(Object source, Object property) {
37
		getMap().put(new IdentityWrapper(source), property);
38
	}
39
40
	/**
41
	 * @param source
42
	 * @return the cached property
43
	 */
44
	public synchronized Object get(Object source) {
45
		return map == null ? null : map.get(new IdentityWrapper(source));
46
	}
47
48
	/**
49
	 * @param source
50
	 */
51
	public synchronized void remove(Object source) {
52
		if (map != null)
53
			map.remove(new IdentityWrapper(source));
54
	}
55
56
	/**
57
	 * 
58
	 */
59
	public void dispose() {
60
		if (map != null) {
61
			map.clear();
62
			map = null;
63
		}
64
	}
65
}
(-)src/org/eclipse/core/databinding/property/list/IListPropertyChangeListener.java (+29 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.list;
13
14
import org.eclipse.core.databinding.property.IPropertyChangeListener;
15
16
/**
17
 * Listener for changes to list properties on a property source
18
 * 
19
 * @since 1.2
20
 */
21
public interface IListPropertyChangeListener extends IPropertyChangeListener {
22
	/**
23
	 * Handle a change to a list property on a specific property source.
24
	 * 
25
	 * @param event
26
	 *            an event describing the list change that occured.
27
	 */
28
	public void handleListPropertyChange(ListPropertyChangeEvent event);
29
}
(-)src/org/eclipse/core/internal/databinding/observable/masterdetail/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.observable.masterdetail;
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.value.IValueProperty;
35
import org.eclipse.core.databinding.property.value.IValuePropertyChangeListener;
36
import org.eclipse.core.databinding.property.value.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/PropertyUpdateHelper.java (+68 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
import org.eclipse.core.internal.databinding.IdentityWrapper;
18
19
/**
20
 * @since 3.3
21
 * 
22
 */
23
public class PropertyUpdateHelper {
24
	private ThreadLocal localMap = new ThreadLocal() {
25
		protected Object initialValue() {
26
			return new HashMap();
27
		}
28
	};
29
30
	/**
31
	 * @param source
32
	 * @return blah
33
	 */
34
	public boolean isUpdating(Object source) {
35
		Map map = (Map) localMap.get();
36
		return map.containsKey(new IdentityWrapper(source));
37
	}
38
39
	/**
40
	 * @param source
41
	 * @param state
42
	 */
43
	public void setUpdating(Object source, boolean state) {
44
		Map map = (Map) localMap.get();
45
		IdentityWrapper identityWrapper = new IdentityWrapper(source);
46
		Integer count = (Integer) map.get(identityWrapper);
47
		int newCount = (count == null ? 0 : count.intValue())
48
				+ (state ? 1 : -1);
49
		if (newCount > 0)
50
			map.put(identityWrapper, new Integer(newCount));
51
		else
52
			map.remove(identityWrapper);
53
	}
54
55
	/**
56
	 * 
57
	 */
58
	public void dispose() {
59
		if (localMap != null) {
60
			Map map = (Map) localMap.get();
61
			if (map != null) {
62
				map.clear();
63
				localMap.set(null);
64
			}
65
			localMap = null;
66
		}
67
	}
68
}
(-)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/map/SimpleMapProperty.java (+189 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
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.property.map;
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.databinding.property.SimpleProperty;
24
import org.eclipse.core.internal.databinding.Util;
25
26
/**
27
 * Abstract map property implementation for properties where the map can be
28
 * completely replaced in a single atomic operation.
29
 * <p>
30
 * For example, a map-typed bean property Customer.phoneNumbers can be modified
31
 * by calling Customer.setPhoneNumbers(Map phoneNumbers).
32
 * 
33
 * @since 1.2
34
 */
35
public abstract class SimpleMapProperty extends SimpleProperty implements
36
		IMapProperty {
37
	public final void addMapChangeListener(Object source,
38
			IMapPropertyChangeListener listener) {
39
		addPropertyChangeListener(source, listener);
40
	}
41
42
	public final void removeMapChangeListener(Object source,
43
			IMapPropertyChangeListener listener) {
44
		removePropertyChangeListener(source, listener);
45
	}
46
47
	protected final Object getProperty(Object source) {
48
		return getMap(source);
49
	}
50
51
	public boolean containsKey(Object source, Object key) {
52
		return getMap(source).containsKey(key);
53
	}
54
55
	public boolean containsValue(Object source, Object value) {
56
		return getMap(source).containsValue(value);
57
	}
58
59
	public boolean equals(Object source, Object o) {
60
		return getMap(source).equals(o);
61
	}
62
63
	public Object get(Object source, Object key) {
64
		return getMap(source).get(key);
65
	}
66
67
	public int hashCode(Object source) {
68
		return getMap(source).hashCode();
69
	}
70
71
	public boolean isEmpty(Object source) {
72
		return getMap(source).isEmpty();
73
	}
74
75
	public int size(Object source) {
76
		return getMap(source).size();
77
	}
78
79
	private void updateMapAndFireChange(Object source, Map map, MapDiff diff) {
80
		setUpdating(source, true);
81
		try {
82
			updateMap(source, map, diff);
83
		} finally {
84
			setUpdating(source, false);
85
		}
86
87
		if (hasListeners(source)) {
88
			fireMapChange(source, diff);
89
		}
90
	}
91
92
	/**
93
	 * Updates the property on the source with the specified change. A map
94
	 * change event is fired in the calling method, so implementers do not need
95
	 * to call {@link #fireMapChange(Object, MapDiff)}.
96
	 * 
97
	 * @param source
98
	 *            the property source
99
	 * @param map
100
	 *            the new map
101
	 * @param diff
102
	 *            a diff describing the change
103
	 */
104
	protected abstract void updateMap(Object source, Map map, MapDiff diff);
105
106
	/**
107
	 * Notifies that the source's property changed. Listeners registered in the
108
	 * {@link #addListenerTo(Object)} method should call this method when a
109
	 * property change is observed on the source object.
110
	 * 
111
	 * @param source
112
	 *            the property source
113
	 * @param diff
114
	 *            a diff describing the map change. If null, a diff will be
115
	 *            computed from the cached state and the current state.
116
	 */
117
	protected final void fireMapChange(Object source, MapDiff diff) {
118
		if (!isUpdating(source) && hasListeners(source)) {
119
			if (diff == null) {
120
				diff = Diffs.computeMapDiff((Map) getCached(source),
121
						getMap(source));
122
			}
123
			if (!diff.isEmpty()) {
124
				firePropertyChange(new MapPropertyChangeEvent(source, this,
125
						diff));
126
			}
127
		}
128
	}
129
130
	public void clear(Object source) {
131
		if (!isEmpty(source)) {
132
			updateMapAndFireChange(source, new HashMap(), Diffs
133
					.createMapDiffRemoveAll(new HashMap(getMap(source))));
134
		}
135
	}
136
137
	public Object put(Object source, Object key, Object value) {
138
		Map map = new HashMap(getMap(source));
139
		boolean addition = !map.containsKey(key);
140
		Object result = map.put(key, value);
141
		MapDiff diff;
142
		if (addition) {
143
			diff = Diffs.createMapDiffSingleAdd(key, value);
144
		} else {
145
			diff = Diffs.createMapDiffSingleChange(key, result, value);
146
		}
147
		updateMapAndFireChange(source, map, diff);
148
		return result;
149
	}
150
151
	public void putAll(Object source, Map t) {
152
		if (t.isEmpty())
153
			return;
154
155
		Map map = new HashMap(getMap(source));
156
		Set addedKeys = new HashSet();
157
		Set changedKeys = new HashSet();
158
		Map oldValues = new HashMap();
159
		Map newValues = new HashMap();
160
		for (Iterator it = t.entrySet().iterator(); it.hasNext();) {
161
			Map.Entry entry = (Map.Entry) it.next();
162
			Object key = entry.getKey();
163
			Object newValue = entry.getValue();
164
			boolean addition = !map.containsKey(key);
165
			Object oldValue = map.put(key, newValue);
166
			if (addition) {
167
				addedKeys.add(key);
168
			} else if (!Util.equals(oldValue, newValue)) {
169
				changedKeys.add(key);
170
				oldValues.put(key, oldValue);
171
			}
172
			newValues.put(key, newValue);
173
		}
174
		updateMapAndFireChange(source, map, Diffs.createMapDiff(addedKeys,
175
				Collections.EMPTY_SET, changedKeys, oldValues, newValues));
176
	}
177
178
	public Object remove(Object source, Object key) {
179
		Map map = getMap(source);
180
		if (map.containsKey(key)) {
181
			map = new HashMap(map);
182
			Object result = map.remove(key);
183
			updateMapAndFireChange(source, map, Diffs
184
					.createMapDiffSingleRemove(key, result));
185
			return result;
186
		}
187
		return null;
188
	}
189
}
(-)src/org/eclipse/core/databinding/property/set/ISetProperty.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.set;
13
14
import java.util.Set;
15
16
import org.eclipse.core.databinding.property.ICollectionProperty;
17
18
/**
19
 * Interface for set-typed properties
20
 * 
21
 * @since 1.2
22
 * @noimplement This interface is not intended to be implemented by clients.
23
 */
24
public interface ISetProperty extends ICollectionProperty {
25
	/**
26
	 * Returns a Set with the current contents of the source's set property
27
	 * 
28
	 * @param source
29
	 *            the property source
30
	 * @return a Set with the current contents of the source's set property
31
	 */
32
	public Set getSet(Object source);
33
34
	/**
35
	 * Adds the given set property change listener to the list of listeners for
36
	 * the given source.
37
	 * 
38
	 * @param source
39
	 *            the property source
40
	 * @param listener
41
	 *            the listener
42
	 */
43
	public void addSetChangeListener(Object source,
44
			ISetPropertyChangeListener listener);
45
46
	/**
47
	 * Removes the given set property change listener from the list of listeners
48
	 * for the given source.
49
	 * 
50
	 * @param source
51
	 *            the property source
52
	 * @param listener
53
	 *            the listener
54
	 */
55
	public void removeSetChangeListener(Object source,
56
			ISetPropertyChangeListener listener);
57
}
(-)src/org/eclipse/core/databinding/property/list/ListPropertyChangeEvent.java (+59 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.list;
13
14
import org.eclipse.core.databinding.observable.list.ListDiff;
15
import org.eclipse.core.databinding.property.IPropertyChangeListener;
16
import org.eclipse.core.databinding.property.PropertyChangeEvent;
17
import org.eclipse.core.runtime.Assert;
18
19
/**
20
 * List change event describing an incremental change of a list property on a
21
 * particular property source.
22
 * 
23
 * @since 1.2
24
 */
25
public class ListPropertyChangeEvent extends PropertyChangeEvent {
26
	private static final long serialVersionUID = 1L;
27
28
	/**
29
	 * The list property that changed
30
	 */
31
	public final IListProperty property;
32
33
	/**
34
	 * ListDiff enumerating the added and removed elements in the list.
35
	 */
36
	public final ListDiff diff;
37
38
	/**
39
	 * Constructs a ListPropertyChangeEvent with the given attributes
40
	 * 
41
	 * @param source
42
	 *            the property source
43
	 * @param property
44
	 *            the property that changed on the source
45
	 * @param diff
46
	 *            a ListDiff describing the changes to the list property
47
	 */
48
	public ListPropertyChangeEvent(Object source, IListProperty property,
49
			ListDiff diff) {
50
		super(source);
51
		this.property = property;
52
		Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$
53
		this.diff = diff;
54
	}
55
56
	protected void dispatch(IPropertyChangeListener listener) {
57
		((IListPropertyChangeListener) listener).handleListPropertyChange(this);
58
	}
59
}
(-)src/org/eclipse/core/internal/databinding/observable/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.observable;
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.set.ISetProperty;
25
import org.eclipse.core.databinding.property.set.ISetPropertyChangeListener;
26
import org.eclipse.core.databinding.property.set.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/map/MapPropertyChangeEvent.java (+59 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.map;
13
14
import org.eclipse.core.databinding.observable.map.MapDiff;
15
import org.eclipse.core.databinding.property.IPropertyChangeListener;
16
import org.eclipse.core.databinding.property.PropertyChangeEvent;
17
import org.eclipse.core.runtime.Assert;
18
19
/**
20
 * Map change event describing an incremental change of a map property on a
21
 * particular property source.
22
 * 
23
 * @since 1.2
24
 */
25
public class MapPropertyChangeEvent extends PropertyChangeEvent {
26
	private static final long serialVersionUID = 1L;
27
28
	/**
29
	 * The map property that changed
30
	 */
31
	public final IMapProperty property;
32
33
	/**
34
	 * MapDiff enumerating the added, changed, and removed entries in the map.
35
	 */
36
	public final MapDiff diff;
37
38
	/**
39
	 * Constructs a MapPropertyChangeEvent with the given attributes
40
	 * 
41
	 * @param source
42
	 *            the property source
43
	 * @param property
44
	 *            the property that changed on the source
45
	 * @param diff
46
	 *            a MapDiff describing the changes to the map property
47
	 */
48
	public MapPropertyChangeEvent(Object source, IMapProperty property,
49
			MapDiff diff) {
50
		super(source);
51
		this.property = property;
52
		Assert.isNotNull(diff, "diff cannot be null"); //$NON-NLS-1$
53
		this.diff = diff;
54
	}
55
56
	protected void dispatch(IPropertyChangeListener listener) {
57
		((IMapPropertyChangeListener) listener).handleMapPropertyChange(this);
58
	}
59
}
(-)src/org/eclipse/core/databinding/property/Property.java (+144 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
	private boolean disposed;
23
24
	private synchronized PropertyChangeSupport getChangeSupport() {
25
		if (changeSupport == null) {
26
			changeSupport = new PropertyChangeSupport() {
27
				protected void addListenerTo(Object source) {
28
					Property.this.addListenerTo(source);
29
				}
30
31
				protected void removeListenerFrom(Object source) {
32
					Property.this.removeListenerFrom(source);
33
				}
34
			};
35
		}
36
		return changeSupport;
37
	}
38
39
	/**
40
	 * Adds the listener to the list of listeners for the specified property
41
	 * source.
42
	 * 
43
	 * @param source
44
	 *            the property source
45
	 * @param listener
46
	 *            the listener to add
47
	 */
48
	protected final synchronized void addPropertyChangeListener(Object source,
49
			IPropertyChangeListener listener) {
50
		if (!isDisposed()) {
51
			getChangeSupport().addListener(source, listener);
52
		}
53
	}
54
55
	/**
56
	 * Removes the listener from the list of listeners for the specified
57
	 * property source.
58
	 * 
59
	 * @param source
60
	 *            the property source
61
	 * @param listener
62
	 *            the listener to remove
63
	 */
64
	protected final synchronized void removePropertyChangeListener(
65
			Object source, IPropertyChangeListener listener) {
66
		if (changeSupport != null) {
67
			changeSupport.removeListener(source, listener);
68
		}
69
	}
70
71
	/**
72
	 * Returns whether this property has listeners registered for the given
73
	 * property source
74
	 * 
75
	 * @param source
76
	 *            the property source
77
	 * @return whether this property has listeners registered for the given
78
	 *         property source
79
	 */
80
	protected final boolean hasListeners(Object source) {
81
		PropertyChangeSupport changeSupport = this.changeSupport;
82
		return changeSupport != null && changeSupport.hasListeners(source);
83
	}
84
85
	/**
86
	 * Fires a property change notification for all listeners registered on the
87
	 * property source indicated by the event object
88
	 * 
89
	 * @param event
90
	 *            the property change event
91
	 */
92
	protected void firePropertyChange(PropertyChangeEvent event) {
93
		PropertyChangeSupport changeSupport = this.changeSupport;
94
		if (changeSupport != null
95
				&& changeSupport.hasListeners(event.getSource())) {
96
			changeSupport.firePropertyChange(event);
97
		}
98
	}
99
100
	/**
101
	 * Adds a listener on the given property source for changes to the property.
102
	 * The listener should fire a property change event whenever the a property
103
	 * change is observed. This method is called when the first property change
104
	 * listener is added for the given source object.
105
	 * <p>
106
	 * This method does nothing if the source object has no listener support for
107
	 * the property.
108
	 * 
109
	 * @param source
110
	 *            the source object to observe for property changes.
111
	 * @see #removeListenerFrom(Object)
112
	 * @noreference This method is not intended to be referenced by clients.
113
	 */
114
	protected abstract void addListenerTo(Object source);
115
116
	/**
117
	 * Remove the listeners previous added on the property source for changes to
118
	 * the property. This method is called when the last property change
119
	 * listener is removed for the given source object.
120
	 * <p>
121
	 * This method does nothing if the source object has no listener support for
122
	 * the property.
123
	 * 
124
	 * @param source
125
	 *            the source object to stop observing for property changes.
126
	 * @see #addListenerTo(Object)
127
	 * @noreference This method is not intended to be referenced by clients.
128
	 */
129
	protected abstract void removeListenerFrom(Object source);
130
131
	public boolean isDisposed() {
132
		return disposed;
133
	}
134
135
	public synchronized void dispose() {
136
		if (!disposed) {
137
			if (changeSupport != null) {
138
				changeSupport.dispose();
139
				changeSupport = null;
140
			}
141
			disposed = true;
142
		}
143
	}
144
}
(-)src/org/eclipse/jface/tests/databinding/BindingTestSuite.java (-1 / +5 lines)
Lines 13-19 Link Here
13
 *     Ashley Cambrell - bugs 198903, 198904
13
 *     Ashley Cambrell - bugs 198903, 198904
14
 *     Matthew Hall - bugs 210115, 212468, 212223, 206839, 208858, 208322,
14
 *     Matthew Hall - bugs 210115, 212468, 212223, 206839, 208858, 208322,
15
 *                    212518, 215531, 221351, 184830, 213145, 218269, 239015,
15
 *                    212518, 215531, 221351, 184830, 213145, 218269, 239015,
16
 *                    237703, 237718, 222289, 247394, 233306, 247647
16
 *                    237703, 237718, 222289, 247394, 233306, 247647, 194734
17
 *     Ovidio Mallo - bug 237163, bug 235195
17
 *     Ovidio Mallo - bug 237163, bug 235195
18
 *******************************************************************************/
18
 *******************************************************************************/
19
package org.eclipse.jface.tests.databinding;
19
package org.eclipse.jface.tests.databinding;
Lines 64-69 Link Here
64
import org.eclipse.core.tests.databinding.observable.value.ComputedValueTest;
64
import org.eclipse.core.tests.databinding.observable.value.ComputedValueTest;
65
import org.eclipse.core.tests.databinding.observable.value.DecoratingObservableValueTest;
65
import org.eclipse.core.tests.databinding.observable.value.DecoratingObservableValueTest;
66
import org.eclipse.core.tests.databinding.observable.value.WritableValueTest;
66
import org.eclipse.core.tests.databinding.observable.value.WritableValueTest;
67
import org.eclipse.core.tests.databinding.property.PropertyTest;
67
import org.eclipse.core.tests.databinding.util.PolicyTest;
68
import org.eclipse.core.tests.databinding.util.PolicyTest;
68
import org.eclipse.core.tests.databinding.validation.MultiValidatorTest;
69
import org.eclipse.core.tests.databinding.validation.MultiValidatorTest;
69
import org.eclipse.core.tests.databinding.validation.ValidationStatusTest;
70
import org.eclipse.core.tests.databinding.validation.ValidationStatusTest;
Lines 256-261 Link Here
256
		addTest(DecoratingObservableValueTest.suite());
257
		addTest(DecoratingObservableValueTest.suite());
257
		addTest(WritableValueTest.suite());
258
		addTest(WritableValueTest.suite());
258
		
259
		
260
		//org.eclipse.core.tests.databinding.property
261
		addTestSuite(PropertyTest.class);
262
		
259
		//org.eclipse.core.tests.databinding.validation
263
		//org.eclipse.core.tests.databinding.validation
260
		addTestSuite(MultiValidatorTest.class);
264
		addTestSuite(MultiValidatorTest.class);
261
		addTestSuite(ValidationStatusTest.class);
265
		addTestSuite(ValidationStatusTest.class);
(-)src/org/eclipse/core/tests/databinding/property/PropertyTest.java (+185 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.tests.databinding.property;
13
14
import org.eclipse.core.databinding.property.IPropertyChangeListener;
15
import org.eclipse.core.databinding.property.Property;
16
import org.eclipse.core.databinding.property.PropertyChangeEvent;
17
18
import junit.framework.TestCase;
19
20
/**
21
 * @since 3.2
22
 * 
23
 */
24
public class PropertyTest extends TestCase {
25
	private Source source;
26
	private SourceProperty property;
27
	private ChangeListener listener;
28
29
	protected void setUp() throws Exception {
30
		source = new Source();
31
		property = new SourceProperty();
32
		listener = new ChangeListener();
33
	}
34
35
	public void testAddListenerTo() {
36
		assertFalse(source.hasListener());
37
		property.addChangeListener(source, listener);
38
		assertTrue(source.hasListener());
39
40
		assertEquals(0, listener.count);
41
42
		source.change();
43
44
		assertEquals(1, listener.count);
45
		assertEquals(new ChangeEvent(source, "Version 1"), listener.event);
46
	}
47
48
	public void testAddListenerTo_MultipleListeners() {
49
		assertFalse(source.hasListener());
50
51
		property.addChangeListener(source, listener);
52
53
		assertTrue(source.hasListener());
54
55
		ChangeListener listener2 = new ChangeListener();
56
		property.addChangeListener(source, listener2);
57
58
		assertTrue(source.hasListener());
59
60
		property.removeChangeListener(source, listener);
61
62
		assertTrue(source.hasListener());
63
64
		property.removeChangeListener(source, listener2);
65
66
		assertFalse(source.hasListener());
67
	}
68
69
	public void testRemoveListenerFrom() {
70
		property.addChangeListener(source, listener);
71
		assertTrue(source.hasListener());
72
		property.removeChangeListener(source, listener);
73
		assertFalse(source.hasListener());
74
75
		source.change();
76
77
		assertEquals(0, listener.count);
78
	}
79
80
	public void testIsDisposed() {
81
		assertFalse(property.isDisposed());
82
		property.dispose();
83
		assertTrue(property.isDisposed());
84
	}
85
86
	public void testDispose() {
87
		property.addChangeListener(source, listener);
88
		assertTrue(source.hasListener());
89
		property.dispose();
90
		assertFalse(source.hasListener());
91
	}
92
93
	static class Source {
94
		private int version = 0;
95
96
		private ISourceListener listener;
97
98
		void change() {
99
			++version;
100
			if (listener != null)
101
				listener.notify(this, "Version " + version);
102
		}
103
104
		boolean hasListener() {
105
			return listener != null;
106
		}
107
	}
108
109
	interface ISourceListener {
110
		void notify(Object source, String message);
111
	}
112
113
	static class SourceProperty extends Property {
114
		private ISourceListener listener = new ISourceListener() {
115
			public void notify(Object source, String message) {
116
				firePropertyChange(new ChangeEvent(source, message));
117
			}
118
		};
119
120
		public void addChangeListener(Object source, IChangeListener listener) {
121
			addPropertyChangeListener(source, listener);
122
		}
123
124
		public void removeChangeListener(Object source, IChangeListener listener) {
125
			removePropertyChangeListener(source, listener);
126
		}
127
128
		protected void addListenerTo(Object source) {
129
			((Source) source).listener = listener;
130
		}
131
132
		protected void removeListenerFrom(Object source) {
133
			((Source) source).listener = null;
134
		}
135
136
		void change(Object source) {
137
			((Source) source).change();
138
		}
139
	}
140
141
	static interface IChangeListener extends IPropertyChangeListener {
142
		void handlePropertyStubChange(ChangeEvent event);
143
	}
144
145
	static class ChangeListener implements IChangeListener {
146
		int count = 0;
147
		ChangeEvent event;
148
149
		public void handlePropertyStubChange(ChangeEvent event) {
150
			count++;
151
			this.event = event;
152
		}
153
	}
154
155
	static class ChangeEvent extends PropertyChangeEvent {
156
		private static final long serialVersionUID = 1L;
157
158
		final String message;
159
160
		ChangeEvent(Object source, String message) {
161
			super(source);
162
			this.message = message;
163
		}
164
165
		protected void dispatch(IPropertyChangeListener listener) {
166
			((IChangeListener) listener).handlePropertyStubChange(this);
167
		}
168
169
		public boolean equals(Object obj) {
170
			if (obj == this)
171
				return true;
172
			if (!super.equals(obj))
173
				return false;
174
175
			ChangeEvent that = (ChangeEvent) obj;
176
			return message.equals(that.message);
177
		}
178
179
		public int hashCode() {
180
			int hash = super.hashCode();
181
			hash = hash * 37 + message.hashCode();
182
			return hash;
183
		}
184
	}
185
}

Return to bug 194734