001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.commons.beanutils.locale.converters;
019:
020: import org.apache.commons.beanutils.ConversionException;
021: import org.apache.commons.beanutils.locale.BaseLocaleConverter;
022: import org.apache.commons.logging.LogFactory;
023: import org.apache.commons.logging.Log;
024:
025: import java.text.ParseException;
026: import java.text.ParsePosition;
027: import java.text.SimpleDateFormat;
028: import java.text.DateFormat;
029: import java.text.DateFormatSymbols;
030: import java.util.Locale;
031:
032: /**
033: * <p>Standard {@link org.apache.commons.beanutils.locale.LocaleConverter}
034: * implementation that converts an incoming
035: * locale-sensitive String into a <code>java.util.Date</code> object,
036: * optionally using a default value or throwing a
037: * {@link org.apache.commons.beanutils.ConversionException}
038: * if a conversion error occurs.</p>
039: *
040: * @author Yauheny Mikulski
041: * @author Michael Szlapa
042: */
043:
044: public class DateLocaleConverter extends BaseLocaleConverter {
045:
046: // ----------------------------------------------------- Instance Variables
047:
048: /** All logging goes through this logger */
049: private Log log = LogFactory.getLog(DateLocaleConverter.class);
050:
051: /** Should the date conversion be lenient? */
052: boolean isLenient = false;
053:
054: /**
055: * Default Pattern Characters
056: *
057: */
058: private static final String DEFAULT_PATTERN_CHARS = DateLocaleConverter
059: .initDefaultChars();
060:
061: // ----------------------------------------------------------- Constructors
062:
063: /**
064: * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
065: * that will throw a {@link org.apache.commons.beanutils.ConversionException}
066: * if a conversion error occurs. The locale is the default locale for
067: * this instance of the Java Virtual Machine and an unlocalized pattern is used
068: * for the convertion.
069: *
070: */
071: public DateLocaleConverter() {
072:
073: this (false);
074: }
075:
076: /**
077: * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
078: * that will throw a {@link org.apache.commons.beanutils.ConversionException}
079: * if a conversion error occurs. The locale is the default locale for
080: * this instance of the Java Virtual Machine.
081: *
082: * @param locPattern Indicate whether the pattern is localized or not
083: */
084: public DateLocaleConverter(boolean locPattern) {
085:
086: this (Locale.getDefault(), locPattern);
087: }
088:
089: /**
090: * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
091: * that will throw a {@link org.apache.commons.beanutils.ConversionException}
092: * if a conversion error occurs. An unlocalized pattern is used for the convertion.
093: *
094: * @param locale The locale
095: */
096: public DateLocaleConverter(Locale locale) {
097:
098: this (locale, false);
099: }
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,
137: boolean locPattern) {
138:
139: super (locale, pattern, locPattern);
140: }
141:
142: /**
143: * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
144: * that will return the specified default value
145: * if a conversion error occurs. The locale is the default locale for
146: * this instance of the Java Virtual Machine and an unlocalized pattern is used
147: * for the convertion.
148: *
149: * @param defaultValue The default value to be returned
150: */
151: public DateLocaleConverter(Object defaultValue) {
152:
153: this (defaultValue, false);
154: }
155:
156: /**
157: * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
158: * that will return the specified default value
159: * if a conversion error occurs. The locale is the default locale for
160: * this instance of the Java Virtual Machine.
161: *
162: * @param defaultValue The default value to be returned
163: * @param locPattern Indicate whether the pattern is localized or not
164: */
165: public DateLocaleConverter(Object defaultValue, boolean locPattern) {
166:
167: this (defaultValue, Locale.getDefault(), locPattern);
168: }
169:
170: /**
171: * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
172: * that will return the specified default value
173: * if a conversion error occurs. An unlocalized pattern is used for the convertion.
174: *
175: * @param defaultValue The default value to be returned
176: * @param locale The locale
177: */
178: public DateLocaleConverter(Object defaultValue, Locale locale) {
179:
180: this (defaultValue, locale, false);
181: }
182:
183: /**
184: * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
185: * that will return the specified default value
186: * if a conversion error occurs.
187: *
188: * @param defaultValue The default value to be returned
189: * @param locale The locale
190: * @param locPattern Indicate whether the pattern is localized or not
191: */
192: public DateLocaleConverter(Object defaultValue, Locale locale,
193: boolean locPattern) {
194:
195: this (defaultValue, locale, null, locPattern);
196: }
197:
198: /**
199: * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
200: * that will return the specified default value
201: * if a conversion error occurs. An unlocalized pattern is used for the convertion.
202: *
203: * @param defaultValue The default value to be returned
204: * @param locale The locale
205: * @param pattern The convertion pattern
206: */
207: public DateLocaleConverter(Object defaultValue, Locale locale,
208: String pattern) {
209:
210: this (defaultValue, locale, pattern, false);
211: }
212:
213: /**
214: * Create a {@link org.apache.commons.beanutils.locale.LocaleConverter}
215: * that will return the specified default value
216: * if a conversion error occurs.
217: *
218: * @param defaultValue The default value to be returned
219: * @param locale The locale
220: * @param pattern The convertion pattern
221: * @param locPattern Indicate whether the pattern is localized or not
222: */
223: public DateLocaleConverter(Object defaultValue, Locale locale,
224: String pattern, boolean locPattern) {
225:
226: super (defaultValue, locale, pattern, locPattern);
227: }
228:
229: // --------------------------------------------------------- Methods
230:
231: /**
232: * Returns whether date formatting is lenient.
233: *
234: * @return true if the <code>DateFormat</code> used for formatting is lenient
235: * @see java.text.DateFormat#isLenient
236: */
237: public boolean isLenient() {
238: return isLenient;
239: }
240:
241: /**
242: * Specify whether or not date-time parsing should be lenient.
243: *
244: * @param lenient true if the <code>DateFormat</code> used for formatting should be lenient
245: * @see java.text.DateFormat#setLenient
246: */
247: public void setLenient(boolean lenient) {
248: isLenient = lenient;
249: }
250:
251: // --------------------------------------------------------- Methods
252:
253: /**
254: * Convert the specified locale-sensitive input object into an output object of the
255: * specified type.
256: *
257: * @param value The input object to be converted
258: * @param pattern The pattern is used for the convertion
259: * @return the converted Date value
260: *
261: * @exception org.apache.commons.beanutils.ConversionException
262: * if conversion cannot be performed successfully
263: * @throws ParseException if an error occurs parsing
264: */
265: protected Object parse(Object value, String pattern)
266: throws ParseException {
267:
268: // Handle Date
269: if (value instanceof java.util.Date) {
270: return value;
271: }
272:
273: // Handle Calendar
274: if (value instanceof java.util.Calendar) {
275: return ((java.util.Calendar) value).getTime();
276: }
277:
278: if (locPattern) {
279: pattern = convertLocalizedPattern(pattern, locale);
280: }
281:
282: // Create Formatter - use default if pattern is null
283: DateFormat formatter = pattern == null ? DateFormat
284: .getDateInstance(DateFormat.SHORT, locale)
285: : new SimpleDateFormat(pattern, locale);
286: formatter.setLenient(isLenient);
287:
288: // Parse the Date
289: ParsePosition pos = new ParsePosition(0);
290: String strValue = value.toString();
291: Object parsedValue = formatter.parseObject(strValue, pos);
292: if (pos.getErrorIndex() > -1) {
293: throw new ConversionException("Error parsing date '"
294: + value + "' at position=" + pos.getErrorIndex());
295: }
296: if (pos.getIndex() < strValue.length()) {
297: throw new ConversionException("Date '" + value
298: + "' contains unparsed characters from position="
299: + pos.getIndex());
300: }
301:
302: return parsedValue;
303: }
304:
305: /**
306: * Convert a pattern from a localized format to the default format.
307: *
308: * @param locale The locale
309: * @param localizedPattern The pattern in 'local' symbol format
310: * @return pattern in 'default' symbol format
311: */
312: private String convertLocalizedPattern(String localizedPattern,
313: Locale locale) {
314:
315: if (localizedPattern == null) {
316: return null;
317: }
318:
319: // Note that this is a little obtuse.
320: // However, it is the best way that anyone can come up with
321: // that works with some 1.4 series JVM.
322:
323: // Get the symbols for the localized pattern
324: DateFormatSymbols localizedSymbols = new DateFormatSymbols(
325: locale);
326: String localChars = localizedSymbols.getLocalPatternChars();
327:
328: if (DEFAULT_PATTERN_CHARS.equals(localChars)) {
329: return localizedPattern;
330: }
331:
332: // Convert the localized pattern to default
333: String convertedPattern = null;
334: try {
335: convertedPattern = convertPattern(localizedPattern,
336: localChars, DEFAULT_PATTERN_CHARS);
337: } catch (Exception ex) {
338: log.info("Converting pattern '" + localizedPattern
339: + "' for " + locale, ex);
340: }
341: return convertedPattern;
342: }
343:
344: /**
345: * <p>Converts a Pattern from one character set to another.</p>
346: */
347: private String convertPattern(String pattern, String fromChars,
348: String toChars) {
349:
350: StringBuffer converted = new StringBuffer();
351: boolean quoted = false;
352:
353: for (int i = 0; i < pattern.length(); ++i) {
354: char this Char = pattern.charAt(i);
355: if (quoted) {
356: if (this Char == '\'') {
357: quoted = false;
358: }
359: } else {
360: if (this Char == '\'') {
361: quoted = true;
362: } else if ((this Char >= 'a' && this Char <= 'z')
363: || (this Char >= 'A' && this Char <= 'Z')) {
364: int index = fromChars.indexOf(this Char);
365: if (index == -1) {
366: throw new IllegalArgumentException(
367: "Illegal pattern character '"
368: + this Char + "'");
369: }
370: this Char = toChars.charAt(index);
371: }
372: }
373: converted.append(this Char);
374: }
375:
376: if (quoted) {
377: throw new IllegalArgumentException(
378: "Unfinished quote in pattern");
379: }
380:
381: return converted.toString();
382: }
383:
384: /**
385: * This method is called at class initialization time to define the
386: * value for constant member DEFAULT_PATTERN_CHARS. All other methods needing
387: * this data should just read that constant.
388: */
389: private static String initDefaultChars() {
390: DateFormatSymbols defaultSymbols = new DateFormatSymbols(
391: Locale.US);
392: return defaultSymbols.getLocalPatternChars();
393: }
394:
395: }
|