1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 or later
5    * as published by the Free Software Foundation. This program is distributed
6    * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
7    * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *      http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * © CrossWire Bible Society, 2007 - 2016
18   *
19   */
20  package org.crosswire.common.icu;
21  
22  import java.lang.reflect.InvocationTargetException;
23  import java.text.DateFormat;
24  import java.text.SimpleDateFormat;
25  import java.util.Date;
26  
27  import org.crosswire.common.util.ClassUtil;
28  import org.crosswire.common.util.ReflectionUtil;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /**
33   * DateFormat provides a wrapper of some of DateFormat and SimpleDateFormat
34   * using ICU4J if present, otherwise from core Java. Note, only those methods in
35   * DateFormat that are actually used are here.
36   *
37   * @author DM Smith
38   * @see gnu.lgpl.License The GNU Lesser General Public License for details.<br>
39   * The copyright to this program is held by its authors.
40   */
41  public final class DateFormatter {
42      // Note these values are the same for Java and ICU4J
43      /**
44       * Constant for full style pattern.
45       */
46      public static final int FULL = 0;
47      /**
48       * Constant for long style pattern.
49       */
50      public static final int LONG = 1;
51      /**
52       * Constant for medium style pattern.
53       */
54      public static final int MEDIUM = 2;
55      /**
56       * Constant for short style pattern.
57       */
58      public static final int SHORT = 3;
59      /**
60       * Constant for default style pattern. Its value is MEDIUM.
61       */
62      public static final int DEFAULT = MEDIUM;
63      private static final Logger LOGGER = LoggerFactory.getLogger(DateFormatter.class);
64      private static final String DEFAULT_SIMPLE_DATE_FORMAT_CLASS = "com.ibm.icu.text.SimpleDateFormat";
65      private static final String DEFAULT_DATE_FORMAT_CLASS = "com.ibm.icu.text.DateFormat";
66  
67      /**
68       * The actual formatter.
69       */
70      private Object formatter;
71      /**
72       * The class of the formatter
73       */
74      private Class<?> formatterClass;
75      private static Class<?> defaultSimpleDateFormat;
76      private static Class<?> defaultDateFormat;
77  
78      static {
79          try {
80              defaultSimpleDateFormat = ClassUtil.forName(DEFAULT_SIMPLE_DATE_FORMAT_CLASS);
81          } catch (ClassNotFoundException ex) {
82              LOGGER.info("Error loading simple date format class [{}]", DEFAULT_SIMPLE_DATE_FORMAT_CLASS);
83          }
84  
85          try {
86              defaultDateFormat = ClassUtil.forName(DEFAULT_DATE_FORMAT_CLASS);
87          } catch (ClassNotFoundException ex) {
88              LOGGER.info("Error loading date format class [{}]", DEFAULT_SIMPLE_DATE_FORMAT_CLASS);
89          }
90      }
91  
92      /**
93       * Prevent instantiation.
94       */
95      private DateFormatter() {
96      }
97  
98      /**
99       * Construct a DateFormatter with the given date format.
100      *
101      * @param format the date format
102      * @return a DateFormatter of the given format
103      * @see java.text.DateFormat#getDateInstance(int)
104      */
105     public static DateFormatter getDateInstance(int format) {
106         DateFormatter fmt = new DateFormatter();
107         boolean oops = false;
108         try {
109             fmt.formatterClass = defaultDateFormat;
110             // To call a method taking a type of int, the type has to match but
111             // the object has to be wrapped
112             Class<?>[] instanceTypes = {
113                 int.class
114             };
115             Object[] instanceParams = {
116                 Integer.valueOf(format)
117             };
118             fmt.formatter = ReflectionUtil.invoke(fmt.formatterClass, fmt.formatterClass, "getDateInstance", instanceParams, instanceTypes);
119         } catch (NoSuchMethodException e) {
120             oops = true;
121         } catch (IllegalAccessException e) {
122             oops = true;
123         } catch (InvocationTargetException e) {
124             oops = true;
125         } catch (NullPointerException e) {
126             oops = true;
127         }
128 
129         if (oops) {
130             fmt.formatterClass = DateFormat.class;
131             fmt.formatter = DateFormat.getDateInstance(format);
132         }
133 
134         return fmt;
135     }
136 
137     /**
138      * Construct a DateFormatter with the default date format.
139      *
140      * @return a DateFormatter of the default format
141      * @see java.text.DateFormat#getDateInstance()
142      */
143     public static DateFormatter getDateInstance() {
144         return getDateInstance(DEFAULT);
145     }
146 
147     /**
148      * Construct a simple DateFormatter with the given date format.
149      *
150      * @param format the date format
151      * @return a DateFormatter with the given date format
152      * @see java.text.DateFormat#getDateInstance(int)
153      */
154     public static DateFormatter getSimpleDateInstance(String format) {
155         DateFormatter fmt = new DateFormatter();
156         boolean oops = false;
157         try {
158             fmt.formatterClass = defaultSimpleDateFormat;
159             fmt.formatter = ReflectionUtil.construct(fmt.formatterClass, format);
160         } catch (NoSuchMethodException e) {
161             oops = true;
162         } catch (IllegalAccessException e) {
163             oops = true;
164         } catch (InvocationTargetException e) {
165             oops = true;
166         } catch (NullPointerException e) {
167             oops = true;
168         } catch (InstantiationException e) {
169             oops = true;
170         }
171 
172         if (oops) {
173             fmt.formatterClass = SimpleDateFormat.class;
174             fmt.formatter = new SimpleDateFormat(format);
175         }
176 
177         return fmt;
178     }
179 
180     /**
181      * Set whether this DataFormatter should be lenient in parsing dates.
182      *
183      * @param lenient whether to be lenient or not
184      * @see java.text.DateFormat#setLenient(boolean)
185      */
186     public void setLenient(boolean lenient) {
187         try {
188             Class<?>[] lenientTypes = {
189                     boolean.class
190             };
191             Object[] lenientParams = {
192                     Boolean.valueOf(lenient)
193             };
194             ReflectionUtil.invoke(formatterClass, formatter, "setLenient", lenientParams, lenientTypes);
195         } catch (NoSuchMethodException e) {
196             assert false : e;
197         } catch (IllegalAccessException e) {
198             assert false : e;
199         } catch (InvocationTargetException e) {
200             assert false : e;
201         }
202     }
203 
204     /**
205      * Formats a Date into a date/time string.
206      *
207      * @param date the time value to be formatted into a time string.
208      * @return the formatted time string.
209      * @see java.text.DateFormat#format(java.util.Date)
210      */
211     public String format(Date date) {
212         try {
213             return (String) ReflectionUtil.invoke(formatterClass, formatter, "format", date);
214         } catch (NoSuchMethodException e) {
215             assert false : e;
216         } catch (IllegalAccessException e) {
217             assert false : e;
218         } catch (InvocationTargetException e) {
219             assert false : e;
220         }
221         return "";
222     }
223 
224     /**
225      * Convert text to a date.
226      *
227      * @param text the input to parse as a date
228      * @return the resultant date
229      * @see java.text.DateFormat#parse(java.lang.String)
230      */
231     public Date parse(String text) {
232         try {
233             return (Date) ReflectionUtil.invoke(formatterClass, formatter, "parse", text);
234         } catch (NoSuchMethodException e) {
235             assert false : e;
236         } catch (IllegalAccessException e) {
237             assert false : e;
238         } catch (InvocationTargetException e) {
239             assert false : e;
240         }
241         return new Date();
242     }
243 }
244