View Javadoc
1   /**
2    * Copyright 2004 Juan Heyns. All rights reserved.
3    * <p/>
4    * Redistribution and use in source and binary forms, with or without modification, are
5    * permitted provided that the following conditions are met:
6    * <p/>
7    * 1. Redistributions of source code must retain the above copyright notice, this list of
8    * conditions and the following disclaimer.
9    * <p/>
10   * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11   * of conditions and the following disclaimer in the documentation and/or other materials
12   * provided with the distribution.
13   * <p/>
14   * THIS SOFTWARE IS PROVIDED BY JUAN HEYNS ``AS IS'' AND ANY EXPRESS OR IMPLIED
15   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JUAN HEYNS OR
17   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23   * <p/>
24   * The views and conclusions contained in the software and documentation are those of the
25   * authors and should not be interpreted as representing official policies, either expressed
26   * or implied, of Juan Heyns.
27   */
28  package fr.ifremer.quadrige3.ui.swing.component.date;
29  
30  /*
31   * #%L
32   * Reef DB :: UI
33   * $Id:$
34   * $HeadURL:$
35   * %%
36   * Copyright (C) 2014 - 2015 Ifremer
37   * %%
38   * This program is free software: you can redistribute it and/or modify
39   * it under the terms of the GNU Affero General Public License as published by
40   * the Free Software Foundation, either version 3 of the License, or
41   * (at your option) any later version.
42   * 
43   * This program is distributed in the hope that it will be useful,
44   * but WITHOUT ANY WARRANTY; without even the implied warranty of
45   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
46   * GNU General Public License for more details.
47   * 
48   * You should have received a copy of the GNU Affero General Public License
49   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
50   * #L%
51   */
52  
53  
54  import javax.swing.event.ChangeEvent;
55  import javax.swing.event.ChangeListener;
56  import java.beans.PropertyChangeEvent;
57  import java.beans.PropertyChangeListener;
58  import java.util.Calendar;
59  import java.util.Date;
60  import java.util.HashSet;
61  import java.util.Set;
62  
63  /**
64   * Created 18 April 2010
65   * Updated 26 April 2010
66   *
67   * @author Juan Heyns
68   */
69  public class DateModel {
70  
71      /** Constant <code>PROPERTY_YEAR="year"</code> */
72      public static final String PROPERTY_YEAR = "year";
73      /** Constant <code>PROPERTY_MONTH="month"</code> */
74      public static final String PROPERTY_MONTH = "month";
75      /** Constant <code>PROPERTY_DAY="day"</code> */
76      public static final String PROPERTY_DAY = "day";
77      /** Constant <code>PROPERTY_VALUE="value"</code> */
78      public static final String PROPERTY_VALUE = "value";
79      /** Constant <code>PROPERTY_SELECTED="selected"</code> */
80      public static final String PROPERTY_SELECTED = "selected";
81  
82      private boolean modelAdjusting;
83      private boolean selected;
84      private Date date;
85      private Calendar calendarValue;
86      private final Set<ChangeListener> changeListeners;
87      private final Set<PropertyChangeListener> propertyChangeListeners;
88  
89      /**
90       * <p>Constructor for DateModel.</p>
91       *
92       * @param dateValue a {@link java.util.Date} object.
93       */
94      public DateModel(Date dateValue) {
95          this();
96          setDate(dateValue);
97      }
98  
99      /**
100      * <p>Constructor for DateModel.</p>
101      *
102      * @param calendarValue a {@link java.util.Calendar} object.
103      */
104     public DateModel(Calendar calendarValue) {
105         this();
106         setDate(fromCalendar(calendarValue));
107     }
108 
109     /**
110      * <p>Constructor for DateModel.</p>
111      */
112     public DateModel() {
113         changeListeners = new HashSet<>();
114         propertyChangeListeners = new HashSet<>();
115         selected = false;
116         calendarValue = Calendar.getInstance();
117     }
118 
119     /**
120      * <p>addChangeListener.</p>
121      *
122      * @param changeListener a {@link javax.swing.event.ChangeListener} object.
123      */
124     public synchronized void addChangeListener(ChangeListener changeListener) {
125         changeListeners.add(changeListener);
126     }
127 
128     /**
129      * <p>removeChangeListener.</p>
130      *
131      * @param changeListener a {@link javax.swing.event.ChangeListener} object.
132      */
133     public synchronized void removeChangeListener(ChangeListener changeListener) {
134         changeListeners.remove(changeListener);
135     }
136 
137     /**
138      * <p>fireChangeEvent.</p>
139      */
140     protected synchronized void fireChangeEvent() {
141         if (modelAdjusting) {
142             return;
143         }
144         modelAdjusting = true;
145         for (ChangeListener changeListener : changeListeners) {
146             changeListener.stateChanged(new ChangeEvent(this));
147         }
148         modelAdjusting = false;
149     }
150 
151     /**
152      * <p>addPropertyChangeListener.</p>
153      *
154      * @param listener a {@link java.beans.PropertyChangeListener} object.
155      */
156     public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
157         propertyChangeListeners.add(listener);
158     }
159 
160     /**
161      * <p>removePropertyChangeListener.</p>
162      *
163      * @param listener a {@link java.beans.PropertyChangeListener} object.
164      */
165     public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
166         propertyChangeListeners.remove(listener);
167     }
168 
169     /**
170      * <p>firePropertyChange.</p>
171      *
172      * @param propertyName a {@link java.lang.String} object.
173      * @param oldValue a {@link java.lang.Object} object.
174      * @param newValue a {@link java.lang.Object} object.
175      */
176     protected synchronized void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
177         if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
178             return;
179         }
180 
181         for (PropertyChangeListener listener : propertyChangeListeners) {
182             listener.propertyChange(new PropertyChangeEvent(this, propertyName, oldValue, newValue));
183         }
184     }
185 
186     /**
187      * <p>getDay.</p>
188      *
189      * @return a int.
190      */
191     public int getDay() {
192         return calendarValue.get(Calendar.DATE);
193     }
194 
195     /**
196      * <p>getMonth.</p>
197      *
198      * @return a int.
199      */
200     public int getMonth() {
201         return calendarValue.get(Calendar.MONTH);
202     }
203 
204     /**
205      * <p>getYear.</p>
206      *
207      * @return a int.
208      */
209     public int getYear() {
210         return calendarValue.get(Calendar.YEAR);
211     }
212 
213     /**
214      * <p>Getter for the field <code>date</code>.</p>
215      *
216      * @return a {@link java.util.Date} object.
217      */
218     public Date getDate() {
219         if (!selected) {
220             return null;
221         }
222         return date;
223     }
224 
225     /**
226      * <p>setDay.</p>
227      *
228      * @param day a int.
229      */
230     public void setDay(int day) {
231         int oldDayValue = this.calendarValue.get(Calendar.DATE);
232         calendarValue.set(Calendar.DATE, day);
233         fireChangeEvent();
234         firePropertyChange(PROPERTY_DAY, oldDayValue, this.calendarValue.get(Calendar.DATE));
235         updateDateFromCalendar();
236     }
237 
238     /**
239      * <p>addDay.</p>
240      *
241      * @param add a int.
242      */
243     public void addDay(int add) {
244         int oldDayValue = this.calendarValue.get(Calendar.DATE);
245         calendarValue.add(Calendar.DATE, add);
246         fireChangeEvent();
247         firePropertyChange(PROPERTY_DAY, oldDayValue, this.calendarValue.get(Calendar.DATE));
248     }
249 
250     /**
251      * <p>setMonth.</p>
252      *
253      * @param month a int.
254      */
255     public void setMonth(int month) {
256         int oldYearValue = this.calendarValue.get(Calendar.YEAR);
257         int oldMonthValue = this.calendarValue.get(Calendar.MONTH);
258         int oldDayValue = this.calendarValue.get(Calendar.DAY_OF_MONTH);
259 
260         Calendar newVal = Calendar.getInstance();
261         newVal.set(Calendar.DAY_OF_MONTH, 1);
262         newVal.set(Calendar.MONTH, month);
263         newVal.set(Calendar.YEAR, oldYearValue);
264 
265         if (newVal.getActualMaximum(Calendar.DAY_OF_MONTH) <= oldDayValue) {
266             newVal.set(Calendar.DAY_OF_MONTH,
267                     newVal.getActualMaximum(Calendar.DAY_OF_MONTH));
268         } else {
269             newVal.set(Calendar.DAY_OF_MONTH, oldDayValue);
270         }
271 
272         calendarValue.set(Calendar.MONTH, newVal.get(Calendar.MONTH));
273         calendarValue.set(Calendar.DAY_OF_MONTH, newVal.get(Calendar.DAY_OF_MONTH));
274 
275         fireChangeEvent();
276         firePropertyChange(PROPERTY_MONTH, oldMonthValue, this.calendarValue.get(Calendar.MONTH));
277         // only fire change event when day actually changed
278         if (this.calendarValue.get(Calendar.DAY_OF_MONTH) != oldDayValue) {
279             firePropertyChange(PROPERTY_DAY, oldDayValue,
280                     this.calendarValue.get(Calendar.DAY_OF_MONTH));
281         }
282     }
283 
284     /**
285      * <p>addMonth.</p>
286      *
287      * @param add a int.
288      */
289     public void addMonth(int add) {
290         int oldMonthValue = this.calendarValue.get(Calendar.MONTH);
291         calendarValue.add(Calendar.MONTH, add);
292         fireChangeEvent();
293         firePropertyChange(PROPERTY_MONTH, oldMonthValue, this.calendarValue.get(Calendar.MONTH));
294     }
295 
296     /**
297      * <p>setYear.</p>
298      *
299      * @param year a int.
300      */
301     public void setYear(int year) {
302         int oldYearValue = this.calendarValue.get(Calendar.YEAR);
303         int oldMonthValue = this.calendarValue.get(Calendar.MONTH);
304         int oldDayValue = this.calendarValue.get(Calendar.DAY_OF_MONTH);
305 
306         Calendar newVal = Calendar.getInstance();
307         newVal.set(Calendar.DAY_OF_MONTH, 1);
308         newVal.set(Calendar.MONTH, oldMonthValue);
309         newVal.set(Calendar.YEAR, year);
310 
311         if (newVal.getActualMaximum(Calendar.DAY_OF_MONTH) <= oldDayValue) {
312             newVal.set(Calendar.DAY_OF_MONTH,
313                     newVal.getActualMaximum(Calendar.DAY_OF_MONTH));
314         } else {
315             newVal.set(Calendar.DAY_OF_MONTH, oldDayValue);
316         }
317 
318         calendarValue.set(Calendar.YEAR, newVal.get(Calendar.YEAR));
319         calendarValue.set(Calendar.DAY_OF_MONTH, newVal.get(Calendar.DAY_OF_MONTH));
320 
321         fireChangeEvent();
322         firePropertyChange(PROPERTY_YEAR, oldYearValue, this.calendarValue.get(Calendar.YEAR));
323         // only fire change event when day actually changed
324         if (this.calendarValue.get(Calendar.DAY_OF_MONTH) != oldDayValue) {
325             firePropertyChange(PROPERTY_DAY, oldDayValue,
326                     this.calendarValue.get(Calendar.DAY_OF_MONTH));
327         }
328     }
329 
330     /**
331      * <p>addYear.</p>
332      *
333      * @param add a int.
334      */
335     public void addYear(int add) {
336         int oldYearValue = this.calendarValue.get(Calendar.YEAR);
337         calendarValue.add(Calendar.YEAR, add);
338         fireChangeEvent();
339         firePropertyChange(PROPERTY_YEAR, oldYearValue, this.calendarValue.get(Calendar.YEAR));
340     }
341 
342     /**
343      * <p>Setter for the field <code>date</code>.</p>
344      *
345      * @param value a {@link java.util.Date} object.
346      */
347     public void setDate(Date value) {
348         int oldYearValue = this.calendarValue.get(Calendar.YEAR);
349         int oldMonthValue = this.calendarValue.get(Calendar.MONTH);
350         int oldDayValue = this.calendarValue.get(Calendar.DATE);
351         boolean oldSelectedValue = isSelected();
352 
353         selected = value != null;
354 
355         if (value == null) {
356             value = new Date();
357         }
358 
359         this.calendarValue = toCalendar(value);
360         setToMidnight();
361 
362         fireChangeEvent();
363         firePropertyChange(PROPERTY_YEAR, oldYearValue, this.calendarValue.get(Calendar.YEAR));
364         firePropertyChange(PROPERTY_MONTH, oldMonthValue, this.calendarValue.get(Calendar.MONTH));
365         firePropertyChange(PROPERTY_DAY, oldDayValue, this.calendarValue.get(Calendar.DATE));
366         updateDateFromCalendar();
367         firePropertyChange(PROPERTY_SELECTED, oldSelectedValue, this.selected);
368     }
369 
370     /**
371      * <p>Setter for the field <code>date</code>.</p>
372      *
373      * @param year a int.
374      * @param month a int.
375      * @param day a int.
376      */
377     public void setDate(int year, int month, int day) {
378         int oldYearValue = this.calendarValue.get(Calendar.YEAR);
379         int oldMonthValue = this.calendarValue.get(Calendar.MONTH);
380         int oldDayValue = this.calendarValue.get(Calendar.DATE);
381         calendarValue.set(year, month, day);
382         fireChangeEvent();
383         firePropertyChange(PROPERTY_YEAR, oldYearValue, this.calendarValue.get(Calendar.YEAR));
384         firePropertyChange(PROPERTY_MONTH, oldMonthValue, this.calendarValue.get(Calendar.MONTH));
385         firePropertyChange(PROPERTY_DAY, oldDayValue, this.calendarValue.get(Calendar.DATE));
386         updateDateFromCalendar();
387     }
388 
389     private void updateDateFromCalendar() {
390         date = fromCalendar(calendarValue);
391         firePropertyChange(PROPERTY_VALUE, null, date);
392     }
393 
394     /**
395      * <p>isSelected.</p>
396      *
397      * @return a boolean.
398      */
399     public boolean isSelected() {
400         return selected;
401     }
402 
403     /**
404      * <p>Setter for the field <code>selected</code>.</p>
405      *
406      * @param selected a boolean.
407      */
408     public void setSelected(boolean selected) {
409         boolean oldSelectedValue = isSelected();
410         this.selected = selected;
411         fireChangeEvent();
412         updateDateFromCalendar();
413         firePropertyChange(PROPERTY_SELECTED, oldSelectedValue, this.selected);
414     }
415 
416     private void setToMidnight() {
417         calendarValue.set(Calendar.HOUR_OF_DAY, 0);
418         calendarValue.set(Calendar.MINUTE, 0);
419         calendarValue.set(Calendar.SECOND, 0);
420         calendarValue.set(Calendar.MILLISECOND, 0);
421     }
422 
423     private Calendar toCalendar(Date from) {
424         Calendar to = Calendar.getInstance();
425         to.setTime(from);
426         return to;
427     }
428 
429     private Date fromCalendar(Calendar from) {
430         return new Date(from.getTimeInMillis());
431     }
432 
433 }