Skip to main content
summaryrefslogtreecommitdiffstats
blob: af3e0b0187cd61dc5c5141f6d983f9e33e7e0c06 (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
/*******************************************************************************
 * Copyright (c) 2007, 2010 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.common.utility.internal.model.value.swing;

import java.util.Arrays;
import java.util.List;
import javax.swing.SpinnerListModel;
import javax.swing.event.ChangeListener;
import org.eclipse.jpt.common.utility.internal.StringTools;
import org.eclipse.jpt.common.utility.internal.model.listener.awt.AWTPropertyChangeListenerWrapper;
import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent;
import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener;
import org.eclipse.jpt.common.utility.model.value.PropertyValueModel;
import org.eclipse.jpt.common.utility.model.value.WritablePropertyValueModel;

/**
 * This javax.swing.SpinnerListModel can be used to keep a ChangeListener
 * (e.g. a JSpinner) in synch with a PropertyValueModel that holds a value
 * in the list.
 * 
 * This class must be a sub-class of SpinnerListModel because of some
 * crappy jdk code....  ~bjv
 * @see javax.swing.JSpinner#createEditor(javax.swing.SpinnerModel)
 * 
 * NB: This model should only be used for values that have a reasonably
 * inexpensive #equals() implementation.
 * @see #synchronize(Object)
 * 
 * If this class needs to be modified, it would behoove us to review the
 * other, similar classes:
 * @see DateSpinnerModelAdapter
 * @see NumberSpinnerModelAdapter
 */
public class ListSpinnerModelAdapter
	extends SpinnerListModel
{

	/**
	 * The default spinner value; used when the underlying model value is null.
	 * The default is the first item on the list.
	 */
	private final Object defaultValue;

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

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


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

	/**
	 * Constructor - the value holder is required.
	 * Use the model value itself as the default spinner value.
	 */
	public ListSpinnerModelAdapter(WritablePropertyValueModel<Object> valueHolder) {
		this(valueHolder, valueHolder.getValue());
	}

	/**
	 * Constructor - the value holder is required.
	 */
	public ListSpinnerModelAdapter(WritablePropertyValueModel<Object> valueHolder, Object defaultValue) {
		this(valueHolder, new Object[] {defaultValue}, defaultValue);
	}

	/**
	 * Constructor - the value holder is required.
	 * Use the first item in the list of values as the default spinner value.
	 */
	public ListSpinnerModelAdapter(WritablePropertyValueModel<Object> valueHolder, Object[] values) {
		this(valueHolder, values, values[0]);
	}

	/**
	 * Constructor - the value holder is required.
	 */
	public ListSpinnerModelAdapter(WritablePropertyValueModel<Object> valueHolder, Object[] values, Object defaultValue) {
		this(valueHolder, Arrays.asList(values), defaultValue);
	}

	/**
	 * Constructor - the value holder is required.
	 * Use the first item in the list of values as the default spinner value.
	 */
	public ListSpinnerModelAdapter(WritablePropertyValueModel<Object> valueHolder, List<Object> values) {
		this(valueHolder, values, values.get(0));
	}

	/**
	 * Constructor - the value holder is required.
	 */
	public ListSpinnerModelAdapter(WritablePropertyValueModel<Object> valueHolder, List<Object> values, Object defaultValue) {
		super(values);
		this.valueHolder = valueHolder;
		this.valueChangeListener = this.buildValueChangeListener();
		// postpone listening to the underlying value
		// until we have listeners ourselves...
		this.defaultValue = defaultValue;
	}


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

	protected PropertyChangeListener buildValueChangeListener() {
		return new AWTPropertyChangeListenerWrapper(this.buildValueChangeListener_());
	}

	protected PropertyChangeListener buildValueChangeListener_() {
		return new PropertyChangeListener() {
			public void propertyChanged(PropertyChangeEvent event) {
				ListSpinnerModelAdapter.this.synchronize(event.getNewValue());
			}
			@Override
			public String toString() {
				return "value listener"; //$NON-NLS-1$
			}
		};
	}


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

	/**
	 * Extend to check whether this method is being called before we 
	 * have any listeners.
	 * This is necessary because some crappy jdk code gets the value
	 * from the model *before* listening to the model.  ~bjv
	 * @see javax.swing.JSpinner.DefaultEditor(javax.swing.JSpinner)
	 */
    @Override
	public Object getValue() {
		if (this.getChangeListeners().length == 0) {
			// sorry about this "lateral" call to super  ~bjv
			super.setValue(this.spinnerValueOf(this.valueHolder.getValue()));
		}
		return super.getValue();
	}

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

	/**
	 * Extend to start listening to the underlying value if necessary.
	 */
    @Override
	public void addChangeListener(ChangeListener listener) {
		if (this.getChangeListeners().length == 0) {
			this.valueHolder.addPropertyChangeListener(PropertyValueModel.VALUE, this.valueChangeListener);
			this.synchronize(this.valueHolder.getValue());
		}
		super.addChangeListener(listener);
	}

	/**
	 * Extend to stop listening to the underlying value if appropriate.
	 */
    @Override
	public void removeChangeListener(ChangeListener listener) {
		super.removeChangeListener(listener);
		if (this.getChangeListeners().length == 0) {
			this.valueHolder.removePropertyChangeListener(PropertyValueModel.VALUE, this.valueChangeListener);
		}
	}


	// ********** queries **********

	protected Object getDefaultValue() {
		return this.defaultValue;
	}

	/**
	 * Convert to a non-null value.
	 */
	protected Object spinnerValueOf(Object value) {
		return (value == null) ? this.getDefaultValue() : value;
	}


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

	/**
	 * Set the spinner value if it has changed.
	 */
	void synchronize(Object value) {
		Object newValue = this.spinnerValueOf(value);
		// check to see whether the spinner value has already been synchronized
		// (via #setValue())
		if ( ! this.getValue().equals(newValue)) {
			this.setValue(newValue);
		}
	}


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

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

}

Back to the top