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     */
021    package org.jdesktop.swingx.calendar;
022    
023    import java.text.DateFormat;
024    import java.text.ParseException;
025    import java.text.SimpleDateFormat;
026    import java.util.ArrayList;
027    import java.util.List;
028    import java.util.Locale;
029    import java.util.logging.Logger;
030    
031    import javax.swing.JFormattedTextField;
032    import javax.swing.plaf.UIResource;
033    
034    import org.jdesktop.swingx.plaf.UIManagerExt;
035    import 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     */
043    public 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    }