1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.beanutils.locale.converters;
19
20 import org.apache.commons.beanutils.ConversionException;
21 import org.apache.commons.beanutils.locale.BaseLocaleConverter;
22 import org.apache.commons.logging.LogFactory;
23 import org.apache.commons.logging.Log;
24
25 import java.text.ParseException;
26 import java.text.ParsePosition;
27 import java.text.SimpleDateFormat;
28 import java.text.DateFormat;
29 import java.text.DateFormatSymbols;
30 import java.util.Locale;
31
32
33 /***
34 * <p>Standard {@link org.apache.commons.beanutils.locale.LocaleConverter}
35 * implementation that converts an incoming
36 * locale-sensitive String into a <code>java.util.Date</code> object,
37 * optionally using a default value or throwing a
38 * {@link org.apache.commons.beanutils.ConversionException}
39 * if a conversion error occurs.</p>
40 *
41 * @author Yauheny Mikulski
42 * @author Michael Szlapa
43 */
44
45 public class DateLocaleConverter extends BaseLocaleConverter {
46
47
48
49 /*** All logging goes through this logger */
50 private Log log = LogFactory.getLog(DateLocaleConverter.class);
51
52 /*** Should the date conversion be lenient? */
53 boolean isLenient = false;
54
55 /***
56 * Default Pattern Characters
57 *
58 */
59 private static final String DEFAULT_PATTERN_CHARS = DateLocaleConverter.initDefaultChars();
60
61
62
63 /***
64 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
65 * that will throw a {@link org.apache.commons.beanutils.ConversionException}
66 * if a conversion error occurs. The locale is the default locale for
67 * this instance of the Java Virtual Machine and an unlocalized pattern is used
68 * for the convertion.
69 *
70 */
71 public DateLocaleConverter() {
72
73 this(false);
74 }
75
76 /***
77 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
78 * that will throw a {@link org.apache.commons.beanutils.ConversionException}
79 * if a conversion error occurs. The locale is the default locale for
80 * this instance of the Java Virtual Machine.
81 *
82 * @param locPattern Indicate whether the pattern is localized or not
83 */
84 public DateLocaleConverter(boolean locPattern) {
85
86 this(Locale.getDefault(), locPattern);
87 }
88
89 /***
90 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
91 * that will throw a {@link org.apache.commons.beanutils.ConversionException}
92 * if a conversion error occurs. An unlocalized pattern is used for the convertion.
93 *
94 * @param locale The locale
95 */
96 public DateLocaleConverter(Locale locale) {
97
98 this(locale, false);
99 }
100
101 /***
102 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
103 * that will throw a {@link org.apache.commons.beanutils.ConversionException}
104 * if a conversion error occurs.
105 *
106 * @param locale The locale
107 * @param locPattern Indicate whether the pattern is localized or not
108 */
109 public DateLocaleConverter(Locale locale, boolean locPattern) {
110
111 this(locale, (String) null, locPattern);
112 }
113
114 /***
115 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
116 * that will throw a {@link org.apache.commons.beanutils.ConversionException}
117 * if a conversion error occurs. An unlocalized pattern is used for the convertion.
118 *
119 * @param locale The locale
120 * @param pattern The convertion pattern
121 */
122 public DateLocaleConverter(Locale locale, String pattern) {
123
124 this(locale, pattern, false);
125 }
126
127 /***
128 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
129 * that will throw a {@link org.apache.commons.beanutils.ConversionException}
130 * if a conversion error occurs.
131 *
132 * @param locale The locale
133 * @param pattern The convertion pattern
134 * @param locPattern Indicate whether the pattern is localized or not
135 */
136 public DateLocaleConverter(Locale locale, String pattern, boolean locPattern) {
137
138 super(locale, pattern, locPattern);
139 }
140
141 /***
142 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
143 * that will return the specified default value
144 * if a conversion error occurs. The locale is the default locale for
145 * this instance of the Java Virtual Machine and an unlocalized pattern is used
146 * for the convertion.
147 *
148 * @param defaultValue The default value to be returned
149 */
150 public DateLocaleConverter(Object defaultValue) {
151
152 this(defaultValue, false);
153 }
154
155 /***
156 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
157 * that will return the specified default value
158 * if a conversion error occurs. The locale is the default locale for
159 * this instance of the Java Virtual Machine.
160 *
161 * @param defaultValue The default value to be returned
162 * @param locPattern Indicate whether the pattern is localized or not
163 */
164 public DateLocaleConverter(Object defaultValue, boolean locPattern) {
165
166 this(defaultValue, Locale.getDefault(), locPattern);
167 }
168
169 /***
170 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
171 * that will return the specified default value
172 * if a conversion error occurs. An unlocalized pattern is used for the convertion.
173 *
174 * @param defaultValue The default value to be returned
175 * @param locale The locale
176 */
177 public DateLocaleConverter(Object defaultValue, Locale locale) {
178
179 this(defaultValue, locale, false);
180 }
181
182 /***
183 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
184 * that will return the specified default value
185 * if a conversion error occurs.
186 *
187 * @param defaultValue The default value to be returned
188 * @param locale The locale
189 * @param locPattern Indicate whether the pattern is localized or not
190 */
191 public DateLocaleConverter(Object defaultValue, Locale locale, boolean locPattern) {
192
193 this(defaultValue, locale, null, locPattern);
194 }
195
196
197 /***
198 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
199 * that will return the specified default value
200 * if a conversion error occurs. An unlocalized pattern is used for the convertion.
201 *
202 * @param defaultValue The default value to be returned
203 * @param locale The locale
204 * @param pattern The convertion pattern
205 */
206 public DateLocaleConverter(Object defaultValue, Locale locale, String pattern) {
207
208 this(defaultValue, locale, pattern, false);
209 }
210
211 /***
212 * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
213 * that will return the specified default value
214 * if a conversion error occurs.
215 *
216 * @param defaultValue The default value to be returned
217 * @param locale The locale
218 * @param pattern The convertion pattern
219 * @param locPattern Indicate whether the pattern is localized or not
220 */
221 public DateLocaleConverter(Object defaultValue, Locale locale, String pattern, boolean locPattern) {
222
223 super(defaultValue, locale, pattern, locPattern);
224 }
225
226
227
228 /***
229 * Returns whether date formatting is lenient.
230 *
231 * @return true if the <code>DateFormat</code> used for formatting is lenient
232 * @see java.text.DateFormat#isLenient
233 */
234 public boolean isLenient() {
235 return isLenient;
236 }
237
238 /***
239 * Specify whether or not date-time parsing should be lenient.
240 *
241 * @param lenient true if the <code>DateFormat</code> used for formatting should be lenient
242 * @see java.text.DateFormat#setLenient
243 */
244 public void setLenient(boolean lenient) {
245 isLenient = lenient;
246 }
247
248
249
250 /***
251 * Convert the specified locale-sensitive input object into an output object of the
252 * specified type.
253 *
254 * @param value The input object to be converted
255 * @param pattern The pattern is used for the convertion
256 * @return the converted Date value
257 *
258 * @exception org.apache.commons.beanutils.ConversionException
259 * if conversion cannot be performed successfully
260 * @throws ParseException if an error occurs parsing
261 */
262 protected Object parse(Object value, String pattern) throws ParseException {
263
264
265 if (value instanceof java.util.Date) {
266 return value;
267 }
268
269
270 if (value instanceof java.util.Calendar) {
271 return ((java.util.Calendar)value).getTime();
272 }
273
274 if (locPattern) {
275 pattern = convertLocalizedPattern(pattern, locale);
276 }
277
278
279 DateFormat formatter = pattern == null ? DateFormat.getDateInstance(DateFormat.SHORT, locale)
280 : new SimpleDateFormat(pattern, locale);
281 formatter.setLenient(isLenient);
282
283
284
285 ParsePosition pos = new ParsePosition(0);
286 String strValue = value.toString();
287 Object parsedValue = formatter.parseObject(strValue, pos);
288 if (pos.getErrorIndex() > -1) {
289 throw new ConversionException("Error parsing date '" + value +
290 "' at position="+ pos.getErrorIndex());
291 }
292 if (pos.getIndex() < strValue.length()) {
293 throw new ConversionException("Date '" + value +
294 "' contains unparsed characters from position=" + pos.getIndex());
295 }
296
297 return parsedValue;
298 }
299
300 /***
301 * Convert a pattern from a localized format to the default format.
302 *
303 * @param locale The locale
304 * @param localizedPattern The pattern in 'local' symbol format
305 * @return pattern in 'default' symbol format
306 */
307 private String convertLocalizedPattern(String localizedPattern, Locale locale) {
308
309 if (localizedPattern == null) {
310 return null;
311 }
312
313
314
315
316
317
318 DateFormatSymbols localizedSymbols = new DateFormatSymbols(locale);
319 String localChars = localizedSymbols.getLocalPatternChars();
320
321 if (DEFAULT_PATTERN_CHARS.equals(localChars)) {
322 return localizedPattern;
323 }
324
325
326 String convertedPattern = null;
327 try {
328 convertedPattern = convertPattern(localizedPattern,
329 localChars,
330 DEFAULT_PATTERN_CHARS);
331 } catch (Exception ex) {
332 log.info("Converting pattern '" + localizedPattern + "' for " + locale, ex);
333 }
334 return convertedPattern;
335 }
336
337 /***
338 * <p>Converts a Pattern from one character set to another.</p>
339 */
340 private String convertPattern(String pattern, String fromChars, String toChars) {
341
342 StringBuffer converted = new StringBuffer();
343 boolean quoted = false;
344
345 for (int i = 0; i < pattern.length(); ++i) {
346 char thisChar = pattern.charAt(i);
347 if (quoted) {
348 if (thisChar == '\'') {
349 quoted = false;
350 }
351 } else {
352 if (thisChar == '\'') {
353 quoted = true;
354 } else if ((thisChar >= 'a' && thisChar <= 'z') ||
355 (thisChar >= 'A' && thisChar <= 'Z')) {
356 int index = fromChars.indexOf(thisChar );
357 if (index == -1) {
358 throw new IllegalArgumentException(
359 "Illegal pattern character '" + thisChar + "'");
360 }
361 thisChar = toChars.charAt(index);
362 }
363 }
364 converted.append(thisChar);
365 }
366
367 if (quoted) {
368 throw new IllegalArgumentException("Unfinished quote in pattern");
369 }
370
371 return converted.toString();
372 }
373
374 /***
375 * This method is called at class initialization time to define the
376 * value for constant member DEFAULT_PATTERN_CHARS. All other methods needing
377 * this data should just read that constant.
378 */
379 private static String initDefaultChars() {
380 DateFormatSymbols defaultSymbols = new DateFormatSymbols(Locale.US);
381 return defaultSymbols.getLocalPatternChars();
382 }
383
384 }