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  import javax.swing.event.ChangeEvent;
54  import javax.swing.event.ChangeListener;
55  import java.beans.PropertyChangeEvent;
56  import java.beans.PropertyChangeListener;
57  import java.time.LocalDate;
58  import java.util.Date;
59  import java.util.HashSet;
60  import java.util.Set;
61  
62  /**
63   * Created 18 April 2010
64   * Updated 26 April 2010
65   *
66   * @author Juan Heyns
67   */
68  public class LocalDateModel {
69  
70      /**
71       * Constant <code>PROPERTY_YEAR="year"</code>
72       */
73      private static final String PROPERTY_YEAR = "year";
74      /**
75       * Constant <code>PROPERTY_MONTH="month"</code>
76       */
77      private static final String PROPERTY_MONTH = "month";
78      /**
79       * Constant <code>PROPERTY_DAY="day"</code>
80       */
81      private static final String PROPERTY_DAY = "day";
82      /**
83       * Constant <code>PROPERTY_VALUE="value"</code>
84       */
85      static final String PROPERTY_VALUE = "value";
86      /**
87       * Constant <code>PROPERTY_SELECTED="selected"</code>
88       */
89      private static final String PROPERTY_SELECTED = "selected";
90  
91      private boolean modelAdjusting;
92      private boolean selected;
93      private LocalDate date;
94      private final Set<ChangeListener> changeListeners;
95      private final Set<PropertyChangeListener> propertyChangeListeners;
96  
97      /**
98       * <p>Constructor for DateModel.</p>
99       *
100      * @param dateValue a {@link Date} object.
101      */
102     LocalDateModel(LocalDate dateValue) {
103         this();
104         setLocalDate(dateValue);
105     }
106 
107     /**
108      * <p>Constructor for DateModel.</p>
109      */
110     LocalDateModel() {
111         changeListeners = new HashSet<>();
112         propertyChangeListeners = new HashSet<>();
113         selected = false;
114         date = LocalDate.now();
115     }
116 
117     /**
118      * <p>addChangeListener.</p>
119      *
120      * @param changeListener a {@link ChangeListener} object.
121      */
122     synchronized void addChangeListener(ChangeListener changeListener) {
123         changeListeners.add(changeListener);
124     }
125 
126     /**
127      * <p>removeChangeListener.</p>
128      *
129      * @param changeListener a {@link ChangeListener} object.
130      */
131     synchronized void removeChangeListener(ChangeListener changeListener) {
132         changeListeners.remove(changeListener);
133     }
134 
135     /**
136      * <p>fireChangeEvent.</p>
137      */
138     private synchronized void fireChangeEvent() {
139         if (modelAdjusting) {
140             return;
141         }
142         modelAdjusting = true;
143         for (ChangeListener changeListener : changeListeners) {
144             changeListener.stateChanged(new ChangeEvent(this));
145         }
146         modelAdjusting = false;
147     }
148 
149     /**
150      * <p>addPropertyChangeListener.</p>
151      *
152      * @param listener a {@link PropertyChangeListener} object.
153      */
154     public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
155         propertyChangeListeners.add(listener);
156     }
157 
158     /**
159      * <p>removePropertyChangeListener.</p>
160      *
161      * @param listener a {@link PropertyChangeListener} object.
162      */
163     public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
164         propertyChangeListeners.remove(listener);
165     }
166 
167     /**
168      * <p>firePropertyChange.</p>
169      *
170      * @param propertyName a {@link String} object.
171      * @param oldValue     a {@link Object} object.
172      * @param newValue     a {@link Object} object.
173      */
174     protected synchronized void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
175         if (oldValue != null && oldValue.equals(newValue)) {
176             return;
177         }
178 
179         for (PropertyChangeListener listener : propertyChangeListeners) {
180             listener.propertyChange(new PropertyChangeEvent(this, propertyName, oldValue, newValue));
181         }
182     }
183 
184     /**
185      * <p>getDay.</p>
186      *
187      * @return a int.
188      */
189     int getDay() {
190         return getSafeLocalDate().getDayOfMonth();
191     }
192 
193     /**
194      * <p>getMonth.</p>
195      *
196      * @return a int.
197      */
198     int getMonth() {
199         return getSafeLocalDate().getMonthValue();
200     }
201 
202     /**
203      * <p>getYear.</p>
204      *
205      * @return a int.
206      */
207     public int getYear() {
208         return getSafeLocalDate().getYear();
209     }
210 
211     /**
212      * <p>Getter for the field <code>date</code>.</p>
213      *
214      * @return a {@link Date} object.
215      */
216     LocalDate getLocalDate() {
217         if (!selected) {
218             return null;
219         }
220         return date;
221     }
222 
223     LocalDate getSafeLocalDate() {
224         return date != null ? date : LocalDate.now();
225     }
226 
227     /**
228      * <p>setDay.</p>
229      *
230      * @param day a int.
231      */
232     void setDay(int day) {
233         int oldDayValue = getDay();
234         if (day < 1) {
235             date = getSafeLocalDate().withDayOfMonth(1).minusDays(1 - day);
236         } else if (day > getSafeLocalDate().lengthOfMonth()) {
237             date = getSafeLocalDate().withDayOfMonth(getSafeLocalDate().lengthOfMonth()).plusDays(day - getSafeLocalDate().lengthOfMonth());
238         } else {
239             date = getSafeLocalDate().withDayOfMonth(day);
240         }
241         fireChangeEvent();
242         firePropertyChange(PROPERTY_DAY, oldDayValue, getDay());
243         firePropertyChange(PROPERTY_VALUE, null, date);
244     }
245 
246     /**
247      * <p>addDay.</p>
248      *
249      * @param add a int.
250      */
251     public void addDay(int add) {
252         int oldDayValue = getDay();
253         date = getSafeLocalDate().plusDays(add);
254         fireChangeEvent();
255         firePropertyChange(PROPERTY_DAY, oldDayValue, getDay());
256     }
257 
258     /**
259      * <p>setMonth.</p>
260      *
261      * @param month a int.
262      */
263     void setMonth(int month) {
264         int oldMonthValue = getMonth();
265         int oldDayValue = getDay();
266 
267         date = getSafeLocalDate().withMonth(month);
268 
269         fireChangeEvent();
270         firePropertyChange(PROPERTY_MONTH, oldMonthValue, getMonth());
271         // only fire change event when day actually changed
272         if (getDay() != oldDayValue) {
273             firePropertyChange(PROPERTY_DAY, oldDayValue, getDay());
274         }
275     }
276 
277     /**
278      * <p>addMonth.</p>
279      *
280      * @param add a int.
281      */
282     void addMonth(int add) {
283         int oldMonthValue = getMonth();
284         date = getSafeLocalDate().plusMonths(add);
285         fireChangeEvent();
286         firePropertyChange(PROPERTY_MONTH, oldMonthValue, getMonth());
287     }
288 
289     /**
290      * <p>setYear.</p>
291      *
292      * @param year a int.
293      */
294     public void setYear(int year) {
295         int oldYearValue = getYear();
296         int oldDayValue = getDay();
297 
298         date = getSafeLocalDate().withYear(year);
299 
300         fireChangeEvent();
301         firePropertyChange(PROPERTY_YEAR, oldYearValue, getYear());
302         // only fire change event when day actually changed
303         if (getDay() != oldDayValue) {
304             firePropertyChange(PROPERTY_DAY, oldDayValue, getDay());
305         }
306     }
307 
308     /**
309      * <p>addYear.</p>
310      *
311      * @param add a int.
312      */
313     void addYear(int add) {
314         int oldYearValue = getYear();
315         date = getSafeLocalDate().plusYears(add);
316         fireChangeEvent();
317         firePropertyChange(PROPERTY_YEAR, oldYearValue, getYear());
318     }
319 
320     /**
321      * <p>Setter for the field <code>date</code>.</p>
322      *
323      * @param value a {@link Date} object.
324      */
325     void setLocalDate(LocalDate value) {
326         int oldYearValue = getYear();
327         int oldMonthValue = getMonth();
328         int oldDayValue = getDay();
329         boolean oldSelectedValue = isSelected();
330 
331         selected = value != null;
332         date = value != null ? value : LocalDate.now();
333 
334         fireChangeEvent();
335         firePropertyChange(PROPERTY_YEAR, oldYearValue, getYear());
336         firePropertyChange(PROPERTY_MONTH, oldMonthValue, getMonth());
337         firePropertyChange(PROPERTY_DAY, oldDayValue, getDay());
338         firePropertyChange(PROPERTY_VALUE, null, date);
339         firePropertyChange(PROPERTY_SELECTED, oldSelectedValue, this.selected);
340     }
341 
342     /**
343      * <p>Setter for the field <code>date</code>.</p>
344      *
345      * @param year  a int.
346      * @param month a int.
347      * @param day   a int.
348      */
349     public void setLocalDate(int year, int month, int day) {
350         int oldYearValue = getYear();
351         int oldMonthValue = getMonth();
352         int oldDayValue = getDay();
353 
354         date = LocalDate.of(year, month, day);
355 
356         fireChangeEvent();
357         firePropertyChange(PROPERTY_YEAR, oldYearValue, getYear());
358         firePropertyChange(PROPERTY_MONTH, oldMonthValue, getMonth());
359         firePropertyChange(PROPERTY_DAY, oldDayValue, getDay());
360         firePropertyChange(PROPERTY_VALUE, null, date);
361     }
362 
363     /**
364      * <p>isSelected.</p>
365      *
366      * @return a boolean.
367      */
368     public boolean isSelected() {
369         return selected;
370     }
371 
372     /**
373      * <p>Setter for the field <code>selected</code>.</p>
374      *
375      * @param selected a boolean.
376      */
377     public void setSelected(boolean selected) {
378         boolean oldSelectedValue = isSelected();
379         this.selected = selected;
380         fireChangeEvent();
381         firePropertyChange(PROPERTY_VALUE, null, date);
382         firePropertyChange(PROPERTY_SELECTED, oldSelectedValue, this.selected);
383     }
384 
385 }