Author: adrianc Date: Mon Aug 12 15:52:06 2013 New Revision: 1513175 URL: http://svn.apache.org/r1513175 Log: Fixed some bugs in the date-time conversions. There were problems in the code that tried to match types in the java.util.Date class hierarchy, so sometimes the wrong converter would be returned by Converters.getConverter().
This commit is a port of Apache OFBiz revisions 1164906, 1350081, 1482507, 1497890. Modified: commons/sandbox/convert/trunk/src/main/java/org/apache/commons/convert/Converters.java commons/sandbox/convert/trunk/src/main/java/org/apache/commons/convert/DateTimeConverters.java commons/sandbox/convert/trunk/src/test/java/org/apache/commons/convert/TestDateTimeConverters.java Modified: commons/sandbox/convert/trunk/src/main/java/org/apache/commons/convert/Converters.java URL: http://svn.apache.org/viewvc/commons/sandbox/convert/trunk/src/main/java/org/apache/commons/convert/Converters.java?rev=1513175&r1=1513174&r2=1513175&view=diff ============================================================================== --- commons/sandbox/convert/trunk/src/main/java/org/apache/commons/convert/Converters.java (original) +++ commons/sandbox/convert/trunk/src/main/java/org/apache/commons/convert/Converters.java Mon Aug 12 15:52:06 2013 @@ -84,31 +84,43 @@ public class Converters { */ public static <S, T> Converter<S, T> getConverter(Class<S> sourceClass, Class<T> targetClass) throws ClassNotFoundException { String key = sourceClass.getName().concat(DELIMITER).concat(targetClass.getName()); -OUTER: - do { - Converter<?, ?> result = converterMap.get(key); - if (result != null) { - return Util.cast(result); - } - if (noConversions.contains(key)) { - throw new ClassNotFoundException("No converter found for " + key); - } - for (Converter<?, ?> value : converterMap.values()) { - if (value.canConvert(sourceClass, targetClass)) { - converterMap.putIfAbsent(key, value); - continue OUTER; - } - } - for (ConverterCreator value : creators) { - result = createConverter(value, sourceClass, targetClass); + OUTER: + do { + Converter<?, ?> result = converterMap.get(key); if (result != null) { - converterMap.putIfAbsent(key, result); + return Util.cast(result); + } + if (noConversions.contains(key)) { + throw new ClassNotFoundException("No converter found for " + key); + } + Class<?> foundSourceClass = null; + Converter<?, ?> foundConverter = null; + for (Converter<?, ?> value : converterMap.values()) { + if (value.canConvert(sourceClass, targetClass)) { + // this converter can deal with the source/target pair + if (foundSourceClass == null || foundSourceClass.isAssignableFrom(value.getSourceClass())) { + // remember the current target source class; if we find another converter, check + // to see if it's source class is assignable to this one, and if so, it means it's + // a child class, so we'll then take that converter. + foundSourceClass = value.getSourceClass(); + foundConverter = value; + } + } + } + if (foundConverter != null) { + converterMap.putIfAbsent(key, foundConverter); continue OUTER; } - } - noConversions.add(key); - throw new ClassNotFoundException("No converter found for " + key); - } while (true); + for (ConverterCreator value : creators) { + result = createConverter(value, sourceClass, targetClass); + if (result != null) { + converterMap.putIfAbsent(key, result); + continue OUTER; + } + } + noConversions.add(key); + throw new ClassNotFoundException("No converter found for " + key); + } while (true); } private static <S, SS extends S, T, TT extends T> Converter<SS, TT> createConverter(ConverterCreator creater, Class<SS> sourceClass, Class<TT> targetClass) { Modified: commons/sandbox/convert/trunk/src/main/java/org/apache/commons/convert/DateTimeConverters.java URL: http://svn.apache.org/viewvc/commons/sandbox/convert/trunk/src/main/java/org/apache/commons/convert/DateTimeConverters.java?rev=1513175&r1=1513174&r2=1513175&view=diff ============================================================================== --- commons/sandbox/convert/trunk/src/main/java/org/apache/commons/convert/DateTimeConverters.java (original) +++ commons/sandbox/convert/trunk/src/main/java/org/apache/commons/convert/DateTimeConverters.java Mon Aug 12 15:52:06 2013 @@ -114,6 +114,24 @@ public class DateTimeConverters implemen } /** + * An object that converts a <code>Calendar</code> to a <code>Date</code>. + */ + public static class CalendarToDate extends AbstractConverter<Calendar, Date> { + public CalendarToDate() { + super(Calendar.class, Date.class); + } + + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return Util.instanceOf(sourceClass, this.getSourceClass()) && Date.class.equals(targetClass); + } + + public Date convert(Calendar obj) throws ConversionException { + return obj.getTime(); + } + } + + /** * An object that converts a <code>Calendar</code> to a <code>Long</code>. */ public static class CalendarToLong extends AbstractConverter<Calendar, Long> { @@ -162,6 +180,40 @@ public class DateTimeConverters implemen } /** + * An object that converts a <code>Calendar</code> to a <code>Timestamp</code>. + */ + public static class CalendarToTimestamp extends AbstractConverter<Calendar, Timestamp> { + public CalendarToTimestamp() { + super(Calendar.class, Timestamp.class); + } + + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return Util.instanceOf(sourceClass, this.getSourceClass()) && Timestamp.class.equals(targetClass); + } + + public Timestamp convert(Calendar obj) throws ConversionException { + return new Timestamp(obj.getTimeInMillis()); + } + } + + + /** + * An object that converts a <code>Date</code> to a <code>Calendar</code>. + */ + public static class DateToCalendar extends GenericLocalizedConverter<Date, Calendar> { + public DateToCalendar() { + super(Date.class, Calendar.class); + } + + public Calendar convert(Date obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException { + Calendar cal = Calendar.getInstance(timeZone, locale); + cal.setTime(obj); + return cal; + } + } + + /** * An object that converts a <code>java.util.Date</code> to a * <code>java.sql.Date</code>. */ @@ -178,8 +230,33 @@ public class DateTimeConverters implemen /** * Returns <code>obj</code> converted to a <code>java.sql.Date</code>. */ + @SuppressWarnings("deprecation") public java.sql.Date convert(Date obj) throws ConversionException { - return new java.sql.Date(obj.getTime()); + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(obj.getTime()); + return new java.sql.Date(cal.get(Calendar.YEAR) - 1900, cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)); + } + } + + /** + * An object that converts a <code>java.util.Date</code> to a + * <code>java.sql.Time</code>. + */ + public static class DateToSqlTime extends AbstractConverter<java.util.Date, java.sql.Time> { + public DateToSqlTime() { + super(Date.class, java.sql.Time.class); + } + + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return sourceClass == this.getSourceClass() && targetClass == this.getTargetClass(); + } + + @SuppressWarnings("deprecation") + public java.sql.Time convert(Date obj) throws ConversionException { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(obj.getTime()); + return new java.sql.Time(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND)); } } @@ -192,6 +269,11 @@ public class DateTimeConverters implemen super(Date.class, String.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return Date.class.equals(sourceClass) && String.class.equals(targetClass); + } + /** * Converts <code>obj</code> to a <code>String</code> formatted as * {@link DateTimeConverters#CALENDAR_FORMAT}. The returned string is @@ -253,6 +335,20 @@ public class DateTimeConverters implemen } } + public static abstract class GenericLocalizedConverter<S, T> extends AbstractLocalizedConverter<S, T> { + protected GenericLocalizedConverter(Class<S> sourceClass, Class<T> targetClass) { + super(sourceClass, targetClass); + } + + public T convert(S obj) throws ConversionException { + return convert(obj, Locale.getDefault(), TimeZone.getDefault(), null); + } + + public T convert(S obj, Locale locale, TimeZone timeZone) throws ConversionException { + return convert(obj, locale, timeZone, null); + } + } + /** * An object that converts a <code>Long</code> to a * <code>Calendar</code>. @@ -293,6 +389,11 @@ public class DateTimeConverters implemen super(Long.class, Date.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return Long.class.equals(sourceClass) && Date.class.equals(targetClass); + } + /** * Returns <code>obj</code> converted to a <code>java.util.Date</code>. */ @@ -310,6 +411,11 @@ public class DateTimeConverters implemen super(Long.class, java.sql.Date.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return Long.class.equals(sourceClass) && java.sql.Date.class.equals(targetClass); + } + /** * Returns <code>obj</code> converted to a <code>java.sql.Date</code>. */ @@ -327,6 +433,11 @@ public class DateTimeConverters implemen super(Long.class, java.sql.Time.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return Long.class.equals(sourceClass) && java.sql.Time.class.equals(targetClass); + } + /** * Returns <code>obj</code> converted to a <code>java.sql.Time</code>. */ @@ -344,6 +455,11 @@ public class DateTimeConverters implemen super(Long.class, java.sql.Timestamp.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return Long.class.equals(sourceClass) && java.sql.Timestamp.class.equals(targetClass); + } + /** * Returns <code>obj</code> converted to a <code>java.sql.Timestamp</code>. */ @@ -383,6 +499,11 @@ public class DateTimeConverters implemen super(java.sql.Date.class, String.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return java.sql.Date.class.equals(sourceClass) && String.class.equals(targetClass); + } + public String convert(java.sql.Date obj) throws ConversionException { return obj.toString(); } @@ -430,6 +551,11 @@ public class DateTimeConverters implemen super(java.sql.Time.class, String.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return java.sql.Time.class.equals(sourceClass) && String.class.equals(targetClass); + } + public String convert(java.sql.Time obj) throws ConversionException { return obj.toString(); } @@ -499,6 +625,11 @@ public class DateTimeConverters implemen super(String.class, Date.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return String.class.equals(sourceClass) && Date.class.equals(targetClass); + } + /** * Converts <code>obj</code> to a <code>java.util.Date</code>. * The string must be formatted as @@ -537,6 +668,11 @@ public class DateTimeConverters implemen super(String.class, java.sql.Date.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return String.class.equals(sourceClass) && java.sql.Date.class.equals(targetClass); + } + public java.sql.Date convert(String obj) throws ConversionException { return java.sql.Date.valueOf(obj); } @@ -566,6 +702,11 @@ public class DateTimeConverters implemen super(String.class, java.sql.Time.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return String.class.equals(sourceClass) && java.sql.Time.class.equals(targetClass); + } + public java.sql.Time convert(String obj) throws ConversionException { return java.sql.Time.valueOf(obj); } @@ -595,6 +736,11 @@ public class DateTimeConverters implemen super(String.class, java.sql.Timestamp.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return String.class.equals(sourceClass) && java.sql.Timestamp.class.equals(targetClass); + } + public Timestamp convert(String obj) throws ConversionException { return java.sql.Timestamp.valueOf(obj); } @@ -649,6 +795,44 @@ public class DateTimeConverters implemen /** * An object that converts a <code>java.sql.Timestamp</code> to a + * <code>java.sql.Date</code>. + */ + public static class TimestampToSqlDate extends AbstractConverter<java.sql.Timestamp, java.sql.Date> { + public TimestampToSqlDate() { + super(java.sql.Timestamp.class, java.sql.Date.class); + } + + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return java.sql.Timestamp.class.equals(sourceClass) && java.sql.Date.class.equals(targetClass); + } + + public java.sql.Date convert(java.sql.Timestamp obj) throws ConversionException { + return new java.sql.Date(obj.getTime()); + } + } + + /** + * An object that converts a <code>java.sql.Timestamp</code> to a + * <code>java.sql.Time</code>. + */ + public static class TimestampToSqlTime extends AbstractConverter<java.sql.Timestamp, java.sql.Time> { + public TimestampToSqlTime() { + super(java.sql.Timestamp.class, java.sql.Time.class); + } + + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return java.sql.Timestamp.class.equals(sourceClass) && java.sql.Time.class.equals(targetClass); + } + + public java.sql.Time convert(java.sql.Timestamp obj) throws ConversionException { + return new java.sql.Time(obj.getTime()); + } + } + + /** + * An object that converts a <code>java.sql.Timestamp</code> to a * <code>String</code>. */ public static class TimestampToString extends AbstractLocalizedConverter<java.sql.Timestamp, String> { @@ -656,6 +840,11 @@ public class DateTimeConverters implemen super(java.sql.Timestamp.class, String.class); } + @Override + public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) { + return java.sql.Timestamp.class.equals(sourceClass) && String.class.equals(targetClass); + } + public String convert(java.sql.Timestamp obj) throws ConversionException { return obj.toString(); } Modified: commons/sandbox/convert/trunk/src/test/java/org/apache/commons/convert/TestDateTimeConverters.java URL: http://svn.apache.org/viewvc/commons/sandbox/convert/trunk/src/test/java/org/apache/commons/convert/TestDateTimeConverters.java?rev=1513175&r1=1513174&r2=1513175&view=diff ============================================================================== --- commons/sandbox/convert/trunk/src/test/java/org/apache/commons/convert/TestDateTimeConverters.java (original) +++ commons/sandbox/convert/trunk/src/test/java/org/apache/commons/convert/TestDateTimeConverters.java Mon Aug 12 15:52:06 2013 @@ -20,7 +20,6 @@ package org.apache.commons.convert; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.Calendar; import java.util.Collection; import java.util.List; import java.util.Locale; @@ -79,23 +78,28 @@ public class TestDateTimeConverters exte public void testDateTimeConverters() throws Exception { ConverterLoader loader = new DateTimeConverters(); loader.loadConverters(); - long currentTime = System.currentTimeMillis(); + // Java date-related classes default to Jan 1, 1970 00:00:00 in some methods, + // so we use it here for simplicity. + java.util.Date utilDate = new java.util.Date(70, 0, 1, 0, 0, 0); + long currentTime = utilDate.getTime(); java.util.Calendar cal = java.util.Calendar.getInstance(); cal.setTimeInMillis(currentTime); - java.util.Date utilDate = new java.util.Date(currentTime); - java.sql.Date sqlDate = new java.sql.Date(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)); - java.sql.Time sqlTime = new java.sql.Time(cal.get(Calendar.HOUR), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND)); + java.sql.Date sqlDate = new java.sql.Date(70, 0, 1); + java.sql.Time sqlTime = new java.sql.Time(0, 0, 0); java.sql.Timestamp timestamp = new java.sql.Timestamp(currentTime); // Source class = java.util.Calendar DateFormat df = new SimpleDateFormat(DateTimeConverters.CALENDAR_FORMAT); df.setCalendar(cal); + assertConversion("CalendarToDate", new DateTimeConverters.CalendarToDate(), cal, utilDate); assertConversion("CalendarToLong", new DateTimeConverters.CalendarToLong(), cal, currentTime); assertConversion("CalendarToString", new DateTimeConverters.CalendarToString(), cal, df.format(cal.getTime())); + assertConversion("CalendarToTimestamp", new DateTimeConverters.CalendarToTimestamp(), cal, timestamp); assertToCollection("CalendarToCollection", cal); // Source class = java.util.Date + assertConversion("DateToCalendar", new DateTimeConverters.DateToCalendar(), utilDate, cal); assertConversion("DateToLong", new DateTimeConverters.GenericDateToLong<java.util.Date>(java.util.Date.class), utilDate, currentTime); - assertConversion("DateToSqlDate", new DateTimeConverters.DateToSqlDate(), new java.util.Date(sqlDate.getTime()), sqlDate); - assertConversion("DateToSqlDate", new DateTimeConverters.DateToSqlDate(), new java.sql.Timestamp(sqlDate.getTime()), sqlDate); + assertConversion("DateToSqlDate", new DateTimeConverters.DateToSqlDate(), utilDate, sqlDate); + assertConversion("DateToSqlTime", new DateTimeConverters.DateToSqlTime(), utilDate, sqlTime); assertConversion("DateToString", new DateTimeConverters.DateToString(), utilDate, df.format(cal.getTime())); assertConversion("DateToTimestamp", new DateTimeConverters.DateToTimestamp(), utilDate, timestamp); assertConversion("DateToTimestamp", new DateTimeConverters.DateToTimestamp(), timestamp, timestamp, false); @@ -112,6 +116,8 @@ public class TestDateTimeConverters exte assertConversion("SqlTimeToString", new DateTimeConverters.SqlTimeToString(), sqlTime, sqlTime.toString()); // Source class = java.sql.Timestamp assertConversion("TimestampToLong", new DateTimeConverters.GenericDateToLong<java.sql.Timestamp>(java.sql.Timestamp.class), timestamp, currentTime); + assertConversion("TimestampToSqlDate", new DateTimeConverters.TimestampToSqlDate(), new java.sql.Timestamp(sqlDate.getTime()), sqlDate); + assertConversion("TimestampToSqlTime", new DateTimeConverters.TimestampToSqlTime(), new java.sql.Timestamp(sqlDate.getTime()), sqlTime); assertConversion("TimestampToString", new DateTimeConverters.TimestampToString(), timestamp, timestamp.toString()); assertToCollection("TimestampToCollection", timestamp); // TimeZone tests