Skip to main content
summaryrefslogtreecommitdiffstats
blob: 27c1330ed001d2d79445db5b805c3fe9d8651c5b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*******************************************************************************
 * Copyright (c) 2006, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Brad Reynolds - bug 164653
 *     Matthew Hall - bugs 118516, 146397, 226289, 246103, 249526, 264307
 *******************************************************************************/

package org.eclipse.core.databinding.observable.map;

import java.util.AbstractMap;

import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.ChangeSupport;
import org.eclipse.core.databinding.observable.DisposeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.IDisposeListener;
import org.eclipse.core.databinding.observable.IStaleListener;
import org.eclipse.core.databinding.observable.ObservableTracker;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.StaleEvent;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.AssertionFailedException;

/**
 * 
 * <p>
 * This class is thread safe. All state accessing methods must be invoked from
 * the {@link Realm#isCurrent() current realm}. Methods for adding and removing
 * listeners may be invoked from any thread.
 * </p>
 * 
 * @since 1.0
 */
public abstract class AbstractObservableMap extends AbstractMap implements
		IObservableMap {

	private final class PrivateChangeSupport extends ChangeSupport {
		private PrivateChangeSupport(Realm realm) {
			super(realm);
		}

		protected void firstListenerAdded() {
			AbstractObservableMap.this.firstListenerAdded();
		}

		protected void lastListenerRemoved() {
			AbstractObservableMap.this.lastListenerRemoved();
		}

		protected boolean hasListeners() {
			return super.hasListeners();
		}
	}

	private PrivateChangeSupport changeSupport;
	private boolean disposed = false;

	private boolean stale;

	/**
	 */
	public AbstractObservableMap() {
		this(Realm.getDefault());
	}

	/**
	 * 
	 */
	protected void lastListenerRemoved() {
	}

	/**
	 * 
	 */
	protected void firstListenerAdded() {
	}

	/**
	 * @param realm
	 */
	public AbstractObservableMap(Realm realm) {
		Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$
		ObservableTracker.observableCreated(this);
		changeSupport = new PrivateChangeSupport(realm);
	}

	public synchronized void addMapChangeListener(IMapChangeListener listener) {
		if (!disposed)
			changeSupport.addListener(MapChangeEvent.TYPE, listener);
	}

	public synchronized void removeMapChangeListener(IMapChangeListener listener) {
		if (!disposed)
			changeSupport.removeListener(MapChangeEvent.TYPE, listener);
	}

	public synchronized void addChangeListener(IChangeListener listener) {
		if (!disposed)
			changeSupport.addChangeListener(listener);
	}

	public synchronized void addStaleListener(IStaleListener listener) {
		if (!disposed)
			changeSupport.addStaleListener(listener);
	}

	/**
	 * @return whether the observable map has listeners registered
	 * @since 1.2
	 */
	protected synchronized boolean hasListeners() {
		return !disposed && changeSupport.hasListeners();
	}

	/**
	 * @since 1.2
	 */
	public void addDisposeListener(IDisposeListener listener) {
		if (!disposed)
			changeSupport.addDisposeListener(listener);
	}

	/**
	 * @since 1.2
	 */
	public void removeDisposeListener(IDisposeListener listener) {
		if (!disposed)
			changeSupport.removeDisposeListener(listener);
	}

	/**
	 * @since 1.2
	 */
	public boolean isDisposed() {
		return disposed;
	}

	public synchronized void dispose() {
		if (!disposed) {
			disposed = true;
			changeSupport.fireEvent(new DisposeEvent(this));
			changeSupport.dispose();
			changeSupport = null;
		}
	}

	public Realm getRealm() {
		return changeSupport.getRealm();
	}

	public boolean isStale() {
		checkRealm();
		return stale;
	}

	/**
	 * @since 1.2
	 */
	public Object getKeyType() {
		return null;
	}

	/**
	 * @since 1.2
	 */
	public Object getValueType() {
		return null;
	}

	public synchronized void removeChangeListener(IChangeListener listener) {
		changeSupport.removeChangeListener(listener);
	}

	public synchronized void removeStaleListener(IStaleListener listener) {
		changeSupport.removeStaleListener(listener);
	}

	/**
	 * Sets the stale state. Must be invoked from the current realm.
	 * 
	 * @param stale
	 */
	public void setStale(boolean stale) {
		checkRealm();
		this.stale = stale;
		if (stale) {
			fireStale();
		}
	}

	/**
	 * Fires stale events. Must be invoked from current realm.
	 */
	protected void fireStale() {
		checkRealm();
		changeSupport.fireEvent(new StaleEvent(this));
	}

	/**
	 * Fires change events. Must be invoked from current realm.
	 */
	protected void fireChange() {
		checkRealm();
		changeSupport.fireEvent(new ChangeEvent(this));
	}

	/**
	 * Fires map change events. Must be invoked from current realm.
	 * 
	 * @param diff
	 */
	protected void fireMapChange(MapDiff diff) {
		checkRealm();
		fireChange();
		changeSupport.fireEvent(new MapChangeEvent(this, diff));
	}

	/**
	 * Asserts that the realm is the current realm.
	 * 
	 * @see Realm#isCurrent()
	 * @throws AssertionFailedException
	 *             if the realm is not the current realm
	 */
	protected void checkRealm() {
		Assert.isTrue(getRealm().isCurrent(),
				"This operation must be run within the observable's realm"); //$NON-NLS-1$
	}
}

Back to the top