Skip to main content
summaryrefslogtreecommitdiffstats
blob: 6cd893d878f3d3192bf19fa7484d1ade41ca3595 (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
/*******************************************************************************
 * Copyright (c) 2007, 2008 Oracle. 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:
 *     Oracle - initial API and implementation
 ******************************************************************************/
package org.eclipse.jpt.utility.internal.model.value.swing;

import javax.swing.AbstractSpinnerModel;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.eclipse.jpt.utility.internal.StringTools;
import org.eclipse.jpt.utility.internal.model.listener.awt.AWTPropertyChangeListenerWrapper;
import org.eclipse.jpt.utility.model.event.PropertyChangeEvent;
import org.eclipse.jpt.utility.model.listener.PropertyChangeListener;
import org.eclipse.jpt.utility.model.value.PropertyValueModel;
import org.eclipse.jpt.utility.model.value.WritablePropertyValueModel;

/**
 * This javax.swing.SpinnerModel can be used to keep a ChangeListener
 * (e.g. a JSpinner) in synch with a PropertyValueModel that holds a value.
 * 
 * Note: it is likely you want to use one of the following classes instead of
 * this one:
 *     DateSpinnerModelAdapter
 *     NumberSpinnerModelAdapter
 *     ListSpinnerModelAdapter
 * 
 * NB: This model should only be used for values that have a fairly
 * inexpensive #equals() implementation.
 * @see #synchronizeDelegate(Object)
 */
public class SpinnerModelAdapter
	extends AbstractSpinnerModel
{
	/** The delegate spinner model whose behavior we "enhance". */
	protected final SpinnerModel delegate;

	/** A listener that allows us to forward any changes made to the delegate spinner model. */
	protected final ChangeListener delegateListener;

	/** A value model on the underlying value. */
	protected final WritablePropertyValueModel<Object> valueHolder;

	/** A listener that allows us to synchronize with changes made to the underlying value. */
	protected final PropertyChangeListener valueListener;


	// ********** constructors **********

	/**
	 * Constructor - the value holder and delegate are required.
	 */
	public SpinnerModelAdapter(WritablePropertyValueModel<Object> valueHolder, SpinnerModel delegate) {
		super();
		if (valueHolder == null || delegate == null) {
			throw new NullPointerException();
		}
		this.valueHolder = valueHolder;
		this.delegate = delegate;
		// postpone listening to the underlying value
		// until we have listeners ourselves...
		this.valueListener = this.buildValueListener();
		this.delegateListener = this.buildDelegateListener();
	}

	/**
	 * Constructor - the value holder is required.
	 * This will wrap a simple number spinner model.
	 */
	public SpinnerModelAdapter(WritablePropertyValueModel<Object> valueHolder) {
		this(valueHolder, new SpinnerNumberModel());
	}


	// ********** initialization **********

	protected PropertyChangeListener buildValueListener() {
		return new AWTPropertyChangeListenerWrapper(this.buildValueListener_());
	}

	protected PropertyChangeListener buildValueListener_() {
		return new PropertyChangeListener() {
			public void propertyChanged(PropertyChangeEvent event) {
				SpinnerModelAdapter.this.valueChanged(event);
			}
			@Override
			public String toString() {
				return "value listener";
			}
		};
	}

	/**
	 * expand access a bit for inner class
	 */
	@Override
	protected void fireStateChanged() {
		super.fireStateChanged();
	}

	protected ChangeListener buildDelegateListener() {
		return new ChangeListener() {
			public void stateChanged(ChangeEvent event) {
				// forward the event, with this as the source
				SpinnerModelAdapter.this.fireStateChanged();
			}
			@Override
			public String toString() {
				return "delegate listener";
			}
		};
	}


	// ********** SpinnerModel implementation **********

	public Object getValue() {
		return this.delegate.getValue();
	}

	/**
	 * Extend to update the underlying value directly.
	 * The resulting event will be ignored: @see #synchronizeDelegate(Object).
	 */
	public void setValue(Object value) {
		this.delegate.setValue(value);
		this.valueHolder.setValue(value);
	}

	public Object getNextValue() {
		return this.delegate.getNextValue();
	}

	public Object getPreviousValue() {
		return this.delegate.getPreviousValue();
	}

	/**
	 * Extend to start listening to the underlying value if necessary.
	 */
    @Override
	public void addChangeListener(ChangeListener listener) {
		if (this.listenerList.getListenerCount(ChangeListener.class) == 0) {
			this.delegate.addChangeListener(this.delegateListener);
			this.engageValueHolder();
		}
		super.addChangeListener(listener);
	}

	/**
	 * Extend to stop listening to the underlying value if appropriate.
	 */
    @Override
	public void removeChangeListener(ChangeListener listener) {
		super.removeChangeListener(listener);
		if (this.listenerList.getListenerCount(ChangeListener.class) == 0) {
			this.disengageValueHolder();
			this.delegate.removeChangeListener(this.delegateListener);
		}
	}


	// ********** behavior **********

	/**
	 * A third party has modified the underlying value.
	 * Synchronize the delegate model accordingly.
	 */
	protected void valueChanged(PropertyChangeEvent event) {
		this.synchronizeDelegate(event.getNewValue());
	}

	/**
	 * Set the delegate's value if it has changed.
	 */
	protected void synchronizeDelegate(Object value) {
		// check to see whether the delegate has already been synchronized
		// (via #setValue())
		if ( ! this.delegate.getValue().equals(value)) {
			this.delegate.setValue(value);
		}
	}

	protected void engageValueHolder() {
		this.valueHolder.addPropertyChangeListener(PropertyValueModel.VALUE, this.valueListener);
		this.synchronizeDelegate(this.valueHolder.getValue());
	}

	protected void disengageValueHolder() {
		this.valueHolder.removePropertyChangeListener(PropertyValueModel.VALUE, this.valueListener);
	}


	// ********** standard methods **********

	@Override
	public String toString() {
		return StringTools.buildToStringFor(this, this.valueHolder);
	}

}

Back to the top