Skip to main content
summaryrefslogtreecommitdiffstats
blob: 710834c0e3f197111fdcbfc6569ce5b4fc95fb23 (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
/*******************************************************************************
 * 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.utility.internal.model.value.swing;

import java.util.Calendar;
import java.util.Date;
import javax.swing.SpinnerDateModel;
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.SpinnerDateModel can be used to keep a ChangeListener
 * (e.g. a JSpinner) in synch with a PropertyValueModel that holds a date.
 * 
 * This class must be a sub-class of SpinnerDateModel because of some
 * crappy jdk code....  ~bjv
 * @see javax.swing.JSpinner#createEditor(javax.swing.SpinnerModel)
 * 
 * If this class needs to be modified, it would behoove us to review the
 * other, similar classes:
 * @see ListSpinnerModelAdapter
 * @see NumberSpinnerModelAdapter
 */
public class DateSpinnerModelAdapter
	extends SpinnerDateModel
{

	/**
	 * The default spinner value; used when the underlying model date value is null.
	 * The default is the current date.
	 */
	private final Date defaultValue;

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

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


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

	/**
	 * Constructor - the date holder is required.
	 * The default spinner value is the current date.
	 */
	public DateSpinnerModelAdapter(WritablePropertyValueModel<Object> dateHolder) {
		this(dateHolder, new Date());
	}

	/**
	 * Constructor - the date holder and default value are required.
	 */
	public DateSpinnerModelAdapter(WritablePropertyValueModel<Object> dateHolder, Date defaultValue) {
		this(dateHolder, null, null, Calendar.DAY_OF_MONTH, defaultValue);
	}

	/**
	 * Constructor - the date holder is required.
	 * The default spinner value is the current date.
	 */
	public DateSpinnerModelAdapter(WritablePropertyValueModel<Object> dateHolder, Comparable<?> start, Comparable<?> end, int calendarField) {
		this(dateHolder, start, end, calendarField, new Date());
	}

	/**
	 * Constructor - the date holder is required.
	 */
	public DateSpinnerModelAdapter(WritablePropertyValueModel<Object> dateHolder, Comparable<?> start, Comparable<?> end, int calendarField, Date defaultValue) {
		super(dateHolder.getValue() == null ? defaultValue : (Date) dateHolder.getValue(), start, end, calendarField);
		this.dateHolder = dateHolder;
		this.dateChangeListener = this.buildDateChangeListener();
		// postpone listening to the underlying date
		// until we have listeners ourselves...
		this.defaultValue = defaultValue;
	}


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

	protected PropertyChangeListener buildDateChangeListener() {
		return new AWTPropertyChangeListenerWrapper(this.buildDateChangeListener_());
	}

	protected PropertyChangeListener buildDateChangeListener_() {
		return new PropertyChangeListener() {
			public void propertyChanged(PropertyChangeEvent event) {
				DateSpinnerModelAdapter.this.synchronize(event.getNewValue());
			}
			@Override
			public String toString() {
				return "date 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.dateHolder.getValue()));
		}
		return super.getValue();
	}

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

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

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


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

	protected Date 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 date 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.dateHolder);
	}

}

Back to the top