001/*
002 * $Id: DatePickerFormatter.java 3140 2008-12-16 15:09:09Z kleopatra $
003 * 
004 * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020 */
021package org.jdesktop.swingx.calendar;
022
023import java.text.DateFormat;
024import java.text.ParseException;
025import java.text.SimpleDateFormat;
026import java.util.ArrayList;
027import java.util.List;
028import java.util.Locale;
029import java.util.logging.Logger;
030
031import javax.swing.JFormattedTextField;
032import javax.swing.plaf.UIResource;
033
034import org.jdesktop.swingx.plaf.UIManagerExt;
035import org.jdesktop.swingx.util.Contract;
036
037/**
038 * Default formatter for the JXDatePicker component.  
039 * It can handle a variety of date formats.
040 *
041 * @author Joshua Outwater
042 */
043public class DatePickerFormatter extends
044        JFormattedTextField.AbstractFormatter {
045    
046    private static final Logger LOG = Logger
047            .getLogger(DatePickerFormatter.class.getName());
048    private DateFormat _formats[] = null;
049
050    
051    /**
052     * Instantiates a formatter with the localized format patterns defined
053     * in the swingx.properties.
054     * 
055     * These formats are localizable and fields may be re-arranged, such as
056     * swapping the month and day fields.  The keys for localizing these fields
057     * are:
058     * <ul>
059     * <li>JXDatePicker.longFormat
060     * <li>JXDatePicker.mediumFormat
061     * <li>JXDatePicker.shortFormat
062     * </ul>
063     *
064     */
065    public DatePickerFormatter() {
066        this(null, null);
067    }
068
069    /**
070     * Instantiates a formatter with the given date formats. If the 
071     * array is null, default formats are created from the localized
072     * patterns in swingx.properties. If empty?
073     * 
074     * @param formats the array of formats to use. May be null to 
075     *   use defaults or empty to do nothing (?), but must not contain
076     *   null formats.
077     */
078    public DatePickerFormatter(DateFormat formats[]) {
079        this(formats, null);
080    }
081
082    /**
083     * Instantiates a formatter with default date formats in the 
084     * given locale. The default formats are created from the localized
085     * patterns in swingx.properties. 
086     * 
087     * @param locale the Locale the use for the default formats.
088     */
089    public DatePickerFormatter(Locale locale) {
090        this(null, locale);
091    }
092
093    /**
094     * Instantiates a formatter with the given formats and locale.
095     * 
096     * PENDING JW: makes no sense as a public constructor because the locale is ignored
097     * if the formats are null. So has same public behaviour as the constructor with
098     * formats only ...
099     * 
100     * @param formats
101     * @param locale
102     */
103    public DatePickerFormatter(DateFormat formats[], Locale locale) {
104//        super();
105        if (locale == null) {
106            locale = Locale.getDefault();
107        }
108        if (formats == null) {
109            formats = createDefaultFormats(locale);
110        }
111        Contract.asNotNull(formats, "The array of DateFormats must not contain null formats");
112        _formats = formats;
113    }
114    
115    /**
116     * Returns an array of the formats used by this formatter.
117     * 
118     * @return the formats used by this formatter, guaranteed to be
119     *   not null.
120     */
121    public DateFormat[] getFormats() {
122        DateFormat[] results = new DateFormat[_formats.length];
123        System.arraycopy(_formats, 0, results, 0, results.length);
124        return results;
125    }
126
127    /**
128     * {@inheritDoc}
129     */
130    @Override
131    public Object stringToValue(String text) throws ParseException {
132        Object result = null;
133        ParseException pex = null;
134
135        if (text == null || text.trim().length() == 0) {
136            return null;
137        }
138
139        // If the current formatter did not work loop through the other
140        // formatters and see if any of them can parse the string passed
141        // in.
142        for (DateFormat _format : _formats) {
143            try {
144                result = (_format).parse(text);
145                pex = null;
146                break;
147            } catch (ParseException ex) {
148                pex = ex;
149            }
150        }
151
152        if (pex != null) {
153            throw pex;
154        }
155
156        return result;
157    }
158
159    /**
160     * {@inheritDoc}
161     */
162    @Override
163    public String valueToString(Object value) throws ParseException {
164         if ((value != null) && (_formats.length > 0)){
165            return _formats[0].format(value);
166        }
167        return null;
168    }
169    
170    /**
171     * Creates and returns the localized default formats. First tries to 
172     * add formats created using the patterns stored in the UIManager. If
173     * there are no patterns, use the DateFormat's instance with style
174     * DateFormat.SHORT.
175     * 
176     * @return the localized default formats.
177     */
178    protected DateFormat[] createDefaultFormats(Locale locale) {
179        List<DateFormat> f = new ArrayList<DateFormat>();
180        addFormat(f, "JXDatePicker.longFormat", locale);
181        addFormat(f, "JXDatePicker.mediumFormat", locale);
182        addFormat(f, "JXDatePicker.shortFormat", locale);
183        if (f.size() == 0) {
184           addSystemDefaultFormat(f, locale); 
185        }
186        return f.toArray(new DateFormat[f.size()]);
187    }
188
189    /**
190     * Adds the system's default DateFormat. This implementation adds a
191     * dateInstance of style DateFormat.SHORT.
192     * 
193     * @param f the List of formats to add to
194     * @param locale the Locale to use for the formatter.
195     */
196    private void addSystemDefaultFormat(List<DateFormat> f, Locale locale) {
197        f.add(DateFormat.getDateInstance(DateFormat.SHORT, locale));
198    }
199
200    /**
201     * Creates and adds a DateFormat to the given list. Looks up
202     * a format pattern registered in the UIManager for the given 
203     * key and tries to create a SimpleDateFormat. Does nothing
204     * if there is no format pattern registered or the pattern is
205     * invalid.
206     * 
207     * @param f the list of formats
208     * @param key the key for getting the pattern from the UI
209     */
210    private void addFormat(List<DateFormat> f, String key, Locale locale) {
211        String pattern = UIManagerExt.getString(key, locale);
212        if (pattern == null) return;
213        try {
214            SimpleDateFormat format = new SimpleDateFormat(pattern, locale);
215            f.add(format);
216        } catch (RuntimeException e) {
217            // format string  not available or invalid
218            LOG.finer("creating date format failed for key/pattern: " + key + "/" + pattern);
219        }
220    }
221
222    /**
223     * 
224     * Same as DatePickerFormatter, but tagged as UIResource.
225     * 
226     * @author Jeanette Winzenburg
227     */
228    public static class DatePickerFormatterUIResource extends DatePickerFormatter 
229        implements UIResource {
230
231        /**
232         * @param locale
233         */
234        public DatePickerFormatterUIResource(Locale locale) {
235            super(locale);
236        }
237
238        /**
239         * 
240         */
241        public DatePickerFormatterUIResource() {
242            this(null);
243        }
244     
245        public DatePickerFormatterUIResource(DateFormat[] formats, Locale locale) {
246            super(formats, locale);
247        }
248    }
249}