1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.beanutils2.locale.converters;
19
20 import java.text.DateFormat;
21 import java.text.DateFormatSymbols;
22 import java.text.ParseException;
23 import java.text.ParsePosition;
24 import java.text.SimpleDateFormat;
25 import java.util.Calendar;
26 import java.util.Date;
27 import java.util.Locale;
28
29 import org.apache.commons.beanutils2.ConversionException;
30 import org.apache.commons.beanutils2.locale.BaseLocaleConverter;
31 import org.apache.commons.beanutils2.locale.LocaleConverter;
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34
35
36
37
38
39
40
41
42 public class DateLocaleConverter<D extends Date> extends BaseLocaleConverter<D> {
43
44
45
46
47
48
49
50 public static class Builder<B extends Builder<B, D>, D extends Date> extends BaseLocaleConverter.Builder<B, D> {
51
52
53 private boolean lenient;
54
55
56
57
58
59
60
61
62
63
64 @Override
65 public DateLocaleConverter<D> get() {
66 return new DateLocaleConverter<>(defaultValue, locale, pattern, useDefault || defaultValue != null, localizedPattern, lenient);
67 }
68
69
70
71
72
73
74
75 public boolean isLenient() {
76 return lenient;
77 }
78
79
80
81
82
83
84
85 public B setLenient(final boolean lenient) {
86 this.lenient = lenient;
87 return asThis();
88 }
89
90 }
91
92
93
94
95 private static final String DEFAULT_PATTERN_CHARS = DateLocaleConverter.initDefaultChars();
96
97
98 private static final Log LOG = LogFactory.getLog(DateLocaleConverter.class);
99
100
101
102
103
104
105
106
107 @SuppressWarnings("unchecked")
108 public static <B extends Builder<B, D>, D extends Date> B builder() {
109 return (B) new Builder<>();
110 }
111
112
113
114
115
116 private static String initDefaultChars() {
117 return new DateFormatSymbols(Locale.US).getLocalPatternChars();
118 }
119
120
121 private final boolean isLenient;
122
123
124
125
126
127
128
129
130
131
132
133 protected DateLocaleConverter(final D defaultValue, final Locale locale, final String pattern, final boolean useDefault, final boolean locPattern,
134 final boolean lenient) {
135 super(defaultValue, locale, pattern, useDefault, locPattern);
136 this.isLenient = lenient;
137 }
138
139
140
141
142
143
144
145
146 private String convertLocalizedPattern(final String localizedPattern, final Locale locale) {
147 if (localizedPattern == null) {
148 return null;
149 }
150
151
152
153
154
155
156 final DateFormatSymbols localizedSymbols = new DateFormatSymbols(locale);
157 final String localChars = localizedSymbols.getLocalPatternChars();
158
159 if (DEFAULT_PATTERN_CHARS.equals(localChars)) {
160 return localizedPattern;
161 }
162
163
164 String convertedPattern = null;
165 try {
166 convertedPattern = convertPattern(localizedPattern, localChars, DEFAULT_PATTERN_CHARS);
167 } catch (final Exception ex) {
168 if (LOG.isDebugEnabled()) {
169 LOG.debug("Converting pattern '" + localizedPattern + "' for " + locale, ex);
170 }
171 }
172 return convertedPattern;
173 }
174
175
176
177
178 private String convertPattern(final String pattern, final String fromChars, final String toChars) {
179 final StringBuilder converted = new StringBuilder();
180 boolean quoted = false;
181
182 for (int i = 0; i < pattern.length(); ++i) {
183 char thisChar = pattern.charAt(i);
184 if (quoted) {
185 if (thisChar == '\'') {
186 quoted = false;
187 }
188 } else if (thisChar == '\'') {
189 quoted = true;
190 } else if (thisChar >= 'a' && thisChar <= 'z' || thisChar >= 'A' && thisChar <= 'Z') {
191 final int index = fromChars.indexOf(thisChar);
192 if (index == -1) {
193 throw new IllegalArgumentException("Illegal pattern character '" + thisChar + "'");
194 }
195 thisChar = toChars.charAt(index);
196 }
197 converted.append(thisChar);
198 }
199
200 if (quoted) {
201 throw new IllegalArgumentException("Unfinished quote in pattern");
202 }
203
204 return converted.toString();
205 }
206
207
208
209
210
211
212
213 public boolean isLenient() {
214 return isLenient;
215 }
216
217
218
219
220
221
222
223
224
225
226 @Override
227 protected D parse(final Object value, String pattern) throws ParseException {
228
229 if (value instanceof Date) {
230 return (D) value;
231 }
232
233
234 if (value instanceof Calendar) {
235 return (D) ((Calendar) value).getTime();
236 }
237
238 if (localizedPattern) {
239 pattern = convertLocalizedPattern(pattern, locale);
240 }
241
242
243 final DateFormat formatter = pattern == null ? DateFormat.getDateInstance(DateFormat.SHORT, locale) : new SimpleDateFormat(pattern, locale);
244 formatter.setLenient(isLenient);
245
246
247 final ParsePosition pos = new ParsePosition(0);
248 final String strValue = value.toString();
249 final Object parsedValue = formatter.parseObject(strValue, pos);
250 if (pos.getErrorIndex() > -1) {
251 throw ConversionException.format("Error parsing date '%s' at position = %s", value, pos.getErrorIndex());
252 }
253 if (pos.getIndex() < strValue.length()) {
254 throw ConversionException.format("Date '%s' contains unparsed characters from position = %s", value, pos.getIndex());
255 }
256
257 return (D) parsedValue;
258 }
259
260 }